Logging.swift 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  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 MSVCRT
  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. /// The most basic usage of a `Logger` is
  25. ///
  26. /// logger.info("Hello World!")
  27. ///
  28. public struct Logger {
  29. @usableFromInline
  30. var handler: LogHandler
  31. /// An identifier of the creator of this `Logger`.
  32. public let label: String
  33. internal init(label: String, _ handler: LogHandler) {
  34. self.label = label
  35. self.handler = handler
  36. }
  37. }
  38. extension Logger {
  39. /// Log a message passing the log level as a parameter.
  40. ///
  41. /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged,
  42. /// otherwise nothing will happen.
  43. ///
  44. /// - parameters:
  45. /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`.
  46. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  47. /// - metadata: One-off metadata to attach to this log message
  48. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  49. /// defaults to `#file`).
  50. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  51. /// it defaults to `#function`).
  52. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  53. /// defaults to `#line`).
  54. @inlinable
  55. public func log(level: Logger.Level,
  56. _ message: @autoclosure () -> Logger.Message,
  57. metadata: @autoclosure () -> Logger.Metadata? = nil,
  58. file: String = #file, function: String = #function, line: UInt = #line) {
  59. if self.logLevel <= level {
  60. self.handler.log(level: level,
  61. message: message(),
  62. metadata: metadata(),
  63. file: file, function: function, line: line)
  64. }
  65. }
  66. /// Add, change, or remove a logging metadata item.
  67. ///
  68. /// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the
  69. /// very `Logger` it was changed on.
  70. @inlinable
  71. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  72. get {
  73. return self.handler[metadataKey: metadataKey]
  74. }
  75. set {
  76. self.handler[metadataKey: metadataKey] = newValue
  77. }
  78. }
  79. /// Get or set the log level configured for this `Logger`.
  80. ///
  81. /// - note: `Logger`s treat `logLevel` as a value. This means that a change in `logLevel` will only affect this
  82. /// very `Logger`. It it acceptable for logging backends to have some form of global log level override
  83. /// that affects multiple or even all loggers. This means a change in `logLevel` to one `Logger` might in
  84. /// certain cases have no effect.
  85. @inlinable
  86. public var logLevel: Logger.Level {
  87. get {
  88. return self.handler.logLevel
  89. }
  90. set {
  91. self.handler.logLevel = newValue
  92. }
  93. }
  94. }
  95. extension Logger {
  96. /// Log a message passing with the `Logger.Level.trace` log level.
  97. ///
  98. /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  99. /// otherwise nothing will happen.
  100. ///
  101. /// - parameters:
  102. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  103. /// - metadata: One-off metadata to attach to this log message
  104. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  105. /// defaults to `#file`).
  106. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  107. /// it defaults to `#function`).
  108. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  109. /// defaults to `#line`).
  110. @inlinable
  111. public func trace(_ message: @autoclosure () -> Logger.Message,
  112. metadata: @autoclosure () -> Logger.Metadata? = nil,
  113. file: String = #file, function: String = #function, line: UInt = #line) {
  114. self.log(level: .trace, message(), metadata: metadata(), file: file, function: function, line: line)
  115. }
  116. /// Log a message passing with the `Logger.Level.debug` log level.
  117. ///
  118. /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  119. /// otherwise nothing will happen.
  120. ///
  121. /// - parameters:
  122. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  123. /// - metadata: One-off metadata to attach to this log message
  124. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  125. /// defaults to `#file`).
  126. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  127. /// it defaults to `#function`).
  128. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  129. /// defaults to `#line`).
  130. @inlinable
  131. public func debug(_ message: @autoclosure () -> Logger.Message,
  132. metadata: @autoclosure () -> Logger.Metadata? = nil,
  133. file: String = #file, function: String = #function, line: UInt = #line) {
  134. self.log(level: .debug, message(), metadata: metadata(), file: file, function: function, line: line)
  135. }
  136. /// Log a message passing with the `Logger.Level.info` log level.
  137. ///
  138. /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  139. /// otherwise nothing will happen.
  140. ///
  141. /// - parameters:
  142. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  143. /// - metadata: One-off metadata to attach to this log message
  144. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  145. /// defaults to `#file`).
  146. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  147. /// it defaults to `#function`).
  148. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  149. /// defaults to `#line`).
  150. @inlinable
  151. public func info(_ message: @autoclosure () -> Logger.Message,
  152. metadata: @autoclosure () -> Logger.Metadata? = nil,
  153. file: String = #file, function: String = #function, line: UInt = #line) {
  154. self.log(level: .info, message(), metadata: metadata(), file: file, function: function, line: line)
  155. }
  156. /// Log a message passing with the `Logger.Level.notice` log level.
  157. ///
  158. /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  159. /// otherwise nothing will happen.
  160. ///
  161. /// - parameters:
  162. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  163. /// - metadata: One-off metadata to attach to this log message
  164. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  165. /// defaults to `#file`).
  166. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  167. /// it defaults to `#function`).
  168. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  169. /// defaults to `#line`).
  170. @inlinable
  171. public func notice(_ message: @autoclosure () -> Logger.Message,
  172. metadata: @autoclosure () -> Logger.Metadata? = nil,
  173. file: String = #file, function: String = #function, line: UInt = #line) {
  174. self.log(level: .notice, message(), metadata: metadata(), file: file, function: function, line: line)
  175. }
  176. /// Log a message passing with the `Logger.Level.warning` log level.
  177. ///
  178. /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  179. /// otherwise nothing will happen.
  180. ///
  181. /// - parameters:
  182. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  183. /// - metadata: One-off metadata to attach to this log message
  184. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  185. /// defaults to `#file`).
  186. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  187. /// it defaults to `#function`).
  188. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  189. /// defaults to `#line`).
  190. @inlinable
  191. public func warning(_ message: @autoclosure () -> Logger.Message,
  192. metadata: @autoclosure () -> Logger.Metadata? = nil,
  193. file: String = #file, function: String = #function, line: UInt = #line) {
  194. self.log(level: .warning, message(), metadata: metadata(), file: file, function: function, line: line)
  195. }
  196. /// Log a message passing with the `Logger.Level.error` log level.
  197. ///
  198. /// If `.error` 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. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  205. /// defaults to `#file`).
  206. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  207. /// it defaults to `#function`).
  208. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  209. /// defaults to `#line`).
  210. @inlinable
  211. public func error(_ message: @autoclosure () -> Logger.Message,
  212. metadata: @autoclosure () -> Logger.Metadata? = nil,
  213. file: String = #file, function: String = #function, line: UInt = #line) {
  214. self.log(level: .error, message(), metadata: metadata(), file: file, function: function, line: line)
  215. }
  216. /// Log a message passing with the `Logger.Level.critical` log level.
  217. ///
  218. /// `.critical` messages will always be logged.
  219. ///
  220. /// - parameters:
  221. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  222. /// - metadata: One-off metadata to attach to this log message
  223. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  224. /// defaults to `#file`).
  225. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  226. /// it defaults to `#function`).
  227. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  228. /// defaults to `#line`).
  229. @inlinable
  230. public func critical(_ message: @autoclosure () -> Logger.Message,
  231. metadata: @autoclosure () -> Logger.Metadata? = nil,
  232. file: String = #file, function: String = #function, line: UInt = #line) {
  233. self.log(level: .critical, message(), metadata: metadata(), file: file, function: function, line: line)
  234. }
  235. }
  236. /// The `LoggingSystem` is a global facility where the default logging backend implementation (`LogHandler`) can be
  237. /// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend
  238. /// implementation.
  239. public enum LoggingSystem {
  240. fileprivate static let lock = ReadWriteLock()
  241. fileprivate static var factory: (String) -> LogHandler = StreamLogHandler.standardOutput
  242. fileprivate static var initialized = false
  243. /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend
  244. /// implementation. `bootstrap` can be called at maximum once in any given program, calling it more than once will
  245. /// lead to undefined behaviour, most likely a crash.
  246. ///
  247. /// - parameters:
  248. /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`.
  249. public static func bootstrap(_ factory: @escaping (String) -> LogHandler) {
  250. self.lock.withWriterLock {
  251. precondition(!self.initialized, "logging system can only be initialized once per process.")
  252. self.factory = factory
  253. self.initialized = true
  254. }
  255. }
  256. // for our testing we want to allow multiple bootstraping
  257. internal static func bootstrapInternal(_ factory: @escaping (String) -> LogHandler) {
  258. self.lock.withWriterLock {
  259. self.factory = factory
  260. }
  261. }
  262. }
  263. extension Logger {
  264. /// `Metadata` is a typealias for `[String: Logger.MetadataValue]` the type of the metadata storage.
  265. public typealias Metadata = [String: MetadataValue]
  266. /// A logging metadata value. `Logger.MetadataValue` is string, array, and dictionary literal convertible.
  267. ///
  268. /// `MetadataValue` provides convenient conformances to `ExpressibleByStringInterpolation`,
  269. /// `ExpressibleByStringLiteral`, `ExpressibleByArrayLiteral`, and `ExpressibleByDictionaryLiteral` which means
  270. /// that when constructing `MetadataValue`s you should default to using Swift's usual literals.
  271. ///
  272. /// Examples:
  273. /// - prefer `logger.info("user logged in", metadata: ["user-id": "\(user.id)"])` over
  274. /// `..., metadata: ["user-id": .string(user.id.description)])`
  275. /// - prefer `logger.info("user selected colours", metadata: ["colors": ["\(user.topColor)", "\(user.secondColor)"]])`
  276. /// over `..., metadata: ["colors": .array([.string("\(user.topColor)"), .string("\(user.secondColor)")])`
  277. /// - prefer `logger.info("nested info", metadata: ["nested": ["fave-numbers": ["\(1)", "\(2)", "\(3)"], "foo": "bar"]])`
  278. /// over `..., metadata: ["nested": .dictionary(["fave-numbers": ...])])`
  279. public enum MetadataValue {
  280. /// A metadata value which is a `String`.
  281. ///
  282. /// Because `MetadataValue` implements `ExpressibleByStringInterpolation`, and `ExpressibleByStringLiteral`,
  283. /// you don't need to type `.string(someType.description)` you can use the string interpolation `"\(someType)"`.
  284. case string(String)
  285. /// A metadata value which is some `CustomStringConvertible`.
  286. case stringConvertible(CustomStringConvertible)
  287. /// A metadata value which is a dictionary from `String` to `Logger.MetadataValue`.
  288. ///
  289. /// Because `MetadataValue` implements `ExpressibleByDictionaryLiteral`, you don't need to type
  290. /// `.dictionary(["foo": .string("bar \(buz)")])`, you can just use the more natural `["foo": "bar \(buz)"]`.
  291. case dictionary(Metadata)
  292. /// A metadata value which is an array of `Logger.MetadataValue`s.
  293. ///
  294. /// Because `MetadataValue` implements `ExpressibleByArrayLiteral`, you don't need to type
  295. /// `.array([.string("foo"), .string("bar \(buz)")])`, you can just use the more natural `["foo", "bar \(buz)"]`.
  296. case array([Metadata.Value])
  297. }
  298. /// The log level.
  299. ///
  300. /// Log levels are ordered by their severity, with `.trace` being the least severe and
  301. /// `.critical` being the most severe.
  302. public enum Level: String, Codable, CaseIterable {
  303. /// Appropriate for messages that contain information only when debugging a program.
  304. case trace
  305. /// Appropriate for messages that contain information normally of use only when
  306. /// debugging a program.
  307. case debug
  308. /// Appropriate for informational messages.
  309. case info
  310. /// Appropriate for conditions that are not error conditions, but that may require
  311. /// special handling.
  312. case notice
  313. /// Appropriate for messages that are not error conditions, but more severe than
  314. /// `.notice`.
  315. case warning
  316. /// Appropriate for error conditions.
  317. case error
  318. /// Appropriate for critical error conditions that usually require immediate
  319. /// attention.
  320. ///
  321. /// When a `critical` message is logged, the logging backend (`LogHandler`) is free to perform
  322. /// more heavy-weight operations to capture system state (such as capturing stack traces) to facilitate
  323. /// debugging.
  324. case critical
  325. }
  326. /// Construct a `Logger` given a `label` identifying the creator of the `Logger`.
  327. ///
  328. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  329. /// a datatype.
  330. ///
  331. /// - parameters:
  332. /// - label: An identifier for the creator of a `Logger`.
  333. public init(label: String) {
  334. self = LoggingSystem.lock.withReaderLock { Logger(label: label, LoggingSystem.factory(label)) }
  335. }
  336. /// Construct a `Logger` given a `label` identifying the creator of the `Logger` or a non-standard `LogHandler`.
  337. ///
  338. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  339. /// a datatype.
  340. ///
  341. /// This initializer provides an escape hatch in case the global default logging backend implementation (set up
  342. /// using `LoggingSystem.bootstrap` is not appropriate for this particular logger.
  343. ///
  344. /// - parameters:
  345. /// - label: An identifier for the creator of a `Logger`.
  346. /// - factory: A closure creating non-standard `LogHandler`s.
  347. public init(label: String, factory: (String) -> LogHandler) {
  348. self = Logger(label: label, factory(label))
  349. }
  350. }
  351. extension Logger.Level {
  352. internal var naturalIntegralValue: Int {
  353. switch self {
  354. case .trace:
  355. return 0
  356. case .debug:
  357. return 1
  358. case .info:
  359. return 2
  360. case .notice:
  361. return 3
  362. case .warning:
  363. return 4
  364. case .error:
  365. return 5
  366. case .critical:
  367. return 6
  368. }
  369. }
  370. }
  371. extension Logger.Level: Comparable {
  372. public static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool {
  373. return lhs.naturalIntegralValue < rhs.naturalIntegralValue
  374. }
  375. }
  376. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  377. // https://bugs.swift.org/browse/SR-9687
  378. // Then we could write it as follows and it would work under Swift 5 and not only 4 as it does currently:
  379. // extension Logger.Metadata.Value: Equatable {
  380. extension Logger.MetadataValue: Equatable {
  381. public static func == (lhs: Logger.Metadata.Value, rhs: Logger.Metadata.Value) -> Bool {
  382. switch (lhs, rhs) {
  383. case (.string(let lhs), .string(let rhs)):
  384. return lhs == rhs
  385. case (.stringConvertible(let lhs), .stringConvertible(let rhs)):
  386. return lhs.description == rhs.description
  387. case (.array(let lhs), .array(let rhs)):
  388. return lhs == rhs
  389. case (.dictionary(let lhs), .dictionary(let rhs)):
  390. return lhs == rhs
  391. default:
  392. return false
  393. }
  394. }
  395. }
  396. extension Logger {
  397. /// `Logger.Message` represents a log message's text. It is usually created using string literals.
  398. ///
  399. /// Example creating a `Logger.Message`:
  400. ///
  401. /// let world: String = "world"
  402. /// let myLogMessage: Logger.Message = "Hello \(world)"
  403. ///
  404. /// Most commonly, `Logger.Message`s appear simply as the parameter to a logging method such as:
  405. ///
  406. /// logger.info("Hello \(world)")
  407. ///
  408. public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, ExpressibleByStringInterpolation {
  409. public typealias StringLiteralType = String
  410. private var value: String
  411. public init(stringLiteral value: String) {
  412. self.value = value
  413. }
  414. public var description: String {
  415. return self.value
  416. }
  417. }
  418. }
  419. /// A pseudo-`LogHandler` that can be used to send messages to multiple other `LogHandler`s.
  420. ///
  421. /// The first `LogHandler` passed to the initialisation function of `MultiplexLogHandler` control the `logLevel` as
  422. /// well as the `metadata` for this `LogHandler`. Any subsequent `LogHandler`s used to initialise a
  423. /// `MultiplexLogHandler` are merely to emit the log message to another place.
  424. public struct MultiplexLogHandler: LogHandler {
  425. private var handlers: [LogHandler]
  426. /// Create a `MultiplexLogHandler`.
  427. ///
  428. /// - parameters:
  429. /// - handlers: An array of `LogHandler`s, each of which will receive the log messages sent to this `Logger`.
  430. /// The array must not be empty.
  431. public init(_ handlers: [LogHandler]) {
  432. assert(!handlers.isEmpty)
  433. self.handlers = handlers
  434. }
  435. public var logLevel: Logger.Level {
  436. get {
  437. return self.handlers[0].logLevel
  438. }
  439. set {
  440. self.mutatingForEachHandler {
  441. $0.logLevel = newValue
  442. }
  443. }
  444. }
  445. public func log(level: Logger.Level,
  446. message: Logger.Message,
  447. metadata: Logger.Metadata?,
  448. file: String, function: String, line: UInt) {
  449. self.handlers.forEach { handler in
  450. handler.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line)
  451. }
  452. }
  453. public var metadata: Logger.Metadata {
  454. get {
  455. return self.handlers[0].metadata
  456. }
  457. set {
  458. self.mutatingForEachHandler { $0.metadata = newValue }
  459. }
  460. }
  461. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  462. get {
  463. return self.handlers[0].metadata[metadataKey]
  464. }
  465. set {
  466. self.mutatingForEachHandler { $0[metadataKey: metadataKey] = newValue }
  467. }
  468. }
  469. private mutating func mutatingForEachHandler(_ mutator: (inout LogHandler) -> Void) {
  470. for index in self.handlers.indices {
  471. mutator(&self.handlers[index])
  472. }
  473. }
  474. }
  475. /// A wrapper to facilitate `print`-ing to stderr and stdio that
  476. /// ensures access to the underlying `FILE` is locked to prevent
  477. /// cross-thread interleaving of output.
  478. internal struct StdioOutputStream: TextOutputStream {
  479. internal let file: UnsafeMutablePointer<FILE>
  480. internal let flushMode: FlushMode
  481. internal func write(_ string: String) {
  482. string.withCString { ptr in
  483. #if os(Windows)
  484. _lock_file(self.file)
  485. #else
  486. flockfile(self.file)
  487. #endif
  488. defer {
  489. #if os(Windows)
  490. _unlock_file(self.file)
  491. #else
  492. funlockfile(self.file)
  493. #endif
  494. }
  495. _ = fputs(ptr, self.file)
  496. if case .always = self.flushMode {
  497. self.flush()
  498. }
  499. }
  500. }
  501. /// Flush the underlying stream.
  502. /// This has no effect when using the `.always` flush mode, which is the default
  503. internal func flush() {
  504. _ = fflush(self.file)
  505. }
  506. internal static let stderr = StdioOutputStream(file: systemStderr, flushMode: .always)
  507. internal static let stdout = StdioOutputStream(file: systemStdout, flushMode: .always)
  508. /// Defines the flushing strategy for the underlying stream.
  509. internal enum FlushMode {
  510. case undefined
  511. case always
  512. }
  513. }
  514. // Prevent name clashes
  515. #if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
  516. let systemStderr = Darwin.stderr
  517. let systemStdout = Darwin.stdout
  518. #elseif os(Windows)
  519. let systemStderr = MSVCRT.stderr
  520. let systemStdout = MSVCRT.stdout
  521. #else
  522. let systemStderr = Glibc.stderr!
  523. let systemStdout = Glibc.stdout!
  524. #endif
  525. /// `StreamLogHandler` is a simple implementation of `LogHandler` for directing
  526. /// `Logger` output to either `stderr` or `stdout` via the factory methods.
  527. public struct StreamLogHandler: LogHandler {
  528. /// Factory that makes a `StreamLogHandler` to directs its output to `stdout`
  529. public static func standardOutput(label: String) -> StreamLogHandler {
  530. return StreamLogHandler(label: label, stream: StdioOutputStream.stdout)
  531. }
  532. /// Factory that makes a `StreamLogHandler` to directs its output to `stderr`
  533. public static func standardError(label: String) -> StreamLogHandler {
  534. return StreamLogHandler(label: label, stream: StdioOutputStream.stderr)
  535. }
  536. private let stream: TextOutputStream
  537. private let label: String
  538. public var logLevel: Logger.Level = .info
  539. private var prettyMetadata: String?
  540. public var metadata = Logger.Metadata() {
  541. didSet {
  542. self.prettyMetadata = self.prettify(self.metadata)
  543. }
  544. }
  545. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  546. get {
  547. return self.metadata[metadataKey]
  548. }
  549. set {
  550. self.metadata[metadataKey] = newValue
  551. }
  552. }
  553. // internal for testing only
  554. internal init(label: String, stream: TextOutputStream) {
  555. self.label = label
  556. self.stream = stream
  557. }
  558. public func log(level: Logger.Level,
  559. message: Logger.Message,
  560. metadata: Logger.Metadata?,
  561. file: String, function: String, line: UInt) {
  562. let prettyMetadata = metadata?.isEmpty ?? true
  563. ? self.prettyMetadata
  564. : self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new }))
  565. var stream = self.stream
  566. stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") \(message)\n")
  567. }
  568. private func prettify(_ metadata: Logger.Metadata) -> String? {
  569. return !metadata.isEmpty ? metadata.map { "\($0)=\($1)" }.joined(separator: " ") : nil
  570. }
  571. private func timestamp() -> String {
  572. var buffer = [Int8](repeating: 0, count: 255)
  573. var timestamp = time(nil)
  574. let localTime = localtime(&timestamp)
  575. strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", localTime)
  576. return buffer.withUnsafeBufferPointer {
  577. $0.withMemoryRebound(to: CChar.self) {
  578. String(cString: $0.baseAddress!)
  579. }
  580. }
  581. }
  582. }
  583. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  584. // https://bugs.swift.org/browse/SR-9686
  585. extension Logger.MetadataValue: ExpressibleByStringLiteral {
  586. public typealias StringLiteralType = String
  587. public init(stringLiteral value: String) {
  588. self = .string(value)
  589. }
  590. }
  591. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  592. // https://bugs.swift.org/browse/SR-9686
  593. extension Logger.MetadataValue: CustomStringConvertible {
  594. public var description: String {
  595. switch self {
  596. case .dictionary(let dict):
  597. return dict.mapValues { $0.description }.description
  598. case .array(let list):
  599. return list.map { $0.description }.description
  600. case .string(let str):
  601. return str
  602. case .stringConvertible(let repr):
  603. return repr.description
  604. }
  605. }
  606. }
  607. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  608. // https://bugs.swift.org/browse/SR-9687
  609. extension Logger.MetadataValue: ExpressibleByStringInterpolation {}
  610. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  611. // https://bugs.swift.org/browse/SR-9686
  612. extension Logger.MetadataValue: ExpressibleByDictionaryLiteral {
  613. public typealias Key = String
  614. public typealias Value = Logger.Metadata.Value
  615. public init(dictionaryLiteral elements: (String, Logger.Metadata.Value)...) {
  616. self = .dictionary(.init(uniqueKeysWithValues: elements))
  617. }
  618. }
  619. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  620. // https://bugs.swift.org/browse/SR-9686
  621. extension Logger.MetadataValue: ExpressibleByArrayLiteral {
  622. public typealias ArrayLiteralElement = Logger.Metadata.Value
  623. public init(arrayLiteral elements: Logger.Metadata.Value...) {
  624. self = .array(elements)
  625. }
  626. }