Skip to content

Commit

Permalink
add source for log messages
Browse files Browse the repository at this point in the history
  • Loading branch information
weissi committed Jun 22, 2020
1 parent afc50b2 commit 65d928b
Show file tree
Hide file tree
Showing 12 changed files with 616 additions and 35 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ However, in special cases, it is acceptable that a `LogHandler` provides some gl

`LogHandler`s do not control if a message should be logged or not. `Logger` will only invoke the `log` function of a `LogHandler` if `Logger` determines that a log message should be emitted given the configured log level.

## Source vs Label

A `Logger` carries an (immutable) `label` and each log message carries a `source` parameter (since SwiftLog 1.3.0). The `Logger`'s label
identifies the creator of the `Logger`. If you are using structured logging by preserving metadata across multiple modules, the `Logger`'s
`label` is not a good way to identify where a log message originated from as it identifies the creator of a `Logger` which is often passed
around between libraries to preserve metadata and the like.

If you want to filter all log messages originating from a certain subsystem, filter by `source` which defaults to the module that is emitting the
log message.

## SwiftLog for Swift 4
<a name="help-i-need-swift-4"></a>

Expand Down
37 changes: 35 additions & 2 deletions Sources/Logging/LogHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
/// }
///
/// public func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?,
/// file: String, function: String, line: UInt) {
/// source: String, file: String, function: String, line: UInt) {
/// // [...]
/// }
///
Expand Down Expand Up @@ -120,13 +120,22 @@ public protocol LogHandler {
/// - level: The log level the message was logged at.
/// - message: The message to log. To obtain a `String` representation call `message.description`.
/// - metadata: The metadata associated to this log message.
/// - source: The source where the log message originated, for example the logging module.
/// - file: The file the log message was emitted from.
/// - function: The function the log line was emitted from.
/// - line: The line the log message was emitted from.
func log(level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
file: String, function: String, line: UInt)
source: String,
file: String,
function: String,
line: UInt)

/// SwiftLog 1.0 compatibility method. Please do _not_ implement, implement
/// `log(level:message:metadata:source:file:function:line:)` instead.
@available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)")
func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt)

/// Add, remove, or change the logging metadata.
///
Expand All @@ -151,3 +160,27 @@ public protocol LogHandler {
/// `LogHandler`.
var logLevel: Logger.Level { get set }
}

extension LogHandler {
@available(*, deprecated, message: "You should implement this method instead of using the default implementation")
public func log(level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt) {
self.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line)
}

@available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)")
public func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) {
self.log(level: level,
message: message,
metadata: metadata,
source: Logger.currentModule(filePath: file),
file: file,
function: function,
line: line)
}
}
293 changes: 293 additions & 0 deletions Sources/Logging/LoggerWithSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Logging API open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift Logging API project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Logging API project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// `LoggerWithSource` shares the same API as `Logger`, except that it automatically parses on the supplies `source`
/// instead of requiring the user to supply source when logging a message.
///
/// - info: Do not accept or pass `LoggerWithSource` to/from other modules. The type you use publicly should always be
/// `Logger`.
public struct LoggerWithSource {
/// The `Logger` we are logging with.
public var logger: Logger

/// The source information we are supplying to `Logger`.
public var source: String

/// Construct a `LoggerWithSource` logging with `logger` and `source`.
@inlinable
public init(_ logger: Logger, source: String) {
self.logger = logger
self.source = source
}
}

extension LoggerWithSource {
/// Log a message passing the log level as a parameter.
///
/// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen. The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// - parameters:
/// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`.
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func log(level: Logger.Level,
_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.log(level: level,
message(),
metadata: metadata(),
source: self.source,
file: file, function: function, line: line)
}

/// Add, change, or remove a logging metadata item.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the
/// very `Logger` it was changed on.
@inlinable
public subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
get {
return self.logger[metadataKey: metadataKey]
}
set {
self.logger[metadataKey: metadataKey] = newValue
}
}

/// Get or set the log level configured for this `Logger`.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// - note: `Logger`s treat `logLevel` as a value. This means that a change in `logLevel` will only affect this
/// very `Logger`. It it acceptable for logging backends to have some form of global log level override
/// that affects multiple or even all loggers. This means a change in `logLevel` to one `Logger` might in
/// certain cases have no effect.
@inlinable
public var logLevel: Logger.Level {
get {
return self.logger.logLevel
}
set {
self.logger.logLevel = newValue
}
}
}

extension LoggerWithSource {
/// Log a message passing with the `Logger.Level.trace` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func trace(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.trace(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.debug` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func debug(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.debug(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.info` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func info(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.info(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.notice` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func notice(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.notice(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.warning` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func warning(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.warning(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.error` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged,
/// otherwise nothing will happen.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func error(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.error(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}

/// Log a message passing with the `Logger.Level.critical` log level.
///
/// The `source` is the one supplied to the initializer of `LoggerWithSource`.
///
/// `.critical` messages will always be logged.
///
/// - parameters:
/// - message: The message to be logged. `message` can be used with any string interpolation literal.
/// - metadata: One-off metadata to attach to this log message
/// - file: The file this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#file`).
/// - function: The function this log message originates from (there's usually no need to pass it explicitly as
/// it defaults to `#function`).
/// - line: The line this log message originates from (there's usually no need to pass it explicitly as it
/// defaults to `#line`).
@inlinable
public func critical(_ message: @autoclosure () -> Logger.Message,
metadata: @autoclosure () -> Logger.Metadata? = nil,
file: String = #file, function: String = #function, line: UInt = #line) {
self.logger.critical(message(),
metadata: metadata(),
source: self.source,
file: file,
function: function,
line: line)
}
}
Loading

0 comments on commit 65d928b

Please sign in to comment.