Logging.swift 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Logging API open source project
  4. //
  5. // Copyright (c) 2018-2019 Apple Inc. and the Swift Logging API project authors
  6. // Licensed under Apache License v2.0
  7. //
  8. // See LICENSE.txt for license information
  9. // See CONTRIBUTORS.txt for the list of Swift Logging API project authors
  10. //
  11. // SPDX-License-Identifier: Apache-2.0
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
  15. import Darwin
  16. #elseif os(Windows)
  17. import CRT
  18. #else
  19. import Glibc
  20. #endif
  21. /// A `Logger` is the central type in `SwiftLog`. Its central function is to emit log messages using one of the methods
  22. /// corresponding to a log level.
  23. ///
  24. /// `Logger`s are value types with respect to the `logLevel` and the `metadata` (as well as the immutable `label`
  25. /// and the selected `LogHandler`). Therefore, `Logger`s are suitable to be passed around between libraries if you want
  26. /// to preserve metadata across libraries.
  27. ///
  28. /// The most basic usage of a `Logger` is
  29. ///
  30. /// logger.info("Hello World!")
  31. ///
  32. public struct Logger {
  33. @usableFromInline
  34. var handler: LogHandler
  35. /// An identifier of the creator of this `Logger`.
  36. public let label: String
  37. internal init(label: String, _ handler: LogHandler) {
  38. self.label = label
  39. self.handler = handler
  40. }
  41. }
  42. extension Logger {
  43. /// Log a message passing the log level as a parameter.
  44. ///
  45. /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged,
  46. /// otherwise nothing will happen.
  47. ///
  48. /// - parameters:
  49. /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`.
  50. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  51. /// - metadata: One-off metadata to attach to this log message.
  52. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  53. /// file that is emitting the log message, which usually is the module.
  54. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  55. /// defaults to `#file`).
  56. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  57. /// it defaults to `#function`).
  58. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  59. /// defaults to `#line`).
  60. @inlinable
  61. public func log(level: Logger.Level,
  62. _ message: @autoclosure () -> Logger.Message,
  63. metadata: @autoclosure () -> Logger.Metadata? = nil,
  64. source: @autoclosure () -> String? = nil,
  65. file: String = #file, function: String = #function, line: UInt = #line) {
  66. if self.logLevel <= level {
  67. self.handler.log(level: level,
  68. message: message(),
  69. metadata: metadata(),
  70. source: source() ?? Logger.currentModule(filePath: (file)),
  71. file: file, function: function, line: line)
  72. }
  73. }
  74. /// Add, change, or remove a logging metadata item.
  75. ///
  76. /// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the
  77. /// very `Logger` it was changed on.
  78. @inlinable
  79. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  80. get {
  81. return self.handler[metadataKey: metadataKey]
  82. }
  83. set {
  84. self.handler[metadataKey: metadataKey] = newValue
  85. }
  86. }
  87. /// Get or set the log level configured for this `Logger`.
  88. ///
  89. /// - note: `Logger`s treat `logLevel` as a value. This means that a change in `logLevel` will only affect this
  90. /// very `Logger`. It is acceptable for logging backends to have some form of global log level override
  91. /// that affects multiple or even all loggers. This means a change in `logLevel` to one `Logger` might in
  92. /// certain cases have no effect.
  93. @inlinable
  94. public var logLevel: Logger.Level {
  95. get {
  96. return self.handler.logLevel
  97. }
  98. set {
  99. self.handler.logLevel = newValue
  100. }
  101. }
  102. }
  103. extension Logger {
  104. /// Log a message passing with the `Logger.Level.trace` log level.
  105. ///
  106. /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  107. /// otherwise nothing will happen.
  108. ///
  109. /// - parameters:
  110. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  111. /// - metadata: One-off metadata to attach to this log message
  112. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  113. /// file that is emitting the log message, which usually is the module.
  114. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  115. /// defaults to `#file`).
  116. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  117. /// it defaults to `#function`).
  118. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  119. /// defaults to `#line`).
  120. @inlinable
  121. public func trace(_ message: @autoclosure () -> Logger.Message,
  122. metadata: @autoclosure () -> Logger.Metadata? = nil,
  123. source: @autoclosure () -> String? = nil,
  124. file: String = #file, function: String = #function, line: UInt = #line) {
  125. self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  126. }
  127. /// Log a message passing with the `Logger.Level.debug` log level.
  128. ///
  129. /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  130. /// otherwise nothing will happen.
  131. ///
  132. /// - parameters:
  133. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  134. /// - metadata: One-off metadata to attach to this log message.
  135. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  136. /// file that is emitting the log message, which usually is the module.
  137. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  138. /// defaults to `#file`).
  139. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  140. /// it defaults to `#function`).
  141. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  142. /// defaults to `#line`).
  143. @inlinable
  144. public func debug(_ message: @autoclosure () -> Logger.Message,
  145. metadata: @autoclosure () -> Logger.Metadata? = nil,
  146. source: @autoclosure () -> String? = nil,
  147. file: String = #file, function: String = #function, line: UInt = #line) {
  148. self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  149. }
  150. /// Log a message passing with the `Logger.Level.info` log level.
  151. ///
  152. /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  153. /// otherwise nothing will happen.
  154. ///
  155. /// - parameters:
  156. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  157. /// - metadata: One-off metadata to attach to this log message.
  158. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  159. /// file that is emitting the log message, which usually is the module.
  160. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  161. /// defaults to `#file`).
  162. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  163. /// it defaults to `#function`).
  164. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  165. /// defaults to `#line`).
  166. @inlinable
  167. public func info(_ message: @autoclosure () -> Logger.Message,
  168. metadata: @autoclosure () -> Logger.Metadata? = nil,
  169. source: @autoclosure () -> String? = nil,
  170. file: String = #file, function: String = #function, line: UInt = #line) {
  171. self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  172. }
  173. /// Log a message passing with the `Logger.Level.notice` log level.
  174. ///
  175. /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  176. /// otherwise nothing will happen.
  177. ///
  178. /// - parameters:
  179. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  180. /// - metadata: One-off metadata to attach to this log message.
  181. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  182. /// file that is emitting the log message, which usually is the module.
  183. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  184. /// defaults to `#file`).
  185. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  186. /// it defaults to `#function`).
  187. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  188. /// defaults to `#line`).
  189. @inlinable
  190. public func notice(_ message: @autoclosure () -> Logger.Message,
  191. metadata: @autoclosure () -> Logger.Metadata? = nil,
  192. source: @autoclosure () -> String? = nil,
  193. file: String = #file, function: String = #function, line: UInt = #line) {
  194. self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  195. }
  196. /// Log a message passing with the `Logger.Level.warning` log level.
  197. ///
  198. /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  199. /// otherwise nothing will happen.
  200. ///
  201. /// - parameters:
  202. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  203. /// - metadata: One-off metadata to attach to this log message.
  204. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  205. /// file that is emitting the log message, which usually is the module.
  206. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  207. /// defaults to `#file`).
  208. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  209. /// it defaults to `#function`).
  210. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  211. /// defaults to `#line`).
  212. @inlinable
  213. public func warning(_ message: @autoclosure () -> Logger.Message,
  214. metadata: @autoclosure () -> Logger.Metadata? = nil,
  215. source: @autoclosure () -> String? = nil,
  216. file: String = #file, function: String = #function, line: UInt = #line) {
  217. self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  218. }
  219. /// Log a message passing with the `Logger.Level.error` log level.
  220. ///
  221. /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  222. /// otherwise nothing will happen.
  223. ///
  224. /// - parameters:
  225. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  226. /// - metadata: One-off metadata to attach to this log message.
  227. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  228. /// file that is emitting the log message, which usually is the module.
  229. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  230. /// defaults to `#file`).
  231. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  232. /// it defaults to `#function`).
  233. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  234. /// defaults to `#line`).
  235. @inlinable
  236. public func error(_ message: @autoclosure () -> Logger.Message,
  237. metadata: @autoclosure () -> Logger.Metadata? = nil,
  238. source: @autoclosure () -> String? = nil,
  239. file: String = #file, function: String = #function, line: UInt = #line) {
  240. self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  241. }
  242. /// Log a message passing with the `Logger.Level.critical` log level.
  243. ///
  244. /// `.critical` messages will always be logged.
  245. ///
  246. /// - parameters:
  247. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  248. /// - metadata: One-off metadata to attach to this log message.
  249. /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the
  250. /// file that is emitting the log message, which usually is the module.
  251. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  252. /// defaults to `#file`).
  253. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  254. /// it defaults to `#function`).
  255. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  256. /// defaults to `#line`).
  257. @inlinable
  258. public func critical(_ message: @autoclosure () -> Logger.Message,
  259. metadata: @autoclosure () -> Logger.Metadata? = nil,
  260. source: @autoclosure () -> String? = nil,
  261. file: String = #file, function: String = #function, line: UInt = #line) {
  262. self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  263. }
  264. }
  265. /// The `LoggingSystem` is a global facility where the default logging backend implementation (`LogHandler`) can be
  266. /// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend
  267. /// implementation.
  268. public enum LoggingSystem {
  269. fileprivate static let lock = ReadWriteLock()
  270. fileprivate static var factory: (String) -> LogHandler = StreamLogHandler.standardOutput
  271. fileprivate static var initialized = false
  272. /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend
  273. /// implementation. `bootstrap` can be called at maximum once in any given program, calling it more than once will
  274. /// lead to undefined behavior, most likely a crash.
  275. ///
  276. /// - parameters:
  277. /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`.
  278. public static func bootstrap(_ factory: @escaping (String) -> LogHandler) {
  279. self.lock.withWriterLock {
  280. precondition(!self.initialized, "logging system can only be initialized once per process.")
  281. self.factory = factory
  282. self.initialized = true
  283. }
  284. }
  285. // for our testing we want to allow multiple bootstraping
  286. internal static func bootstrapInternal(_ factory: @escaping (String) -> LogHandler) {
  287. self.lock.withWriterLock {
  288. self.factory = factory
  289. }
  290. }
  291. }
  292. extension Logger {
  293. /// `Metadata` is a typealias for `[String: Logger.MetadataValue]` the type of the metadata storage.
  294. public typealias Metadata = [String: MetadataValue]
  295. /// A logging metadata value. `Logger.MetadataValue` is string, array, and dictionary literal convertible.
  296. ///
  297. /// `MetadataValue` provides convenient conformances to `ExpressibleByStringInterpolation`,
  298. /// `ExpressibleByStringLiteral`, `ExpressibleByArrayLiteral`, and `ExpressibleByDictionaryLiteral` which means
  299. /// that when constructing `MetadataValue`s you should default to using Swift's usual literals.
  300. ///
  301. /// Examples:
  302. /// - prefer `logger.info("user logged in", metadata: ["user-id": "\(user.id)"])` over
  303. /// `..., metadata: ["user-id": .string(user.id.description)])`
  304. /// - prefer `logger.info("user selected colors", metadata: ["colors": ["\(user.topColor)", "\(user.secondColor)"]])`
  305. /// over `..., metadata: ["colors": .array([.string("\(user.topColor)"), .string("\(user.secondColor)")])`
  306. /// - prefer `logger.info("nested info", metadata: ["nested": ["fave-numbers": ["\(1)", "\(2)", "\(3)"], "foo": "bar"]])`
  307. /// over `..., metadata: ["nested": .dictionary(["fave-numbers": ...])])`
  308. public enum MetadataValue {
  309. /// A metadata value which is a `String`.
  310. ///
  311. /// Because `MetadataValue` implements `ExpressibleByStringInterpolation`, and `ExpressibleByStringLiteral`,
  312. /// you don't need to type `.string(someType.description)` you can use the string interpolation `"\(someType)"`.
  313. case string(String)
  314. /// A metadata value which is some `CustomStringConvertible`.
  315. case stringConvertible(CustomStringConvertible)
  316. /// A metadata value which is a dictionary from `String` to `Logger.MetadataValue`.
  317. ///
  318. /// Because `MetadataValue` implements `ExpressibleByDictionaryLiteral`, you don't need to type
  319. /// `.dictionary(["foo": .string("bar \(buz)")])`, you can just use the more natural `["foo": "bar \(buz)"]`.
  320. case dictionary(Metadata)
  321. /// A metadata value which is an array of `Logger.MetadataValue`s.
  322. ///
  323. /// Because `MetadataValue` implements `ExpressibleByArrayLiteral`, you don't need to type
  324. /// `.array([.string("foo"), .string("bar \(buz)")])`, you can just use the more natural `["foo", "bar \(buz)"]`.
  325. case array([Metadata.Value])
  326. }
  327. /// The log level.
  328. ///
  329. /// Log levels are ordered by their severity, with `.trace` being the least severe and
  330. /// `.critical` being the most severe.
  331. public enum Level: String, Codable, CaseIterable {
  332. /// Appropriate for messages that contain information normally of use only when
  333. /// tracing the execution of a program.
  334. case trace
  335. /// Appropriate for messages that contain information normally of use only when
  336. /// debugging a program.
  337. case debug
  338. /// Appropriate for informational messages.
  339. case info
  340. /// Appropriate for conditions that are not error conditions, but that may require
  341. /// special handling.
  342. case notice
  343. /// Appropriate for messages that are not error conditions, but more severe than
  344. /// `.notice`.
  345. case warning
  346. /// Appropriate for error conditions.
  347. case error
  348. /// Appropriate for critical error conditions that usually require immediate
  349. /// attention.
  350. ///
  351. /// When a `critical` message is logged, the logging backend (`LogHandler`) is free to perform
  352. /// more heavy-weight operations to capture system state (such as capturing stack traces) to facilitate
  353. /// debugging.
  354. case critical
  355. }
  356. /// Construct a `Logger` given a `label` identifying the creator of the `Logger`.
  357. ///
  358. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  359. /// a datatype.
  360. ///
  361. /// - parameters:
  362. /// - label: An identifier for the creator of a `Logger`.
  363. public init(label: String) {
  364. self = LoggingSystem.lock.withReaderLock { Logger(label: label, LoggingSystem.factory(label)) }
  365. }
  366. /// Construct a `Logger` given a `label` identifying the creator of the `Logger` or a non-standard `LogHandler`.
  367. ///
  368. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  369. /// a datatype.
  370. ///
  371. /// This initializer provides an escape hatch in case the global default logging backend implementation (set up
  372. /// using `LoggingSystem.bootstrap` is not appropriate for this particular logger.
  373. ///
  374. /// - parameters:
  375. /// - label: An identifier for the creator of a `Logger`.
  376. /// - factory: A closure creating non-standard `LogHandler`s.
  377. public init(label: String, factory: (String) -> LogHandler) {
  378. self = Logger(label: label, factory(label))
  379. }
  380. }
  381. extension Logger.Level {
  382. internal var naturalIntegralValue: Int {
  383. switch self {
  384. case .trace:
  385. return 0
  386. case .debug:
  387. return 1
  388. case .info:
  389. return 2
  390. case .notice:
  391. return 3
  392. case .warning:
  393. return 4
  394. case .error:
  395. return 5
  396. case .critical:
  397. return 6
  398. }
  399. }
  400. }
  401. extension Logger.Level: Comparable {
  402. public static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool {
  403. return lhs.naturalIntegralValue < rhs.naturalIntegralValue
  404. }
  405. }
  406. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  407. // https://bugs.swift.org/browse/SR-9687
  408. // Then we could write it as follows and it would work under Swift 5 and not only 4 as it does currently:
  409. // extension Logger.Metadata.Value: Equatable {
  410. extension Logger.MetadataValue: Equatable {
  411. public static func == (lhs: Logger.Metadata.Value, rhs: Logger.Metadata.Value) -> Bool {
  412. switch (lhs, rhs) {
  413. case (.string(let lhs), .string(let rhs)):
  414. return lhs == rhs
  415. case (.stringConvertible(let lhs), .stringConvertible(let rhs)):
  416. return lhs.description == rhs.description
  417. case (.array(let lhs), .array(let rhs)):
  418. return lhs == rhs
  419. case (.dictionary(let lhs), .dictionary(let rhs)):
  420. return lhs == rhs
  421. default:
  422. return false
  423. }
  424. }
  425. }
  426. extension Logger {
  427. /// `Logger.Message` represents a log message's text. It is usually created using string literals.
  428. ///
  429. /// Example creating a `Logger.Message`:
  430. ///
  431. /// let world: String = "world"
  432. /// let myLogMessage: Logger.Message = "Hello \(world)"
  433. ///
  434. /// Most commonly, `Logger.Message`s appear simply as the parameter to a logging method such as:
  435. ///
  436. /// logger.info("Hello \(world)")
  437. ///
  438. public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, ExpressibleByStringInterpolation {
  439. public typealias StringLiteralType = String
  440. private var value: String
  441. public init(stringLiteral value: String) {
  442. self.value = value
  443. }
  444. public var description: String {
  445. return self.value
  446. }
  447. }
  448. }
  449. /// A pseudo-`LogHandler` that can be used to send messages to multiple other `LogHandler`s.
  450. ///
  451. /// ### Effective Logger.Level
  452. ///
  453. /// When first initialized the multiplex log handlers' log level is automatically set to the minimum of all the
  454. /// passed in log handlers. This ensures that each of the handlers will be able to log at their appropriate level
  455. /// any log events they might be interested in.
  456. ///
  457. /// Example:
  458. /// If log handler `A` is logging at `.debug` level, and log handler `B` is logging at `.info` level, the constructed
  459. /// `MultiplexLogHandler([A, B])`'s effective log level will be set to `.debug`, meaning that debug messages will be
  460. /// handled by this handler, while only logged by the underlying `A` log handler (since `B`'s log level is `.info`
  461. /// and thus it would not actually log that log message).
  462. ///
  463. /// If the log level is _set_ on a `Logger` backed by an `MultiplexLogHandler` the log level will apply to *all*
  464. /// underlying log handlers, allowing a logger to still select at what level it wants to log regardless of if the underlying
  465. /// handler is a multiplex or a normal one. If for some reason one might want to not allow changing a log level of a specific
  466. /// handler passed into the multiplex log handler, this is possible by wrapping it in a handler which ignores any log level changes.
  467. ///
  468. /// ### Effective Logger.Metadata
  469. ///
  470. /// Since a `MultiplexLogHandler` is a combination of multiple log handlers, the handling of metadata can be non-obvious.
  471. /// For example, the underlying log handlers may have metadata of their own set before they are used to initialize the multiplex log handler.
  472. ///
  473. /// The multiplex log handler acts purely as proxy and does not make any changes to underlying handler metadata other than
  474. /// proxying writes that users made on a `Logger` instance backed by this handler.
  475. ///
  476. /// Setting metadata is always proxied through to _all_ underlying handlers, meaning that if a modification like
  477. /// `logger[metadataKey: "x"] = "y"` is made, all underlying log handlers that this multiplex handler was initiated with
  478. /// will observe this change.
  479. ///
  480. /// Reading metadata from the multiplex log handler MAY need to pick one of conflicting values if the underlying log handlers
  481. /// were already initiated with some metadata before passing them into the multiplex handler. The multiplex handler uses
  482. /// the order in which the handlers were passed in during its initialization as a priority indicator - the first handler's
  483. /// values are more important than the next handlers values, etc.
  484. ///
  485. /// Example:
  486. /// If the multiplex log handler was initiated with two handlers like this: `MultiplexLogHandler([handler1, handler2])`.
  487. /// The handlers each have some already set metadata: `handler1` has metadata values for keys `one` and `all`, and `handler2`
  488. /// has values for keys `two` and `all`.
  489. ///
  490. /// A query through the multiplex log handler the key `one` naturally returns `handler1`'s value, and a query for `two`
  491. /// naturally returns `handler2`'s value. Querying for the key `all` will return `handler1`'s value, as that handler was indicated
  492. /// "more important" than the second handler. The same rule applies when querying for the `metadata` property of the
  493. /// multiplex log handler - it constructs `Metadata` uniquing values.
  494. public struct MultiplexLogHandler: LogHandler {
  495. private var handlers: [LogHandler]
  496. private var effectiveLogLevel: Logger.Level
  497. /// Create a `MultiplexLogHandler`.
  498. ///
  499. /// - parameters:
  500. /// - handlers: An array of `LogHandler`s, each of which will receive the log messages sent to this `Logger`.
  501. /// The array must not be empty.
  502. public init(_ handlers: [LogHandler]) {
  503. assert(!handlers.isEmpty, "MultiplexLogHandler.handlers MUST NOT be empty")
  504. self.handlers = handlers
  505. self.effectiveLogLevel = handlers.map { $0.logLevel }.min() ?? .trace
  506. }
  507. public var logLevel: Logger.Level {
  508. get {
  509. return self.effectiveLogLevel
  510. }
  511. set {
  512. self.mutatingForEachHandler { $0.logLevel = newValue }
  513. self.effectiveLogLevel = newValue
  514. }
  515. }
  516. public func log(level: Logger.Level,
  517. message: Logger.Message,
  518. metadata: Logger.Metadata?,
  519. source: String,
  520. file: String,
  521. function: String,
  522. line: UInt) {
  523. for handler in self.handlers where handler.logLevel <= level {
  524. handler.log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line)
  525. }
  526. }
  527. public var metadata: Logger.Metadata {
  528. get {
  529. var effectiveMetadata: Logger.Metadata = [:]
  530. // as a rough estimate we assume that the underlying handlers have a similar metadata count,
  531. // and we use the first one's current count to estimate how big of a dictionary we need to allocate:
  532. effectiveMetadata.reserveCapacity(self.handlers.first!.metadata.count) // !-safe, we always have at least one handler
  533. return self.handlers.reduce(into: effectiveMetadata) { effectiveMetadata, handler in
  534. effectiveMetadata.merge(handler.metadata, uniquingKeysWith: { l, _ in l })
  535. }
  536. }
  537. set {
  538. self.mutatingForEachHandler { $0.metadata = newValue }
  539. }
  540. }
  541. public subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? {
  542. get {
  543. for handler in self.handlers {
  544. if let value = handler[metadataKey: metadataKey] {
  545. return value
  546. }
  547. }
  548. return nil
  549. }
  550. set {
  551. self.mutatingForEachHandler { $0[metadataKey: metadataKey] = newValue }
  552. }
  553. }
  554. private mutating func mutatingForEachHandler(_ mutator: (inout LogHandler) -> Void) {
  555. for index in self.handlers.indices {
  556. mutator(&self.handlers[index])
  557. }
  558. }
  559. }
  560. /// A wrapper to facilitate `print`-ing to stderr and stdio that
  561. /// ensures access to the underlying `FILE` is locked to prevent
  562. /// cross-thread interleaving of output.
  563. internal struct StdioOutputStream: TextOutputStream {
  564. internal let file: UnsafeMutablePointer<FILE>
  565. internal let flushMode: FlushMode
  566. internal func write(_ string: String) {
  567. string.withCString { ptr in
  568. #if os(Windows)
  569. _lock_file(self.file)
  570. #else
  571. flockfile(self.file)
  572. #endif
  573. defer {
  574. #if os(Windows)
  575. _unlock_file(self.file)
  576. #else
  577. funlockfile(self.file)
  578. #endif
  579. }
  580. _ = fputs(ptr, self.file)
  581. if case .always = self.flushMode {
  582. self.flush()
  583. }
  584. }
  585. }
  586. /// Flush the underlying stream.
  587. /// This has no effect when using the `.always` flush mode, which is the default
  588. internal func flush() {
  589. _ = fflush(self.file)
  590. }
  591. internal static let stderr = StdioOutputStream(file: systemStderr, flushMode: .always)
  592. internal static let stdout = StdioOutputStream(file: systemStdout, flushMode: .always)
  593. /// Defines the flushing strategy for the underlying stream.
  594. internal enum FlushMode {
  595. case undefined
  596. case always
  597. }
  598. }
  599. // Prevent name clashes
  600. #if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
  601. let systemStderr = Darwin.stderr
  602. let systemStdout = Darwin.stdout
  603. #elseif os(Windows)
  604. let systemStderr = CRT.stderr
  605. let systemStdout = CRT.stdout
  606. #else
  607. let systemStderr = Glibc.stderr!
  608. let systemStdout = Glibc.stdout!
  609. #endif
  610. /// `StreamLogHandler` is a simple implementation of `LogHandler` for directing
  611. /// `Logger` output to either `stderr` or `stdout` via the factory methods.
  612. public struct StreamLogHandler: LogHandler {
  613. /// Factory that makes a `StreamLogHandler` to directs its output to `stdout`
  614. public static func standardOutput(label: String) -> StreamLogHandler {
  615. return StreamLogHandler(label: label, stream: StdioOutputStream.stdout)
  616. }
  617. /// Factory that makes a `StreamLogHandler` to directs its output to `stderr`
  618. public static func standardError(label: String) -> StreamLogHandler {
  619. return StreamLogHandler(label: label, stream: StdioOutputStream.stderr)
  620. }
  621. private let stream: TextOutputStream
  622. private let label: String
  623. public var logLevel: Logger.Level = .info
  624. private var prettyMetadata: String?
  625. public var metadata = Logger.Metadata() {
  626. didSet {
  627. self.prettyMetadata = self.prettify(self.metadata)
  628. }
  629. }
  630. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  631. get {
  632. return self.metadata[metadataKey]
  633. }
  634. set {
  635. self.metadata[metadataKey] = newValue
  636. }
  637. }
  638. // internal for testing only
  639. internal init(label: String, stream: TextOutputStream) {
  640. self.label = label
  641. self.stream = stream
  642. }
  643. public func log(level: Logger.Level,
  644. message: Logger.Message,
  645. metadata: Logger.Metadata?,
  646. source: String,
  647. file: String,
  648. function: String,
  649. line: UInt) {
  650. let prettyMetadata = metadata?.isEmpty ?? true
  651. ? self.prettyMetadata
  652. : self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new }))
  653. var stream = self.stream
  654. stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") \(message)\n")
  655. }
  656. private func prettify(_ metadata: Logger.Metadata) -> String? {
  657. return !metadata.isEmpty
  658. ? metadata.lazy.sorted(by: { $0.key < $1.key }).map { "\($0)=\($1)" }.joined(separator: " ")
  659. : nil
  660. }
  661. private func timestamp() -> String {
  662. var buffer = [Int8](repeating: 0, count: 255)
  663. var timestamp = time(nil)
  664. let localTime = localtime(&timestamp)
  665. strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", localTime)
  666. return buffer.withUnsafeBufferPointer {
  667. $0.withMemoryRebound(to: CChar.self) {
  668. String(cString: $0.baseAddress!)
  669. }
  670. }
  671. }
  672. }
  673. /// No operation LogHandler, used when no logging is required
  674. public struct SwiftLogNoOpLogHandler: LogHandler {
  675. public init() {}
  676. @inlinable public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {}
  677. @inlinable public subscript(metadataKey _: String) -> Logger.Metadata.Value? {
  678. get {
  679. return nil
  680. }
  681. set {}
  682. }
  683. @inlinable public var metadata: Logger.Metadata {
  684. get {
  685. return [:]
  686. }
  687. set {}
  688. }
  689. @inlinable public var logLevel: Logger.Level {
  690. get {
  691. return .critical
  692. }
  693. set {}
  694. }
  695. }
  696. extension Logger {
  697. @inlinable
  698. internal static func currentModule(filePath: String = #file) -> String {
  699. let utf8All = filePath.utf8
  700. return filePath.utf8.lastIndex(of: UInt8(ascii: "/")).flatMap { lastSlash -> Substring? in
  701. utf8All[..<lastSlash].lastIndex(of: UInt8(ascii: "/")).map { secondLastSlash -> Substring in
  702. filePath[utf8All.index(after: secondLastSlash) ..< lastSlash]
  703. }
  704. }.map {
  705. String($0)
  706. } ?? "n/a"
  707. }
  708. }
  709. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  710. // https://bugs.swift.org/browse/SR-9686
  711. extension Logger.MetadataValue: ExpressibleByStringLiteral {
  712. public typealias StringLiteralType = String
  713. public init(stringLiteral value: String) {
  714. self = .string(value)
  715. }
  716. }
  717. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  718. // https://bugs.swift.org/browse/SR-9686
  719. extension Logger.MetadataValue: CustomStringConvertible {
  720. public var description: String {
  721. switch self {
  722. case .dictionary(let dict):
  723. return dict.mapValues { $0.description }.description
  724. case .array(let list):
  725. return list.map { $0.description }.description
  726. case .string(let str):
  727. return str
  728. case .stringConvertible(let repr):
  729. return repr.description
  730. }
  731. }
  732. }
  733. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  734. // https://bugs.swift.org/browse/SR-9687
  735. extension Logger.MetadataValue: ExpressibleByStringInterpolation {}
  736. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  737. // https://bugs.swift.org/browse/SR-9686
  738. extension Logger.MetadataValue: ExpressibleByDictionaryLiteral {
  739. public typealias Key = String
  740. public typealias Value = Logger.Metadata.Value
  741. public init(dictionaryLiteral elements: (String, Logger.Metadata.Value)...) {
  742. self = .dictionary(.init(uniqueKeysWithValues: elements))
  743. }
  744. }
  745. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  746. // https://bugs.swift.org/browse/SR-9686
  747. extension Logger.MetadataValue: ExpressibleByArrayLiteral {
  748. public typealias ArrayLiteralElement = Logger.Metadata.Value
  749. public init(arrayLiteral elements: Logger.Metadata.Value...) {
  750. self = .array(elements)
  751. }
  752. }