|
| 1 | +// |
| 2 | +// LogChannelLocation + customRaw.swift |
| 3 | +// |
| 4 | +// |
| 5 | +// Created by Ky Leggiero on 2023-06-08. |
| 6 | +// |
| 7 | + |
| 8 | +import Foundation |
| 9 | +import FunctionTools |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +private let errorMessage_logNonRawMessageToRawLocation = "<<SimpleLogging: Attempted to log non-raw message to raw location>>" |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +/// Log to a function, so you can implement some custom logging location where you assemble it yourself. |
| 18 | +/// |
| 19 | +/// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. |
| 20 | +/// |
| 21 | +/// Log channels may choose to pre-filter messages before that function is called. |
| 22 | +public struct CustomRawLogChannelLocation: LogChannelLocation { |
| 23 | + |
| 24 | + /// API users give us this to tell us where/how to log messages |
| 25 | + private let logger: Callback<RawLogMessage> |
| 26 | + |
| 27 | + |
| 28 | + /// Create a new custom log channel location |
| 29 | + /// |
| 30 | + /// The function is passed the raw data about what to log. |
| 31 | + /// Log channels may choose to pre-filter messages before that function is called. |
| 32 | + /// |
| 33 | + /// - Parameter logger: Passed the raw, unrendered log message & metadata |
| 34 | + init(loggingTo logger: @escaping Callback<RawLogMessage>) { |
| 35 | + self.logger = logger |
| 36 | + } |
| 37 | + |
| 38 | + |
| 39 | + public func append(_ message: LogMessageProtocol, options: LoggingOptions) { |
| 40 | + if let message = message as? RawLogMessage { |
| 41 | + logger(message) |
| 42 | + } |
| 43 | + else { |
| 44 | + assertionFailure( |
| 45 | + """ |
| 46 | + When logging to a raw location, `message` must be a `RawLogMessage`. |
| 47 | + Instead, this was received: \(type(of: message)) |
| 48 | +
|
| 49 | + In prodiction, this will be logged as a rendered log line, along with info about this failure, with `error` severity to Swift's `print` destination. |
| 50 | + Production lines will have the following text inside them: |
| 51 | + \(errorMessage_logNonRawMessageToRawLocation) |
| 52 | +
|
| 53 | + The entire rendered log line (including this error and the original severity) will be placed in the message location of the log line. |
| 54 | + Because `RawLogMessage` is the only built-in message which knows its code location, a dummy code location will be used instead, with a file path of `"error"`, a function name of `"error"`, and a line number of `0xbad_c0de` (`195936478`). |
| 55 | + """ |
| 56 | + ) |
| 57 | + |
| 58 | + print( |
| 59 | + RawLogMessage( |
| 60 | + dateLogged: message.dateLogged, |
| 61 | + severity: .error, |
| 62 | + codeLocation: CodeLocation(fullFilePath: "error", functionIdentifier: "error", lineNumber: 0xbad_c0de), |
| 63 | + message: "\t \(errorMessage_logNonRawMessageToRawLocation)\t \(message.entireRenderedLogLine(options: options))" |
| 64 | + ) |
| 65 | + .entireRenderedLogLine(options: options)) |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +public extension UnreliableLogChannelLocation where Self == CustomRawLogChannelLocation { |
| 73 | + |
| 74 | + /// Log to a function, so you can implement some custom logging location where you assemble it yourself. |
| 75 | + /// |
| 76 | + /// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. |
| 77 | + /// |
| 78 | + /// Log channels may choose to pre-filter messages before that function is called. |
| 79 | + /// |
| 80 | + /// - Parameter logger: Passed the raw, unrendered log message & metadata |
| 81 | + static func customRaw(loggingTo logger: @escaping Callback<RawLogMessage>) -> Self { |
| 82 | + .init(loggingTo: logger) |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | + |
| 87 | + |
| 88 | +public extension LogChannel where Location == CustomRawLogChannelLocation { |
| 89 | + /// Log to a function, so you can implement some custom logging location where you assemble it yourself. |
| 90 | + /// |
| 91 | + /// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. |
| 92 | + /// |
| 93 | + /// Log channels may choose to pre-filter messages before that function is called. |
| 94 | + /// |
| 95 | + /// - Parameters: |
| 96 | + /// - name: The human-readable name of the channel |
| 97 | + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.defaultFilter` |
| 98 | + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` |
| 99 | + /// - logger: Passed the raw, unrendered log message & metadata |
| 100 | + func customRaw( |
| 101 | + name: String, |
| 102 | + lowestAllowedSeverity: LogSeverity, |
| 103 | + logSeverityNameStyle: SeverityNameStyle = .default, |
| 104 | + logger: @escaping Callback<RawLogMessage>) |
| 105 | + -> Self { |
| 106 | + .init(name: name, |
| 107 | + location: Location(loggingTo: logger), |
| 108 | + lowestAllowedSeverity: lowestAllowedSeverity, |
| 109 | + logSeverityNameStyle: logSeverityNameStyle) |
| 110 | + } |
| 111 | + |
| 112 | + |
| 113 | + /// Log to a function, so you can implement some custom logging channel without defining a new `struct`. |
| 114 | + /// |
| 115 | + /// The function is passed the fully-rendered log line, like |
| 116 | + /// `2020-11-20 05:26:49.178Z ⚠️ LogToFileTests.swift:144 testLogOnlyCriticalSeveritiesToFile() This message is a warning` |
| 117 | + /// |
| 118 | + /// - Parameters: |
| 119 | + /// - name: The human-readable name of the channel |
| 120 | + /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to `.default` |
| 121 | + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` |
| 122 | + /// - logger: Passed the raw, unrendered log message & metadata |
| 123 | + static func custom( |
| 124 | + name: String, |
| 125 | + severityFilter: LogSeverityFilter = .default, |
| 126 | + logSeverityNameStyle: SeverityNameStyle = .default, |
| 127 | + logger: @escaping Callback<RawLogMessage>) |
| 128 | + -> Self { |
| 129 | + .init(name: name, |
| 130 | + location: Location(loggingTo: logger), |
| 131 | + severityFilter: severityFilter, |
| 132 | + logSeverityNameStyle: logSeverityNameStyle) |
| 133 | + } |
| 134 | +} |
0 commit comments