From 1823dd873f56ebebeabb0bb046ebe04b51f24be9 Mon Sep 17 00:00:00 2001 From: Ky Leggiero Date: Fri, 9 Jun 2023 00:00:58 -0600 Subject: [PATCH] #17: Rebasing: custom raw log location --- README.md | 2 +- .../SimpleLogging/LogChannel/LogChannel.swift | 2 +- .../LogChannelLocation + custom.swift | 16 +-- .../LogChannelLocation + customRaw.swift | 134 ++++++++++++++++++ .../LogChannelLocation + file.swift | 12 +- ...LogChannelLocation + stdout & stderr.swift | 18 +-- .../LogChannel/LogChannelLocation.swift | 4 +- Sources/SimpleLogging/LogSeverity.swift | 4 +- 8 files changed, 164 insertions(+), 28 deletions(-) create mode 100644 Sources/SimpleLogging/LogChannel/LogChannelLocation + customRaw.swift diff --git a/README.md b/README.md index 2f317ca..5942a9e 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ By default, severities are represented by emoji (like ℹī¸ for `info` and 🆘 By default, this just logs to the same place as Swift's `print` statement. Because enterprise apps have different needs, it can also log to `stdout`, `stderr`, any `FileHandle`, or a custom function. Arbitrarily many of these can operate simultaneously. You can also specify this per-log-call or for all log calls. -**If none is specified, the default channel filter discards messages lower than `info` severity**, since that's the lowest built-in severity which users might care about if they're looking at the logs, but not debugging the code itself. +**If none is specified, the default channel filter discards messages lower than a severity which users might care about** if they're looking at the logs, but not debugging the code itself. ```swift LogManager.defaultChannels += [ diff --git a/Sources/SimpleLogging/LogChannel/LogChannel.swift b/Sources/SimpleLogging/LogChannel/LogChannel.swift index eff9e0a..b7d7d42 100644 --- a/Sources/SimpleLogging/LogChannel/LogChannel.swift +++ b/Sources/SimpleLogging/LogChannel/LogChannel.swift @@ -82,7 +82,7 @@ public struct LogChannel: AnyLogChannel /// - Parameters: /// - name: The human-readable name of this channel /// - location: The location to which this channel sends its log messages - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `defaultFilter`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `defaultFilter` /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default`. public init( name: String, diff --git a/Sources/SimpleLogging/LogChannel/LogChannelLocation + custom.swift b/Sources/SimpleLogging/LogChannel/LogChannelLocation + custom.swift index 5ec0b55..9920553 100644 --- a/Sources/SimpleLogging/LogChannel/LogChannelLocation + custom.swift +++ b/Sources/SimpleLogging/LogChannel/LogChannelLocation + custom.swift @@ -54,15 +54,15 @@ public extension UnreliableLogChannelLocation where Self == CustomLogChannelLoca public extension LogChannel where Location == CustomLogChannelLocation { /// Log to a function, so you can implement some custom logging channel without defining a new `struct`. - /// + /// /// The function is passed the fully-rendered log line, like /// `"2020-11-20 05:26:49.178Z ⚠ī¸ LogToFileTests.swift:144 testLogOnlyCriticalSeveritiesToFile() This message is a warning"` - /// + /// /// - Parameters: - /// - logger: Passed the fully-rendered log line + /// - logger: Passed the fully-rendered log line /// - name: The human-readable name of the channel - /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity level which is allowed in the log. Defaults to `.defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func custom( name: String, lowestAllowedSeverity: LogSeverity, @@ -82,10 +82,10 @@ public extension LogChannel where Location == CustomLogChannelLocation { /// `2020-11-20 05:26:49.178Z ⚠ī¸ LogToFileTests.swift:144 testLogOnlyCriticalSeveritiesToFile() This message is a warning` /// /// - Parameters: - /// - logger: Passed the fully-rendered log line + /// - logger: Passed the fully-rendered log line /// - name: The human-readable name of the channel - /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to `.default` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func custom( name: String, severityFilter: LogSeverityFilter = .default, diff --git a/Sources/SimpleLogging/LogChannel/LogChannelLocation + customRaw.swift b/Sources/SimpleLogging/LogChannel/LogChannelLocation + customRaw.swift new file mode 100644 index 0000000..6870cc6 --- /dev/null +++ b/Sources/SimpleLogging/LogChannel/LogChannelLocation + customRaw.swift @@ -0,0 +1,134 @@ +// +// LogChannelLocation + customRaw.swift +// +// +// Created by Ky Leggiero on 2023-06-08. +// + +import Foundation +import FunctionTools + + + +private let errorMessage_logNonRawMessageToRawLocation = "<>" + + + +/// Log to a function, so you can implement some custom logging location where you assemble it yourself. +/// +/// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. +/// +/// Log channels may choose to pre-filter messages before that function is called. +public struct CustomRawLogChannelLocation: LogChannelLocation { + + /// API users give us this to tell us where/how to log messages + private let logger: Callback + + + /// Create a new custom log channel location + /// + /// The function is passed the raw data about what to log. + /// Log channels may choose to pre-filter messages before that function is called. + /// + /// - Parameter logger: Passed the raw, unrendered log message & metadata + init(loggingTo logger: @escaping Callback) { + self.logger = logger + } + + + public func append(_ message: LogMessageProtocol, options: LoggingOptions) { + if let message = message as? RawLogMessage { + logger(message) + } + else { + assertionFailure( + """ + When logging to a raw location, `message` must be a `RawLogMessage`. + Instead, this was received: \(type(of: message)) + + 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. + Production lines will have the following text inside them: + \(errorMessage_logNonRawMessageToRawLocation) + + The entire rendered log line (including this error and the original severity) will be placed in the message location of the log line. + 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`). + """ + ) + + print( + RawLogMessage( + dateLogged: message.dateLogged, + severity: .error, + codeLocation: CodeLocation(fullFilePath: "error", functionIdentifier: "error", lineNumber: 0xbad_c0de), + message: "\t \(errorMessage_logNonRawMessageToRawLocation)\t \(message.entireRenderedLogLine(options: options))" + ) + .entireRenderedLogLine(options: options)) + } + } +} + + + +public extension UnreliableLogChannelLocation where Self == CustomRawLogChannelLocation { + + /// Log to a function, so you can implement some custom logging location where you assemble it yourself. + /// + /// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. + /// + /// Log channels may choose to pre-filter messages before that function is called. + /// + /// - Parameter logger: Passed the raw, unrendered log message & metadata + static func customRaw(loggingTo logger: @escaping Callback) -> Self { + .init(loggingTo: logger) + } +} + + + +public extension LogChannel where Location == CustomRawLogChannelLocation { + /// Log to a function, so you can implement some custom logging location where you assemble it yourself. + /// + /// The function is passed completely unrendered log message components. See the documentation for `RawLogMessage` for more info. + /// + /// Log channels may choose to pre-filter messages before that function is called. + /// + /// - Parameters: + /// - name: The human-readable name of the channel + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` + /// - logger: Passed the raw, unrendered log message & metadata + func customRaw( + name: String, + lowestAllowedSeverity: LogSeverity, + logSeverityNameStyle: SeverityNameStyle = .default, + logger: @escaping Callback) + -> Self { + .init(name: name, + location: Location(loggingTo: logger), + lowestAllowedSeverity: lowestAllowedSeverity, + logSeverityNameStyle: logSeverityNameStyle) + } + + + /// Log to a function, so you can implement some custom logging channel without defining a new `struct`. + /// + /// The function is passed the fully-rendered log line, like + /// `2020-11-20 05:26:49.178Z ⚠ī¸ LogToFileTests.swift:144 testLogOnlyCriticalSeveritiesToFile() This message is a warning` + /// + /// - Parameters: + /// - name: The human-readable name of the channel + /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to `.default` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` + /// - logger: Passed the raw, unrendered log message & metadata + static func custom( + name: String, + severityFilter: LogSeverityFilter = .default, + logSeverityNameStyle: SeverityNameStyle = .default, + logger: @escaping Callback) + -> Self { + .init(name: name, + location: Location(loggingTo: logger), + severityFilter: severityFilter, + logSeverityNameStyle: logSeverityNameStyle) + } +} diff --git a/Sources/SimpleLogging/LogChannel/LogChannelLocation + file.swift b/Sources/SimpleLogging/LogChannel/LogChannelLocation + file.swift index 34ebd5e..224abe0 100644 --- a/Sources/SimpleLogging/LogChannel/LogChannelLocation + file.swift +++ b/Sources/SimpleLogging/LogChannel/LogChannelLocation + file.swift @@ -131,7 +131,7 @@ public extension LogChannel where Location == FileLogChannelLocation { /// - fileManager: _optional_ - The file manager to use when performing file system operations. Defaults to `.default`. /// - name: _optional_ - The human-readable name of the channel. Pass `nil` to generate one based on the path. Defaults to `nil`. /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` /// /// - Throws: If the parent directory cannot be created, or if you specified that it shouldn't be but it doesn't exist, or if the log file cannot ce created, or if the file couldn't be opened for writing static func file( @@ -161,8 +161,8 @@ public extension LogChannel where Location == FileLogChannelLocation { /// - createIntermediatesIfNecessary: _optional_ - If the parent directory, or any of its ancestor directories, doesn't exist, then passing `true` will cause this to attempt to create it, whereas if you pass `false`, then this will throw an error if they don't exist. Defaults to `true`. /// - fileManager: _optional_ - The file manager to use when performing file system operations. Defaults to `.default`. /// - name: _optional_ - The human-readable name of the channel. Pass `nil` to generate one based on the path. Defaults to `nil`. - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `info`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` /// /// - Throws: If the parent directory cannot be created, or if you specified that it shouldn't be but it doesn't exist, or if the log file cannot ce created, or if the file couldn't be opened for writing static func file( @@ -195,7 +195,7 @@ public extension LogChannel where Location == FileLogChannelLocation { /// - fileManager: _optional_ - The file manager to use when performing file system operations. Defaults to `.default`. /// - name: _optional_ - The human-readable name of the channel. Pass `nil` to generate one based on the path. Defaults to `nil`. /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` /// /// - Throws: If the parent directory cannot be created, or if you specified that it shouldn't be but it doesn't exist, or if the log file cannot ce created, or if the file couldn't be opened for writing static func file( @@ -227,8 +227,8 @@ public extension LogChannel where Location == FileLogChannelLocation { /// - createIntermediatesIfNecessary: _optional_ - If the parent directory, or any of its ancestor directories, doesn't exist, then passing `true` will cause this to attempt to create it, whereas if you pass `false`, then this will throw an error if they don't exist. Defaults to `true`. /// - fileManager: _optional_ - The file manager to use when performing file system operations. Defaults to `.default`. /// - name: _optional_ - The human-readable name of the channel. Pass `nil` to generate one based on the path. Defaults to `nil`. - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `info`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` /// /// - Throws: If the parent directory cannot be created, or if you specified that it shouldn't be but it doesn't exist, or if the log file cannot ce created, or if the file couldn't be opened for writing static func file( diff --git a/Sources/SimpleLogging/LogChannel/LogChannelLocation + stdout & stderr.swift b/Sources/SimpleLogging/LogChannel/LogChannelLocation + stdout & stderr.swift index 2c63053..77eaf48 100644 --- a/Sources/SimpleLogging/LogChannel/LogChannelLocation + stdout & stderr.swift +++ b/Sources/SimpleLogging/LogChannel/LogChannelLocation + stdout & stderr.swift @@ -96,7 +96,7 @@ public extension LogChannel { /// - Parameters: /// - name: _optional_ - The human-readable name of the channel. Defaults to `stdout` /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardOut(name: String = "stdout", severityFilter: LogSeverityFilter = .default, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardOutLogChannelLocation { Self.init(name: name, severityFilter: severityFilter, logSeverityNameStyle: logSeverityNameStyle) @@ -108,7 +108,7 @@ public extension LogChannel { /// - Parameters: /// - name: _optional_ - The human-readable name of the channel. Defaults to `stdout` /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardError(name: String = "stderr", severityFilter: LogSeverityFilter = .default, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardErrorLogChannelLocation { Self.init(name: name, severityFilter: severityFilter, logSeverityNameStyle: logSeverityNameStyle) @@ -120,7 +120,7 @@ public extension LogChannel { /// - Parameters: /// - name: _optional_ - The human-readable name of the channel. Defaults to `stdout` /// - severityFilter: _optional_ - The filter which decides which messages appear in this channel's logs. Defaults to allowing `info` and higher, since `info` is the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardOutAndError(name: String = "stdout & stderr", severityFilter: LogSeverityFilter = .default, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardOutAndErrorLogChannelLocation { Self.init(name: name, severityFilter: severityFilter, logSeverityNameStyle: logSeverityNameStyle) @@ -133,8 +133,8 @@ public extension LogChannel { /// /// - Parameters: /// - name: The human-readable name of this channel - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.default`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardOut(name: String = "stdout", lowestAllowedSeverity: LogSeverity, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardOutLogChannelLocation { Self.init(name: name, lowestAllowedSeverity: lowestAllowedSeverity, logSeverityNameStyle: logSeverityNameStyle) @@ -145,8 +145,8 @@ public extension LogChannel { /// /// - Parameters: /// - name: The human-readable name of this channel - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.default`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardError(name: String = "stderr", lowestAllowedSeverity: LogSeverity, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardErrorLogChannelLocation { Self.init(name: name, lowestAllowedSeverity: lowestAllowedSeverity, logSeverityNameStyle: logSeverityNameStyle) @@ -157,8 +157,8 @@ public extension LogChannel { /// /// - Parameters: /// - name: The human-readable name of this channel - /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.default`, since that's the lowest built-in severity which users might care about if they're looking at logs, but not debugging the code itself. - /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.emoji`, so humans can more easily skim the log. + /// - lowestAllowedSeverity: _optional_ - The lowest severity which will appear in this channel's logs. Defaults to `.defaultFilter` + /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. Defaults to `.default` static func standardOutAndError(name: String = "stdout & stderr", lowestAllowedSeverity: LogSeverity, logSeverityNameStyle: SeverityNameStyle = .default) -> Self where Location == StandardOutAndErrorLogChannelLocation { Self.init(name: name, lowestAllowedSeverity: lowestAllowedSeverity, logSeverityNameStyle: logSeverityNameStyle) diff --git a/Sources/SimpleLogging/LogChannel/LogChannelLocation.swift b/Sources/SimpleLogging/LogChannel/LogChannelLocation.swift index 1b3f85e..4eeb07d 100644 --- a/Sources/SimpleLogging/LogChannel/LogChannelLocation.swift +++ b/Sources/SimpleLogging/LogChannel/LogChannelLocation.swift @@ -69,7 +69,7 @@ public extension LogChannel where Location: SingletonLogChannelLocation { /// which users might care about if they're looking at logs, but not debugging the code /// itself. /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. - /// Defaults to `.emoji`, so humans can more easily skim the log. + /// Defaults to `.default` /// /// - Throws: Any error which occurs while trying to create the channel init(name: String, lowestAllowedSeverity: LogSeverity, logSeverityNameStyle: SeverityNameStyle) { @@ -86,7 +86,7 @@ public extension LogChannel where Location: SingletonLogChannelLocation { /// which users might care about if they're looking at logs, but not debugging the code /// itself. /// - logSeverityNameStyle: _optional_ - The style of the severity names that appear in the log. - /// Defaults to `.emoji`, so humans can more easily skim the log. + /// Defaults to `.default` /// /// - Throws: Any error which occurs while trying to create the channel init(name: String, severityFilter: LogSeverityFilter, logSeverityNameStyle: SeverityNameStyle) { diff --git a/Sources/SimpleLogging/LogSeverity.swift b/Sources/SimpleLogging/LogSeverity.swift index 4241c3a..1f3efbf 100644 --- a/Sources/SimpleLogging/LogSeverity.swift +++ b/Sources/SimpleLogging/LogSeverity.swift @@ -145,7 +145,9 @@ public enum SeverityNameStyle { case emoji - /// The style that's used by default, if none is specified + /// The style that's used by default, if none is specified. + /// + /// Currently set to `.emoji`, so people can more easily skim the log public static var `default`: Self { emoji } }