CommandLineToolkit
201 строка · 6.8 Кб
1/*
2* Copyright (c) Avito Tech LLC
3*/
4
5import DateProvider
6import Dispatch
7import FileSystem
8import CLTLoggingModels
9import Foundation
10import Kibana
11import KibanaModels
12import PathLib
13import Tmp
14
15public final class LoggingSetup {
16private let dateProvider: DateProvider
17private let fileSystem: FileSystem
18private let logDomainName: String
19private let logFileExtension = "log"
20private let logFilePrefix = "pid_"
21private let logFilesCleanUpRegularity: TimeInterval = 10800
22private let aggregatedLoggerHandler: AggregatedLoggerHandler
23private let pluggableLoggerHandlerForKibana = RedirectingLoggerHandler()
24public let rootLoggerHandler: LoggerHandler
25
26public init(
27dateProvider: DateProvider,
28fileSystem: FileSystem,
29logDomainName: String
30) {
31self.dateProvider = dateProvider
32self.fileSystem = fileSystem
33self.logDomainName = logDomainName
34self.aggregatedLoggerHandler = AggregatedLoggerHandler(handlers: [])
35self.rootLoggerHandler = self.aggregatedLoggerHandler
36}
37
38public func createLogger(
39stderrVerbosity: Verbosity,
40detailedLogVerbosity: Verbosity?,
41kibanaVerbosity: Verbosity
42) throws -> ContextualLogger {
43let logger = ContextualLogger(
44dateProvider: dateProvider,
45loggerHandler: rootLoggerHandler,
46metadata: [:]
47)
48add(
49loggerHandler: createStderrInfoLoggerHandler(
50verbosity: stderrVerbosity
51)
52)
53add(
54loggerHandler: LimitingLoggerHandler(
55maximumVerbosity: kibanaVerbosity,
56target: pluggableLoggerHandlerForKibana
57)
58)
59if let detailedLogVerbosity = detailedLogVerbosity {
60let filename = logFilePrefix + String(ProcessInfo.processInfo.processIdentifier)
61let detailedLogPath = try TemporaryFile(
62containerPath: try logsContainerFolder(),
63prefix: filename,
64suffix: "." + logFileExtension,
65deleteOnDealloc: false
66)
67add(
68loggerHandler: createDetailedLoggerHandler(
69fileHandle: detailedLogPath.fileHandleForWriting,
70verbosity: detailedLogVerbosity
71)
72)
73logger.info("Verbose logs available at: \(detailedLogPath.absolutePath)")
74}
75
76return logger
77}
78
79public func set(
80kibanaConfiguration: KibanaConfiguration
81) throws {
82let handler = KibanaLoggerHandler(
83kibanaClient: try HttpKibanaClient(
84dateProvider: dateProvider,
85endpoints: try kibanaConfiguration.endpoints.map { try KibanaHttpEndpoint.from(url: $0) },
86indexPattern: kibanaConfiguration.indexPattern,
87urlSession: .shared,
88authorization: kibanaConfiguration.authorization
89)
90)
91pluggableLoggerHandlerForKibana.setTarget(handler)
92}
93
94public func childProcessLogsContainerProvider() throws -> ChildProcessLogsContainerProvider {
95return ChildProcessLogsContainerProviderImpl(
96fileSystem: fileSystem,
97mainContainerPath: try logsContainerFolder()
98)
99}
100
101public func add(loggerHandler: LoggerHandler) {
102aggregatedLoggerHandler.append(handler: loggerHandler)
103}
104
105public func tearDown(timeout: TimeInterval) {
106aggregatedLoggerHandler.tearDownLogging(timeout: timeout)
107}
108
109public func cleanUpLogs(
110logger: ContextualLogger,
111logDomainName: String,
112olderThan date: Date,
113queue: OperationQueue,
114completion: @escaping (Error?) -> ()
115) throws {
116let logsCleanUpMarkerFileProperties = fileSystem.properties(
117path: try fileSystem.logsCleanUpMarkerFile(
118logDomainName: logDomainName
119)
120)
121guard dateProvider.currentDate().timeIntervalSince(
122try logsCleanUpMarkerFileProperties.modificationDate.get()
123) > logFilesCleanUpRegularity else {
124logger.trace("Skipping log clean up since last clean up happened recently")
125return
126}
127
128logger.trace("Cleaning up old log files")
129try logsCleanUpMarkerFileProperties.touch()
130
131let logsEnumerator = fileSystem.contentEnumerator(
132forPath: try fileSystem.logsFolder(
133logDomainName: logDomainName
134),
135style: .deep
136)
137
138queue.addOperation {
139do {
140try logsEnumerator.each { (path: AbsolutePath) in
141guard path.extension == self.logFileExtension else { return }
142let modificationDate = try self.fileSystem.properties(path: path).modificationDate.get()
143if modificationDate < date {
144do {
145try self.fileSystem.delete(path: path)
146} catch {
147logger.error("Failed to remove old log file at \(path): \(error)")
148}
149}
150}
151completion(nil)
152} catch {
153completion(error)
154}
155}
156}
157
158private func createStderrInfoLoggerHandler(
159verbosity: Verbosity
160) -> LoggerHandler {
161return LimitingLoggerHandler(
162maximumVerbosity: verbosity,
163target: FileHandleLoggerHandler(
164dateProvider: dateProvider,
165fileHandle: FileHandle.standardError,
166logEntryTextFormatter: NSLogLikeLogEntryTextFormatter(
167logLocation: false,
168logCoordinates: false
169),
170fileHandleShouldBeClosed: false,
171skipMetadataFlag: .skipStdOutput
172)
173)
174}
175
176private func createDetailedLoggerHandler(
177fileHandle: FileHandle,
178verbosity: Verbosity
179) -> LoggerHandler {
180return LimitingLoggerHandler(
181maximumVerbosity: verbosity,
182target: FileHandleLoggerHandler(
183dateProvider: dateProvider,
184fileHandle: fileHandle,
185logEntryTextFormatter: NSLogLikeLogEntryTextFormatter(
186logLocation: true,
187logCoordinates: true
188),
189fileHandleShouldBeClosed: true,
190skipMetadataFlag: .skipFileOutput
191)
192)
193}
194
195private func logsContainerFolder() throws -> AbsolutePath {
196try fileSystem.folderForStoringLogs(
197logDomainName: logDomainName,
198processName: ProcessInfo.processInfo.processName
199)
200}
201}
202