CommandLineToolkit

Форк
0
/
ContextualLogger.swift 
121 строка · 4.5 Кб
1
/*
2
 * Copyright (c) Avito Tech LLC
3
 */
4

5
import DateProvider
6
import CLTLoggingModels
7
import Foundation
8
import ProcessController
9

10
/// # Philosophy behind a contextual logging system
11
/// Each layer of a software might want to log different information on top of other information added by layers on top.
12
/// Example:
13
///
14
/// * `main` entrypoint might put some metadata to a logger: pid, process name
15
/// * `command` being executed might add a command name, its arguments, probably user input
16
/// * `process executor` which is being used by a `command` may add `subprocessId` and `subprocessName` to loggable messages.
17
///
18
/// In order to achieve this, `main` may create its instance of `ContextualLogger` and pass it down to the objects
19
/// `command` may get the instance from `main`, and obtain its own instance by adding metadata. `ContextualLogger` here works like a factory.
20
/// When executing subprocess, `command` will pass its `ContextualLogger` to `process executor`.
21
/// `process executor` will again append its own metadata and use new instance to log its stuff.
22
/// This way metadata can be derived between layers of software, extending it where needed, and still allowing layers to log data with its set of metadata without being affected by other layers.
23
public final class ContextualLogger {
24
    private let dateProvider: DateProvider
25
    private let loggerHandler: LoggerHandler
26
    private let metadata: [String: String?]
27
    
28
    public enum ContextKeys: String, CaseIterable {
29
        /// Id of a subprocess started by current process
30
        case subprocessId
31
        
32
        /// Name of a subprocess started by current process
33
        case subprocessName
34
        
35
        /// process id
36
        case processId
37
        
38
        /// process name
39
        case processName
40
        
41
        /// Hostname where process is being executed
42
        case hostname
43
        
44
        /// If subprocess is started via `xcrun`, this key contains a name of a launched tool, e.g. `simctl`
45
        case xcrunToolName
46
        
47
        public static func stringSetForAllRawValues() -> Set<String> {
48
            Set(allCases.map { $0.rawValue })
49
        }
50
    }
51
    
52
    public static let noOp: ContextualLogger = ContextualLogger(
53
        dateProvider: SystemDateProvider(),
54
        loggerHandler: AggregatedLoggerHandler(handlers: []),
55
        metadata: [:]
56
    )
57

58
    public init(
59
        dateProvider: DateProvider,
60
        loggerHandler: LoggerHandler,
61
        metadata: [String: String?]        
62
    ) {
63
        self.dateProvider = dateProvider
64
        self.loggerHandler = loggerHandler
65
        self.metadata = metadata
66
    }
67
    
68
    public func withMetadata(_ keyValues: [String: String?]) -> ContextualLogger {
69
        var metadata = self.metadata
70
        metadata.merge(keyValues) { _, new -> String? in new }
71
        return ContextualLogger(dateProvider: dateProvider, loggerHandler: loggerHandler, metadata: metadata)
72
    }
73
    
74
    public func withMetadata(key: String, value: String?) -> ContextualLogger {
75
        var metadata = self.metadata
76
        metadata[key] = value
77
        return ContextualLogger(dateProvider: dateProvider, loggerHandler: loggerHandler, metadata: metadata)
78
    }
79
    
80
    public func withMetadata(_ coordinate: LogEntryCoordinate) -> ContextualLogger {
81
        var metadata = self.metadata
82
        metadata[coordinate.name] = coordinate.value
83
        return ContextualLogger(dateProvider: dateProvider, loggerHandler: loggerHandler, metadata: metadata)
84
    }
85
    
86
    public func withMetadata(key: ContextKeys, value: String?) -> ContextualLogger {
87
        withMetadata(key: key.rawValue, value: value)
88
    }
89
    
90
    public func log(
91
        _ verbosity: Verbosity,
92
        _ message: String,
93
        subprocessPidInfo: PidInfo?,
94
        source: String?,
95
        file: String,
96
        function: String,
97
        line: UInt
98
    ) {
99
        var flattenedMetadata = self.metadata
100
        
101
        if let subprocessPidInfo = subprocessPidInfo {
102
            flattenedMetadata[ContextKeys.subprocessId.rawValue] = "\(subprocessPidInfo.pid)"
103
            flattenedMetadata[ContextKeys.subprocessName.rawValue] = "\(subprocessPidInfo.name)"
104
        }
105
        
106
        let coordinates = flattenedMetadata.map { (key: String, value: String?) in
107
            LogEntryCoordinate(name: key, value: value)
108
        }
109
        
110
        let logEntry = LogEntry(
111
            file: file,
112
            line: line,
113
            coordinates: coordinates,
114
            message: message,
115
            timestamp: dateProvider.currentDate(),
116
            verbosity: verbosity
117
        )
118
        
119
        loggerHandler.handle(logEntry: logEntry)
120
    }
121
}
122

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.