Logging.swift 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284
  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. #elseif canImport(Glibc)
  19. import Glibc
  20. #elseif canImport(WASILibc)
  21. import WASILibc
  22. #else
  23. #error("Unsupported runtime")
  24. #endif
  25. /// A `Logger` is the central type in `SwiftLog`. Its central function is to emit log messages using one of the methods
  26. /// corresponding to a log level.
  27. ///
  28. /// `Logger`s are value types with respect to the `logLevel` and the `metadata` (as well as the immutable `label`
  29. /// and the selected `LogHandler`). Therefore, `Logger`s are suitable to be passed around between libraries if you want
  30. /// to preserve metadata across libraries.
  31. ///
  32. /// The most basic usage of a `Logger` is
  33. ///
  34. /// logger.info("Hello World!")
  35. ///
  36. public struct Logger {
  37. @usableFromInline
  38. var handler: LogHandler
  39. /// An identifier of the creator of this `Logger`.
  40. public let label: String
  41. internal init(label: String, _ handler: LogHandler) {
  42. self.label = label
  43. self.handler = handler
  44. }
  45. }
  46. extension Logger {
  47. #if compiler(>=5.3)
  48. /// Log a message passing the log level as a parameter.
  49. ///
  50. /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged,
  51. /// otherwise nothing will happen.
  52. ///
  53. /// - parameters:
  54. /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`.
  55. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  56. /// - metadata: One-off metadata to attach to this log message.
  57. /// - source: The source this log messages originates from. Defaults
  58. /// to the module emitting the log message (on Swift 5.3 or
  59. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  60. /// older).
  61. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  62. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  63. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  64. /// it defaults to `#function`).
  65. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  66. /// defaults to `#line`).
  67. @inlinable
  68. public func log(level: Logger.Level,
  69. _ message: @autoclosure () -> Logger.Message,
  70. metadata: @autoclosure () -> Logger.Metadata? = nil,
  71. source: @autoclosure () -> String? = nil,
  72. file: String = #fileID, function: String = #function, line: UInt = #line) {
  73. if self.logLevel <= level {
  74. self.handler.log(level: level,
  75. message: message(),
  76. metadata: metadata(),
  77. source: source() ?? Logger.currentModule(fileID: (file)),
  78. file: file, function: function, line: line)
  79. }
  80. }
  81. #else
  82. @inlinable
  83. public func log(level: Logger.Level,
  84. _ message: @autoclosure () -> Logger.Message,
  85. metadata: @autoclosure () -> Logger.Metadata? = nil,
  86. source: @autoclosure () -> String? = nil,
  87. file: String = #file, function: String = #function, line: UInt = #line) {
  88. if self.logLevel <= level {
  89. self.handler.log(level: level,
  90. message: message(),
  91. metadata: metadata(),
  92. source: source() ?? Logger.currentModule(filePath: (file)),
  93. file: file, function: function, line: line)
  94. }
  95. }
  96. #endif
  97. /// Log a message passing the log level as a parameter.
  98. ///
  99. /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged,
  100. /// otherwise nothing will happen.
  101. ///
  102. /// - parameters:
  103. /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`.
  104. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  105. /// - metadata: One-off metadata to attach to this log message.
  106. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  107. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  108. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  109. /// it defaults to `#function`).
  110. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  111. /// defaults to `#line`).
  112. #if compiler(>=5.3)
  113. @inlinable
  114. public func log(level: Logger.Level,
  115. _ message: @autoclosure () -> Logger.Message,
  116. metadata: @autoclosure () -> Logger.Metadata? = nil,
  117. file: String = #fileID, function: String = #function, line: UInt = #line) {
  118. self.log(level: level, message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  119. }
  120. #else
  121. @inlinable
  122. public func log(level: Logger.Level,
  123. _ message: @autoclosure () -> Logger.Message,
  124. metadata: @autoclosure () -> Logger.Metadata? = nil,
  125. file: String = #file, function: String = #function, line: UInt = #line) {
  126. self.log(level: level, message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  127. }
  128. #endif
  129. /// Add, change, or remove a logging metadata item.
  130. ///
  131. /// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the
  132. /// very `Logger` it was changed on.
  133. @inlinable
  134. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  135. get {
  136. return self.handler[metadataKey: metadataKey]
  137. }
  138. set {
  139. self.handler[metadataKey: metadataKey] = newValue
  140. }
  141. }
  142. /// Get or set the log level configured for this `Logger`.
  143. ///
  144. /// - note: `Logger`s treat `logLevel` as a value. This means that a change in `logLevel` will only affect this
  145. /// very `Logger`. It is acceptable for logging backends to have some form of global log level override
  146. /// that affects multiple or even all loggers. This means a change in `logLevel` to one `Logger` might in
  147. /// certain cases have no effect.
  148. @inlinable
  149. public var logLevel: Logger.Level {
  150. get {
  151. return self.handler.logLevel
  152. }
  153. set {
  154. self.handler.logLevel = newValue
  155. }
  156. }
  157. }
  158. extension Logger {
  159. /// Log a message passing with the `Logger.Level.trace` log level.
  160. ///
  161. /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  162. /// otherwise nothing will happen.
  163. ///
  164. /// - parameters:
  165. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  166. /// - metadata: One-off metadata to attach to this log message
  167. /// - source: The source this log messages originates from. Defaults
  168. /// to the module emitting the log message (on Swift 5.3 or
  169. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  170. /// older).
  171. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  172. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  173. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  174. /// it defaults to `#function`).
  175. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  176. /// defaults to `#line`).
  177. #if compiler(>=5.3)
  178. @inlinable
  179. public func trace(_ message: @autoclosure () -> Logger.Message,
  180. metadata: @autoclosure () -> Logger.Metadata? = nil,
  181. source: @autoclosure () -> String? = nil,
  182. file: String = #fileID, function: String = #function, line: UInt = #line) {
  183. self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  184. }
  185. #else
  186. @inlinable
  187. public func trace(_ message: @autoclosure () -> Logger.Message,
  188. metadata: @autoclosure () -> Logger.Metadata? = nil,
  189. source: @autoclosure () -> String? = nil,
  190. file: String = #file, function: String = #function, line: UInt = #line) {
  191. self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  192. }
  193. #endif
  194. /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  195. /// otherwise nothing will happen.
  196. ///
  197. /// - parameters:
  198. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  199. /// - metadata: One-off metadata to attach to this log message
  200. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  201. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  202. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  203. /// it defaults to `#function`).
  204. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  205. /// defaults to `#line`).
  206. #if compiler(>=5.3)
  207. @inlinable
  208. public func trace(_ message: @autoclosure () -> Logger.Message,
  209. metadata: @autoclosure () -> Logger.Metadata? = nil,
  210. file: String = #fileID, function: String = #function, line: UInt = #line) {
  211. self.trace(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  212. }
  213. #else
  214. @inlinable
  215. public func trace(_ message: @autoclosure () -> Logger.Message,
  216. metadata: @autoclosure () -> Logger.Metadata? = nil,
  217. file: String = #file, function: String = #function, line: UInt = #line) {
  218. self.trace(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  219. }
  220. #endif
  221. /// Log a message passing with the `Logger.Level.debug` log level.
  222. ///
  223. /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  224. /// otherwise nothing will happen.
  225. ///
  226. /// - parameters:
  227. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  228. /// - metadata: One-off metadata to attach to this log message.
  229. /// - source: The source this log messages originates from. Defaults
  230. /// to the module emitting the log message (on Swift 5.3 or
  231. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  232. /// older).
  233. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  234. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  235. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  236. /// it defaults to `#function`).
  237. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  238. /// defaults to `#line`).
  239. #if compiler(>=5.3)
  240. @inlinable
  241. public func debug(_ message: @autoclosure () -> Logger.Message,
  242. metadata: @autoclosure () -> Logger.Metadata? = nil,
  243. source: @autoclosure () -> String? = nil,
  244. file: String = #fileID, function: String = #function, line: UInt = #line) {
  245. self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  246. }
  247. #else
  248. public func debug(_ message: @autoclosure () -> Logger.Message,
  249. metadata: @autoclosure () -> Logger.Metadata? = nil,
  250. source: @autoclosure () -> String? = nil,
  251. file: String = #file, function: String = #function, line: UInt = #line) {
  252. self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  253. }
  254. #endif
  255. /// Log a message passing with the `Logger.Level.debug` log level.
  256. ///
  257. /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  258. /// otherwise nothing will happen.
  259. ///
  260. /// - parameters:
  261. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  262. /// - metadata: One-off metadata to attach to this log message.
  263. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  264. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  265. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  266. /// it defaults to `#function`).
  267. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  268. /// defaults to `#line`).
  269. #if compiler(>=5.3)
  270. @inlinable
  271. public func debug(_ message: @autoclosure () -> Logger.Message,
  272. metadata: @autoclosure () -> Logger.Metadata? = nil,
  273. file: String = #fileID, function: String = #function, line: UInt = #line) {
  274. self.debug(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  275. }
  276. #else
  277. @inlinable
  278. public func debug(_ message: @autoclosure () -> Logger.Message,
  279. metadata: @autoclosure () -> Logger.Metadata? = nil,
  280. file: String = #file, function: String = #function, line: UInt = #line) {
  281. self.debug(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  282. }
  283. #endif
  284. /// Log a message passing with the `Logger.Level.info` log level.
  285. ///
  286. /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  287. /// otherwise nothing will happen.
  288. ///
  289. /// - parameters:
  290. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  291. /// - metadata: One-off metadata to attach to this log message.
  292. /// - source: The source this log messages originates from. Defaults
  293. /// to the module emitting the log message (on Swift 5.3 or
  294. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  295. /// older).
  296. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  297. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  298. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  299. /// it defaults to `#function`).
  300. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  301. /// defaults to `#line`).
  302. #if compiler(>=5.3)
  303. @inlinable
  304. public func info(_ message: @autoclosure () -> Logger.Message,
  305. metadata: @autoclosure () -> Logger.Metadata? = nil,
  306. source: @autoclosure () -> String? = nil,
  307. file: String = #fileID, function: String = #function, line: UInt = #line) {
  308. self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  309. }
  310. #else
  311. @inlinable
  312. public func info(_ message: @autoclosure () -> Logger.Message,
  313. metadata: @autoclosure () -> Logger.Metadata? = nil,
  314. source: @autoclosure () -> String? = nil,
  315. file: String = #file, function: String = #function, line: UInt = #line) {
  316. self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  317. }
  318. #endif
  319. /// Log a message passing with the `Logger.Level.info` log level.
  320. ///
  321. /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  322. /// otherwise nothing will happen.
  323. ///
  324. /// - parameters:
  325. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  326. /// - metadata: One-off metadata to attach to this log message.
  327. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  328. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  329. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  330. /// it defaults to `#function`).
  331. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  332. /// defaults to `#line`).
  333. #if compiler(>=5.3)
  334. @inlinable
  335. public func info(_ message: @autoclosure () -> Logger.Message,
  336. metadata: @autoclosure () -> Logger.Metadata? = nil,
  337. file: String = #fileID, function: String = #function, line: UInt = #line) {
  338. self.info(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  339. }
  340. #else
  341. @inlinable
  342. public func info(_ message: @autoclosure () -> Logger.Message,
  343. metadata: @autoclosure () -> Logger.Metadata? = nil,
  344. file: String = #file, function: String = #function, line: UInt = #line) {
  345. self.info(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  346. }
  347. #endif
  348. /// Log a message passing with the `Logger.Level.notice` log level.
  349. ///
  350. /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  351. /// otherwise nothing will happen.
  352. ///
  353. /// - parameters:
  354. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  355. /// - metadata: One-off metadata to attach to this log message.
  356. /// - source: The source this log messages originates from. Defaults
  357. /// to the module emitting the log message (on Swift 5.3 or
  358. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  359. /// older).
  360. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  361. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  362. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  363. /// it defaults to `#function`).
  364. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  365. /// defaults to `#line`).
  366. #if compiler(>=5.3)
  367. @inlinable
  368. public func notice(_ message: @autoclosure () -> Logger.Message,
  369. metadata: @autoclosure () -> Logger.Metadata? = nil,
  370. source: @autoclosure () -> String? = nil,
  371. file: String = #fileID, function: String = #function, line: UInt = #line) {
  372. self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  373. }
  374. #else
  375. @inlinable
  376. public func notice(_ message: @autoclosure () -> Logger.Message,
  377. metadata: @autoclosure () -> Logger.Metadata? = nil,
  378. source: @autoclosure () -> String? = nil,
  379. file: String = #file, function: String = #function, line: UInt = #line) {
  380. self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  381. }
  382. #endif
  383. /// Log a message passing with the `Logger.Level.notice` log level.
  384. ///
  385. /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  386. /// otherwise nothing will happen.
  387. ///
  388. /// - parameters:
  389. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  390. /// - metadata: One-off metadata to attach to this log message.
  391. /// - source: The source this log messages originates from. Defaults
  392. /// to the module emitting the log message (on Swift 5.3 or
  393. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  394. /// older).
  395. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  396. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  397. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  398. /// it defaults to `#function`).
  399. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  400. /// defaults to `#line`).
  401. #if compiler(>=5.3)
  402. @inlinable
  403. public func notice(_ message: @autoclosure () -> Logger.Message,
  404. metadata: @autoclosure () -> Logger.Metadata? = nil,
  405. file: String = #fileID, function: String = #function, line: UInt = #line) {
  406. self.notice(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  407. }
  408. #else
  409. public func notice(_ message: @autoclosure () -> Logger.Message,
  410. metadata: @autoclosure () -> Logger.Metadata? = nil,
  411. file: String = #file, function: String = #function, line: UInt = #line) {
  412. self.notice(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  413. }
  414. #endif
  415. /// Log a message passing with the `Logger.Level.warning` log level.
  416. ///
  417. /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  418. /// otherwise nothing will happen.
  419. ///
  420. /// - parameters:
  421. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  422. /// - metadata: One-off metadata to attach to this log message.
  423. /// - source: The source this log messages originates from. Defaults
  424. /// to the module emitting the log message (on Swift 5.3 or
  425. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  426. /// older).
  427. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  428. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  429. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  430. /// it defaults to `#function`).
  431. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  432. /// defaults to `#line`).
  433. #if compiler(>=5.3)
  434. @inlinable
  435. public func warning(_ message: @autoclosure () -> Logger.Message,
  436. metadata: @autoclosure () -> Logger.Metadata? = nil,
  437. source: @autoclosure () -> String? = nil,
  438. file: String = #fileID, function: String = #function, line: UInt = #line) {
  439. self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  440. }
  441. #else
  442. @inlinable
  443. public func warning(_ message: @autoclosure () -> Logger.Message,
  444. metadata: @autoclosure () -> Logger.Metadata? = nil,
  445. source: @autoclosure () -> String? = nil,
  446. file: String = #file, function: String = #function, line: UInt = #line) {
  447. self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  448. }
  449. #endif
  450. /// Log a message passing with the `Logger.Level.warning` log level.
  451. ///
  452. /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  453. /// otherwise nothing will happen.
  454. ///
  455. /// - parameters:
  456. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  457. /// - metadata: One-off metadata to attach to this log message.
  458. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  459. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  460. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  461. /// it defaults to `#function`).
  462. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  463. /// defaults to `#line`).
  464. #if compiler(>=5.3)
  465. @inlinable
  466. public func warning(_ message: @autoclosure () -> Logger.Message,
  467. metadata: @autoclosure () -> Logger.Metadata? = nil,
  468. file: String = #fileID, function: String = #function, line: UInt = #line) {
  469. self.warning(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  470. }
  471. #else
  472. @inlinable
  473. public func warning(_ message: @autoclosure () -> Logger.Message,
  474. metadata: @autoclosure () -> Logger.Metadata? = nil,
  475. file: String = #file, function: String = #function, line: UInt = #line) {
  476. self.warning(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  477. }
  478. #endif
  479. /// Log a message passing with the `Logger.Level.error` log level.
  480. ///
  481. /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  482. /// otherwise nothing will happen.
  483. ///
  484. /// - parameters:
  485. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  486. /// - metadata: One-off metadata to attach to this log message.
  487. /// - source: The source this log messages originates from. Defaults
  488. /// to the module emitting the log message (on Swift 5.3 or
  489. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  490. /// older).
  491. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  492. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  493. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  494. /// it defaults to `#function`).
  495. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  496. /// defaults to `#line`).
  497. #if compiler(>=5.3)
  498. @inlinable
  499. public func error(_ message: @autoclosure () -> Logger.Message,
  500. metadata: @autoclosure () -> Logger.Metadata? = nil,
  501. source: @autoclosure () -> String? = nil,
  502. file: String = #fileID, function: String = #function, line: UInt = #line) {
  503. self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  504. }
  505. #else
  506. @inlinable
  507. public func error(_ message: @autoclosure () -> Logger.Message,
  508. metadata: @autoclosure () -> Logger.Metadata? = nil,
  509. source: @autoclosure () -> String? = nil,
  510. file: String = #file, function: String = #function, line: UInt = #line) {
  511. self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  512. }
  513. #endif
  514. /// Log a message passing with the `Logger.Level.error` log level.
  515. ///
  516. /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
  517. /// otherwise nothing will happen.
  518. ///
  519. /// - parameters:
  520. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  521. /// - metadata: One-off metadata to attach to this log message.
  522. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  523. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  524. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  525. /// it defaults to `#function`).
  526. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  527. /// defaults to `#line`).
  528. #if compiler(>=5.3)
  529. @inlinable
  530. public func error(_ message: @autoclosure () -> Logger.Message,
  531. metadata: @autoclosure () -> Logger.Metadata? = nil,
  532. file: String = #fileID, function: String = #function, line: UInt = #line) {
  533. self.error(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  534. }
  535. #else
  536. @inlinable
  537. public func error(_ message: @autoclosure () -> Logger.Message,
  538. metadata: @autoclosure () -> Logger.Metadata? = nil,
  539. file: String = #file, function: String = #function, line: UInt = #line) {
  540. self.error(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  541. }
  542. #endif
  543. /// Log a message passing with the `Logger.Level.critical` log level.
  544. ///
  545. /// `.critical` messages will always be logged.
  546. ///
  547. /// - parameters:
  548. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  549. /// - metadata: One-off metadata to attach to this log message.
  550. /// - source: The source this log messages originates from. Defaults
  551. /// to the module emitting the log message (on Swift 5.3 or
  552. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  553. /// older).
  554. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  555. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  556. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  557. /// it defaults to `#function`).
  558. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  559. /// defaults to `#line`).
  560. #if compiler(>=5.3)
  561. @inlinable
  562. public func critical(_ message: @autoclosure () -> Logger.Message,
  563. metadata: @autoclosure () -> Logger.Metadata? = nil,
  564. source: @autoclosure () -> String? = nil,
  565. file: String = #fileID, function: String = #function, line: UInt = #line) {
  566. self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  567. }
  568. #else
  569. @inlinable
  570. public func critical(_ message: @autoclosure () -> Logger.Message,
  571. metadata: @autoclosure () -> Logger.Metadata? = nil,
  572. source: @autoclosure () -> String? = nil,
  573. file: String = #file, function: String = #function, line: UInt = #line) {
  574. self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
  575. }
  576. #endif
  577. /// Log a message passing with the `Logger.Level.critical` log level.
  578. ///
  579. /// `.critical` messages will always be logged.
  580. ///
  581. /// - parameters:
  582. /// - message: The message to be logged. `message` can be used with any string interpolation literal.
  583. /// - metadata: One-off metadata to attach to this log message.
  584. /// - source: The source this log messages originates from. Defaults
  585. /// to the module emitting the log message (on Swift 5.3 or
  586. /// newer and the folder name containing the log emitting file on Swift 5.2 or
  587. /// older).
  588. /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
  589. /// defaults to `#fileID` (on Swift 5.3 or newer and `#file` on Swift 5.2 or older).
  590. /// - function: The function this log message originates from (there's usually no need to pass it explicitly as
  591. /// it defaults to `#function`).
  592. /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
  593. /// defaults to `#line`).
  594. #if compiler(>=5.3)
  595. @inlinable
  596. public func critical(_ message: @autoclosure () -> Logger.Message,
  597. metadata: @autoclosure () -> Logger.Metadata? = nil,
  598. file: String = #fileID, function: String = #function, line: UInt = #line) {
  599. self.critical(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  600. }
  601. #else
  602. @inlinable
  603. public func critical(_ message: @autoclosure () -> Logger.Message,
  604. metadata: @autoclosure () -> Logger.Metadata? = nil,
  605. file: String = #file, function: String = #function, line: UInt = #line) {
  606. self.critical(message(), metadata: metadata(), source: nil, file: file, function: function, line: line)
  607. }
  608. #endif
  609. }
  610. /// The `LoggingSystem` is a global facility where the default logging backend implementation (`LogHandler`) can be
  611. /// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend
  612. /// implementation.
  613. public enum LoggingSystem {
  614. private static let _factory = FactoryBox(StreamLogHandler.standardOutput)
  615. /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend
  616. /// implementation. `bootstrap` can be called at maximum once in any given program, calling it more than once will
  617. /// lead to undefined behavior, most likely a crash.
  618. ///
  619. /// - parameters:
  620. /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`.
  621. public static func bootstrap(_ factory: @escaping (String) -> LogHandler) {
  622. self._factory.replaceFactory(factory, validate: true)
  623. }
  624. // for our testing we want to allow multiple bootstraping
  625. internal static func bootstrapInternal(_ factory: @escaping (String) -> LogHandler) {
  626. self._factory.replaceFactory(factory, validate: false)
  627. }
  628. fileprivate static var factory: (String) -> LogHandler {
  629. return self._factory.underlying
  630. }
  631. private final class FactoryBox {
  632. private let lock = ReadWriteLock()
  633. fileprivate var _underlying: (String) -> LogHandler
  634. private var initialized = false
  635. init(_ underlying: @escaping (String) -> LogHandler) {
  636. self._underlying = underlying
  637. }
  638. func replaceFactory(_ factory: @escaping (String) -> LogHandler, validate: Bool) {
  639. self.lock.withWriterLock {
  640. precondition(!validate || !self.initialized, "logging system can only be initialized once per process.")
  641. self._underlying = factory
  642. self.initialized = true
  643. }
  644. }
  645. var underlying: (String) -> LogHandler {
  646. return self.lock.withReaderLock {
  647. return self._underlying
  648. }
  649. }
  650. }
  651. }
  652. extension Logger {
  653. /// `Metadata` is a typealias for `[String: Logger.MetadataValue]` the type of the metadata storage.
  654. public typealias Metadata = [String: MetadataValue]
  655. /// A logging metadata value. `Logger.MetadataValue` is string, array, and dictionary literal convertible.
  656. ///
  657. /// `MetadataValue` provides convenient conformances to `ExpressibleByStringInterpolation`,
  658. /// `ExpressibleByStringLiteral`, `ExpressibleByArrayLiteral`, and `ExpressibleByDictionaryLiteral` which means
  659. /// that when constructing `MetadataValue`s you should default to using Swift's usual literals.
  660. ///
  661. /// Examples:
  662. /// - prefer `logger.info("user logged in", metadata: ["user-id": "\(user.id)"])` over
  663. /// `..., metadata: ["user-id": .string(user.id.description)])`
  664. /// - prefer `logger.info("user selected colors", metadata: ["colors": ["\(user.topColor)", "\(user.secondColor)"]])`
  665. /// over `..., metadata: ["colors": .array([.string("\(user.topColor)"), .string("\(user.secondColor)")])`
  666. /// - prefer `logger.info("nested info", metadata: ["nested": ["fave-numbers": ["\(1)", "\(2)", "\(3)"], "foo": "bar"]])`
  667. /// over `..., metadata: ["nested": .dictionary(["fave-numbers": ...])])`
  668. public enum MetadataValue {
  669. /// A metadata value which is a `String`.
  670. ///
  671. /// Because `MetadataValue` implements `ExpressibleByStringInterpolation`, and `ExpressibleByStringLiteral`,
  672. /// you don't need to type `.string(someType.description)` you can use the string interpolation `"\(someType)"`.
  673. case string(String)
  674. /// A metadata value which is some `CustomStringConvertible`.
  675. #if compiler(>=5.6)
  676. case stringConvertible(CustomStringConvertible & Sendable)
  677. #else
  678. case stringConvertible(CustomStringConvertible)
  679. #endif
  680. /// A metadata value which is a dictionary from `String` to `Logger.MetadataValue`.
  681. ///
  682. /// Because `MetadataValue` implements `ExpressibleByDictionaryLiteral`, you don't need to type
  683. /// `.dictionary(["foo": .string("bar \(buz)")])`, you can just use the more natural `["foo": "bar \(buz)"]`.
  684. case dictionary(Metadata)
  685. /// A metadata value which is an array of `Logger.MetadataValue`s.
  686. ///
  687. /// Because `MetadataValue` implements `ExpressibleByArrayLiteral`, you don't need to type
  688. /// `.array([.string("foo"), .string("bar \(buz)")])`, you can just use the more natural `["foo", "bar \(buz)"]`.
  689. case array([Metadata.Value])
  690. }
  691. /// The log level.
  692. ///
  693. /// Log levels are ordered by their severity, with `.trace` being the least severe and
  694. /// `.critical` being the most severe.
  695. public enum Level: String, Codable, CaseIterable {
  696. /// Appropriate for messages that contain information normally of use only when
  697. /// tracing the execution of a program.
  698. case trace
  699. /// Appropriate for messages that contain information normally of use only when
  700. /// debugging a program.
  701. case debug
  702. /// Appropriate for informational messages.
  703. case info
  704. /// Appropriate for conditions that are not error conditions, but that may require
  705. /// special handling.
  706. case notice
  707. /// Appropriate for messages that are not error conditions, but more severe than
  708. /// `.notice`.
  709. case warning
  710. /// Appropriate for error conditions.
  711. case error
  712. /// Appropriate for critical error conditions that usually require immediate
  713. /// attention.
  714. ///
  715. /// When a `critical` message is logged, the logging backend (`LogHandler`) is free to perform
  716. /// more heavy-weight operations to capture system state (such as capturing stack traces) to facilitate
  717. /// debugging.
  718. case critical
  719. }
  720. /// Construct a `Logger` given a `label` identifying the creator of the `Logger`.
  721. ///
  722. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  723. /// a datatype.
  724. ///
  725. /// - parameters:
  726. /// - label: An identifier for the creator of a `Logger`.
  727. public init(label: String) {
  728. self.init(label: label, LoggingSystem.factory(label))
  729. }
  730. /// Construct a `Logger` given a `label` identifying the creator of the `Logger` or a non-standard `LogHandler`.
  731. ///
  732. /// The `label` should identify the creator of the `Logger`. This can be an application, a sub-system, or even
  733. /// a datatype.
  734. ///
  735. /// This initializer provides an escape hatch in case the global default logging backend implementation (set up
  736. /// using `LoggingSystem.bootstrap` is not appropriate for this particular logger.
  737. ///
  738. /// - parameters:
  739. /// - label: An identifier for the creator of a `Logger`.
  740. /// - factory: A closure creating non-standard `LogHandler`s.
  741. public init(label: String, factory: (String) -> LogHandler) {
  742. self = Logger(label: label, factory(label))
  743. }
  744. }
  745. extension Logger.Level {
  746. internal var naturalIntegralValue: Int {
  747. switch self {
  748. case .trace:
  749. return 0
  750. case .debug:
  751. return 1
  752. case .info:
  753. return 2
  754. case .notice:
  755. return 3
  756. case .warning:
  757. return 4
  758. case .error:
  759. return 5
  760. case .critical:
  761. return 6
  762. }
  763. }
  764. }
  765. extension Logger.Level: Comparable {
  766. public static func < (lhs: Logger.Level, rhs: Logger.Level) -> Bool {
  767. return lhs.naturalIntegralValue < rhs.naturalIntegralValue
  768. }
  769. }
  770. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  771. // https://bugs.swift.org/browse/SR-9687
  772. // Then we could write it as follows and it would work under Swift 5 and not only 4 as it does currently:
  773. // extension Logger.Metadata.Value: Equatable {
  774. extension Logger.MetadataValue: Equatable {
  775. public static func == (lhs: Logger.Metadata.Value, rhs: Logger.Metadata.Value) -> Bool {
  776. switch (lhs, rhs) {
  777. case (.string(let lhs), .string(let rhs)):
  778. return lhs == rhs
  779. case (.stringConvertible(let lhs), .stringConvertible(let rhs)):
  780. return lhs.description == rhs.description
  781. case (.array(let lhs), .array(let rhs)):
  782. return lhs == rhs
  783. case (.dictionary(let lhs), .dictionary(let rhs)):
  784. return lhs == rhs
  785. default:
  786. return false
  787. }
  788. }
  789. }
  790. extension Logger {
  791. /// `Logger.Message` represents a log message's text. It is usually created using string literals.
  792. ///
  793. /// Example creating a `Logger.Message`:
  794. ///
  795. /// let world: String = "world"
  796. /// let myLogMessage: Logger.Message = "Hello \(world)"
  797. ///
  798. /// Most commonly, `Logger.Message`s appear simply as the parameter to a logging method such as:
  799. ///
  800. /// logger.info("Hello \(world)")
  801. ///
  802. public struct Message: ExpressibleByStringLiteral, Equatable, CustomStringConvertible, ExpressibleByStringInterpolation {
  803. public typealias StringLiteralType = String
  804. private var value: String
  805. public init(stringLiteral value: String) {
  806. self.value = value
  807. }
  808. public var description: String {
  809. return self.value
  810. }
  811. }
  812. }
  813. /// A pseudo-`LogHandler` that can be used to send messages to multiple other `LogHandler`s.
  814. ///
  815. /// ### Effective Logger.Level
  816. ///
  817. /// When first initialized the multiplex log handlers' log level is automatically set to the minimum of all the
  818. /// passed in log handlers. This ensures that each of the handlers will be able to log at their appropriate level
  819. /// any log events they might be interested in.
  820. ///
  821. /// Example:
  822. /// If log handler `A` is logging at `.debug` level, and log handler `B` is logging at `.info` level, the constructed
  823. /// `MultiplexLogHandler([A, B])`'s effective log level will be set to `.debug`, meaning that debug messages will be
  824. /// handled by this handler, while only logged by the underlying `A` log handler (since `B`'s log level is `.info`
  825. /// and thus it would not actually log that log message).
  826. ///
  827. /// If the log level is _set_ on a `Logger` backed by an `MultiplexLogHandler` the log level will apply to *all*
  828. /// underlying log handlers, allowing a logger to still select at what level it wants to log regardless of if the underlying
  829. /// 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
  830. /// handler passed into the multiplex log handler, this is possible by wrapping it in a handler which ignores any log level changes.
  831. ///
  832. /// ### Effective Logger.Metadata
  833. ///
  834. /// Since a `MultiplexLogHandler` is a combination of multiple log handlers, the handling of metadata can be non-obvious.
  835. /// For example, the underlying log handlers may have metadata of their own set before they are used to initialize the multiplex log handler.
  836. ///
  837. /// The multiplex log handler acts purely as proxy and does not make any changes to underlying handler metadata other than
  838. /// proxying writes that users made on a `Logger` instance backed by this handler.
  839. ///
  840. /// Setting metadata is always proxied through to _all_ underlying handlers, meaning that if a modification like
  841. /// `logger[metadataKey: "x"] = "y"` is made, all underlying log handlers that this multiplex handler was initiated with
  842. /// will observe this change.
  843. ///
  844. /// Reading metadata from the multiplex log handler MAY need to pick one of conflicting values if the underlying log handlers
  845. /// were already initiated with some metadata before passing them into the multiplex handler. The multiplex handler uses
  846. /// the order in which the handlers were passed in during its initialization as a priority indicator - the first handler's
  847. /// values are more important than the next handlers values, etc.
  848. ///
  849. /// Example:
  850. /// If the multiplex log handler was initiated with two handlers like this: `MultiplexLogHandler([handler1, handler2])`.
  851. /// The handlers each have some already set metadata: `handler1` has metadata values for keys `one` and `all`, and `handler2`
  852. /// has values for keys `two` and `all`.
  853. ///
  854. /// A query through the multiplex log handler the key `one` naturally returns `handler1`'s value, and a query for `two`
  855. /// naturally returns `handler2`'s value. Querying for the key `all` will return `handler1`'s value, as that handler was indicated
  856. /// "more important" than the second handler. The same rule applies when querying for the `metadata` property of the
  857. /// multiplex log handler - it constructs `Metadata` uniquing values.
  858. public struct MultiplexLogHandler: LogHandler {
  859. private var handlers: [LogHandler]
  860. private var effectiveLogLevel: Logger.Level
  861. /// Create a `MultiplexLogHandler`.
  862. ///
  863. /// - parameters:
  864. /// - handlers: An array of `LogHandler`s, each of which will receive the log messages sent to this `Logger`.
  865. /// The array must not be empty.
  866. public init(_ handlers: [LogHandler]) {
  867. assert(!handlers.isEmpty, "MultiplexLogHandler.handlers MUST NOT be empty")
  868. self.handlers = handlers
  869. self.effectiveLogLevel = handlers.map { $0.logLevel }.min() ?? .trace
  870. }
  871. public var logLevel: Logger.Level {
  872. get {
  873. return self.effectiveLogLevel
  874. }
  875. set {
  876. self.mutatingForEachHandler { $0.logLevel = newValue }
  877. self.effectiveLogLevel = newValue
  878. }
  879. }
  880. public func log(level: Logger.Level,
  881. message: Logger.Message,
  882. metadata: Logger.Metadata?,
  883. source: String,
  884. file: String,
  885. function: String,
  886. line: UInt) {
  887. for handler in self.handlers where handler.logLevel <= level {
  888. handler.log(level: level, message: message, metadata: metadata, source: source, file: file, function: function, line: line)
  889. }
  890. }
  891. public var metadata: Logger.Metadata {
  892. get {
  893. var effectiveMetadata: Logger.Metadata = [:]
  894. // as a rough estimate we assume that the underlying handlers have a similar metadata count,
  895. // and we use the first one's current count to estimate how big of a dictionary we need to allocate:
  896. effectiveMetadata.reserveCapacity(self.handlers.first!.metadata.count) // !-safe, we always have at least one handler
  897. return self.handlers.reduce(into: effectiveMetadata) { effectiveMetadata, handler in
  898. effectiveMetadata.merge(handler.metadata, uniquingKeysWith: { l, _ in l })
  899. }
  900. }
  901. set {
  902. self.mutatingForEachHandler { $0.metadata = newValue }
  903. }
  904. }
  905. public subscript(metadataKey metadataKey: Logger.Metadata.Key) -> Logger.Metadata.Value? {
  906. get {
  907. for handler in self.handlers {
  908. if let value = handler[metadataKey: metadataKey] {
  909. return value
  910. }
  911. }
  912. return nil
  913. }
  914. set {
  915. self.mutatingForEachHandler { $0[metadataKey: metadataKey] = newValue }
  916. }
  917. }
  918. private mutating func mutatingForEachHandler(_ mutator: (inout LogHandler) -> Void) {
  919. for index in self.handlers.indices {
  920. mutator(&self.handlers[index])
  921. }
  922. }
  923. }
  924. #if canImport(WASILibc) || os(Android)
  925. internal typealias CFilePointer = OpaquePointer
  926. #else
  927. internal typealias CFilePointer = UnsafeMutablePointer<FILE>
  928. #endif
  929. /// A wrapper to facilitate `print`-ing to stderr and stdio that
  930. /// ensures access to the underlying `FILE` is locked to prevent
  931. /// cross-thread interleaving of output.
  932. internal struct StdioOutputStream: TextOutputStream {
  933. internal let file: CFilePointer
  934. internal let flushMode: FlushMode
  935. internal func write(_ string: String) {
  936. self.contiguousUTF8(string).withContiguousStorageIfAvailable { utf8Bytes in
  937. #if os(Windows)
  938. _lock_file(self.file)
  939. #elseif canImport(WASILibc)
  940. // no file locking on WASI
  941. #else
  942. flockfile(self.file)
  943. #endif
  944. defer {
  945. #if os(Windows)
  946. _unlock_file(self.file)
  947. #elseif canImport(WASILibc)
  948. // no file locking on WASI
  949. #else
  950. funlockfile(self.file)
  951. #endif
  952. }
  953. _ = fwrite(utf8Bytes.baseAddress!, 1, utf8Bytes.count, self.file)
  954. if case .always = self.flushMode {
  955. self.flush()
  956. }
  957. }!
  958. }
  959. /// Flush the underlying stream.
  960. /// This has no effect when using the `.always` flush mode, which is the default
  961. internal func flush() {
  962. _ = fflush(self.file)
  963. }
  964. internal func contiguousUTF8(_ string: String) -> String.UTF8View {
  965. var contiguousString = string
  966. #if compiler(>=5.1)
  967. contiguousString.makeContiguousUTF8()
  968. #else
  969. contiguousString = string + ""
  970. #endif
  971. return contiguousString.utf8
  972. }
  973. internal static let stderr = StdioOutputStream(file: systemStderr, flushMode: .always)
  974. internal static let stdout = StdioOutputStream(file: systemStdout, flushMode: .always)
  975. /// Defines the flushing strategy for the underlying stream.
  976. internal enum FlushMode {
  977. case undefined
  978. case always
  979. }
  980. }
  981. // Prevent name clashes
  982. #if os(macOS) || os(tvOS) || os(iOS) || os(watchOS)
  983. let systemStderr = Darwin.stderr
  984. let systemStdout = Darwin.stdout
  985. #elseif os(Windows)
  986. let systemStderr = CRT.stderr
  987. let systemStdout = CRT.stdout
  988. #elseif canImport(Glibc)
  989. let systemStderr = Glibc.stderr!
  990. let systemStdout = Glibc.stdout!
  991. #elseif canImport(WASILibc)
  992. let systemStderr = WASILibc.stderr!
  993. let systemStdout = WASILibc.stdout!
  994. #else
  995. #error("Unsupported runtime")
  996. #endif
  997. /// `StreamLogHandler` is a simple implementation of `LogHandler` for directing
  998. /// `Logger` output to either `stderr` or `stdout` via the factory methods.
  999. public struct StreamLogHandler: LogHandler {
  1000. #if compiler(>=5.6)
  1001. internal typealias _SendableTextOutputStream = TextOutputStream & Sendable
  1002. #else
  1003. internal typealias _SendableTextOutputStream = TextOutputStream
  1004. #endif
  1005. /// Factory that makes a `StreamLogHandler` to directs its output to `stdout`
  1006. public static func standardOutput(label: String) -> StreamLogHandler {
  1007. return StreamLogHandler(label: label, stream: StdioOutputStream.stdout)
  1008. }
  1009. /// Factory that makes a `StreamLogHandler` to directs its output to `stderr`
  1010. public static func standardError(label: String) -> StreamLogHandler {
  1011. return StreamLogHandler(label: label, stream: StdioOutputStream.stderr)
  1012. }
  1013. private let stream: _SendableTextOutputStream
  1014. private let label: String
  1015. public var logLevel: Logger.Level = .info
  1016. private var prettyMetadata: String?
  1017. public var metadata = Logger.Metadata() {
  1018. didSet {
  1019. self.prettyMetadata = self.prettify(self.metadata)
  1020. }
  1021. }
  1022. public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
  1023. get {
  1024. return self.metadata[metadataKey]
  1025. }
  1026. set {
  1027. self.metadata[metadataKey] = newValue
  1028. }
  1029. }
  1030. // internal for testing only
  1031. internal init(label: String, stream: _SendableTextOutputStream) {
  1032. self.label = label
  1033. self.stream = stream
  1034. }
  1035. public func log(level: Logger.Level,
  1036. message: Logger.Message,
  1037. metadata: Logger.Metadata?,
  1038. source: String,
  1039. file: String,
  1040. function: String,
  1041. line: UInt) {
  1042. let prettyMetadata = metadata?.isEmpty ?? true
  1043. ? self.prettyMetadata
  1044. : self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new }))
  1045. var stream = self.stream
  1046. stream.write("\(self.timestamp()) \(level) \(self.label) :\(prettyMetadata.map { " \($0)" } ?? "") [\(source)] \(message)\n")
  1047. }
  1048. private func prettify(_ metadata: Logger.Metadata) -> String? {
  1049. return !metadata.isEmpty
  1050. ? metadata.lazy.sorted(by: { $0.key < $1.key }).map { "\($0)=\($1)" }.joined(separator: " ")
  1051. : nil
  1052. }
  1053. private func timestamp() -> String {
  1054. var buffer = [Int8](repeating: 0, count: 255)
  1055. #if os(Windows)
  1056. var timestamp: __time64_t = __time64_t()
  1057. _ = _time64(&timestamp)
  1058. var localTime: tm = tm()
  1059. _ = _localtime64_s(&localTime, &timestamp)
  1060. _ = strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", &localTime)
  1061. #else
  1062. var timestamp = time(nil)
  1063. let localTime = localtime(&timestamp)
  1064. strftime(&buffer, buffer.count, "%Y-%m-%dT%H:%M:%S%z", localTime)
  1065. #endif
  1066. return buffer.withUnsafeBufferPointer {
  1067. $0.withMemoryRebound(to: CChar.self) {
  1068. String(cString: $0.baseAddress!)
  1069. }
  1070. }
  1071. }
  1072. }
  1073. /// No operation LogHandler, used when no logging is required
  1074. public struct SwiftLogNoOpLogHandler: LogHandler {
  1075. public init() {}
  1076. public init(_: String) {}
  1077. @inlinable public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {}
  1078. @inlinable public subscript(metadataKey _: String) -> Logger.Metadata.Value? {
  1079. get {
  1080. return nil
  1081. }
  1082. set {}
  1083. }
  1084. @inlinable public var metadata: Logger.Metadata {
  1085. get {
  1086. return [:]
  1087. }
  1088. set {}
  1089. }
  1090. @inlinable public var logLevel: Logger.Level {
  1091. get {
  1092. return .critical
  1093. }
  1094. set {}
  1095. }
  1096. }
  1097. extension Logger {
  1098. @inlinable
  1099. internal static func currentModule(filePath: String = #file) -> String {
  1100. let utf8All = filePath.utf8
  1101. return filePath.utf8.lastIndex(of: UInt8(ascii: "/")).flatMap { lastSlash -> Substring? in
  1102. utf8All[..<lastSlash].lastIndex(of: UInt8(ascii: "/")).map { secondLastSlash -> Substring in
  1103. filePath[utf8All.index(after: secondLastSlash) ..< lastSlash]
  1104. }
  1105. }.map {
  1106. String($0)
  1107. } ?? "n/a"
  1108. }
  1109. #if compiler(>=5.3)
  1110. @inlinable
  1111. internal static func currentModule(fileID: String = #fileID) -> String {
  1112. let utf8All = fileID.utf8
  1113. if let slashIndex = utf8All.firstIndex(of: UInt8(ascii: "/")) {
  1114. return String(fileID[..<slashIndex])
  1115. } else {
  1116. return "n/a"
  1117. }
  1118. }
  1119. #endif
  1120. }
  1121. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  1122. // https://bugs.swift.org/browse/SR-9686
  1123. extension Logger.MetadataValue: ExpressibleByStringLiteral {
  1124. public typealias StringLiteralType = String
  1125. public init(stringLiteral value: String) {
  1126. self = .string(value)
  1127. }
  1128. }
  1129. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  1130. // https://bugs.swift.org/browse/SR-9686
  1131. extension Logger.MetadataValue: CustomStringConvertible {
  1132. public var description: String {
  1133. switch self {
  1134. case .dictionary(let dict):
  1135. return dict.mapValues { $0.description }.description
  1136. case .array(let list):
  1137. return list.map { $0.description }.description
  1138. case .string(let str):
  1139. return str
  1140. case .stringConvertible(let repr):
  1141. return repr.description
  1142. }
  1143. }
  1144. }
  1145. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  1146. // https://bugs.swift.org/browse/SR-9687
  1147. extension Logger.MetadataValue: ExpressibleByStringInterpolation {}
  1148. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  1149. // https://bugs.swift.org/browse/SR-9686
  1150. extension Logger.MetadataValue: ExpressibleByDictionaryLiteral {
  1151. public typealias Key = String
  1152. public typealias Value = Logger.Metadata.Value
  1153. public init(dictionaryLiteral elements: (String, Logger.Metadata.Value)...) {
  1154. self = .dictionary(.init(uniqueKeysWithValues: elements))
  1155. }
  1156. }
  1157. // Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
  1158. // https://bugs.swift.org/browse/SR-9686
  1159. extension Logger.MetadataValue: ExpressibleByArrayLiteral {
  1160. public typealias ArrayLiteralElement = Logger.Metadata.Value
  1161. public init(arrayLiteral elements: Logger.Metadata.Value...) {
  1162. self = .array(elements)
  1163. }
  1164. }
  1165. // MARK: - Sendable support helpers
  1166. #if compiler(>=5.6)
  1167. extension Logger: Sendable {}
  1168. extension Logger.MetadataValue: Sendable {}
  1169. extension Logger.Level: Sendable {}
  1170. extension Logger.Message: Sendable {}
  1171. #endif