Skip to content

Commit 3402635

Browse files
committed
add source for log messages
1 parent a9784b0 commit 3402635

File tree

6 files changed

+247
-34
lines changed

6 files changed

+247
-34
lines changed

Sources/Logging/LogHandler.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,22 @@ public protocol LogHandler {
120120
/// - level: The log level the message was logged at.
121121
/// - message: The message to log. To obtain a `String` representation call `message.description`.
122122
/// - metadata: The metadata associated to this log message.
123+
/// - source: The source where the log message originated, for example the logging module.
123124
/// - file: The file the log message was emitted from.
124125
/// - function: The function the log line was emitted from.
125126
/// - line: The line the log message was emitted from.
126127
func log(level: Logger.Level,
127128
message: Logger.Message,
128129
metadata: Logger.Metadata?,
129-
file: String, function: String, line: UInt)
130+
source: String,
131+
file: String,
132+
function: String,
133+
line: UInt)
134+
135+
/// SwiftLog 1.0 compatibility method
136+
@available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)")
137+
func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt)
138+
130139

131140
/// Add, remove, or change the logging metadata.
132141
///
@@ -151,3 +160,27 @@ public protocol LogHandler {
151160
/// `LogHandler`.
152161
var logLevel: Logger.Level { get set }
153162
}
163+
164+
extension LogHandler {
165+
@available(*, deprecated, message: "You should implement this method instead of using the default implementation")
166+
public func log(level: Logger.Level,
167+
message: Logger.Message,
168+
metadata: Logger.Metadata?,
169+
source: String,
170+
file: String,
171+
function: String,
172+
line: UInt) {
173+
self.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line)
174+
}
175+
176+
@available(*, deprecated, renamed: "log(level:message:metadata:source:file:function:line:)")
177+
public func log(level: Logging.Logger.Level, message: Logging.Logger.Message, metadata: Logging.Logger.Metadata?, file: String, function: String, line: UInt) {
178+
self.log(level: level,
179+
message: message,
180+
metadata: metadata,
181+
source: Logger.currentModule(filePath: file),
182+
file: file,
183+
function: function,
184+
line: line)
185+
}
186+
}

Sources/Logging/Logging.swift

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,13 @@ extension Logger {
6060
public func log(level: Logger.Level,
6161
_ message: @autoclosure () -> Logger.Message,
6262
metadata: @autoclosure () -> Logger.Metadata? = nil,
63-
file: String = #file, function: String = #function, line: UInt = #line) {
63+
source: @autoclosure () -> String? = nil,
64+
file: String = (#file), function: String = #function, line: UInt = #line) {
6465
if self.logLevel <= level {
6566
self.handler.log(level: level,
6667
message: message(),
6768
metadata: metadata(),
69+
source: source() ?? Logger.currentModule(filePath: file),
6870
file: file, function: function, line: line)
6971
}
7072
}
@@ -118,8 +120,9 @@ extension Logger {
118120
@inlinable
119121
public func trace(_ message: @autoclosure () -> Logger.Message,
120122
metadata: @autoclosure () -> Logger.Metadata? = nil,
123+
source: @autoclosure () -> String? = nil,
121124
file: String = #file, function: String = #function, line: UInt = #line) {
122-
self.log(level: .trace, message(), metadata: metadata(), file: file, function: function, line: line)
125+
self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
123126
}
124127

125128
/// Log a message passing with the `Logger.Level.debug` log level.
@@ -139,8 +142,9 @@ extension Logger {
139142
@inlinable
140143
public func debug(_ message: @autoclosure () -> Logger.Message,
141144
metadata: @autoclosure () -> Logger.Metadata? = nil,
145+
source: @autoclosure () -> String? = nil,
142146
file: String = #file, function: String = #function, line: UInt = #line) {
143-
self.log(level: .debug, message(), metadata: metadata(), file: file, function: function, line: line)
147+
self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
144148
}
145149

146150
/// Log a message passing with the `Logger.Level.info` log level.
@@ -160,8 +164,9 @@ extension Logger {
160164
@inlinable
161165
public func info(_ message: @autoclosure () -> Logger.Message,
162166
metadata: @autoclosure () -> Logger.Metadata? = nil,
167+
source: @autoclosure () -> String? = nil,
163168
file: String = #file, function: String = #function, line: UInt = #line) {
164-
self.log(level: .info, message(), metadata: metadata(), file: file, function: function, line: line)
169+
self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
165170
}
166171

167172
/// Log a message passing with the `Logger.Level.notice` log level.
@@ -181,8 +186,9 @@ extension Logger {
181186
@inlinable
182187
public func notice(_ message: @autoclosure () -> Logger.Message,
183188
metadata: @autoclosure () -> Logger.Metadata? = nil,
189+
source: @autoclosure () -> String? = nil,
184190
file: String = #file, function: String = #function, line: UInt = #line) {
185-
self.log(level: .notice, message(), metadata: metadata(), file: file, function: function, line: line)
191+
self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
186192
}
187193

188194
/// Log a message passing with the `Logger.Level.warning` log level.
@@ -202,8 +208,9 @@ extension Logger {
202208
@inlinable
203209
public func warning(_ message: @autoclosure () -> Logger.Message,
204210
metadata: @autoclosure () -> Logger.Metadata? = nil,
211+
source: @autoclosure () -> String? = nil,
205212
file: String = #file, function: String = #function, line: UInt = #line) {
206-
self.log(level: .warning, message(), metadata: metadata(), file: file, function: function, line: line)
213+
self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
207214
}
208215

209216
/// Log a message passing with the `Logger.Level.error` log level.
@@ -223,8 +230,9 @@ extension Logger {
223230
@inlinable
224231
public func error(_ message: @autoclosure () -> Logger.Message,
225232
metadata: @autoclosure () -> Logger.Metadata? = nil,
233+
source: @autoclosure () -> String? = nil,
226234
file: String = #file, function: String = #function, line: UInt = #line) {
227-
self.log(level: .error, message(), metadata: metadata(), file: file, function: function, line: line)
235+
self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
228236
}
229237

230238
/// Log a message passing with the `Logger.Level.critical` log level.
@@ -243,8 +251,9 @@ extension Logger {
243251
@inlinable
244252
public func critical(_ message: @autoclosure () -> Logger.Message,
245253
metadata: @autoclosure () -> Logger.Metadata? = nil,
254+
source: @autoclosure () -> String? = nil,
246255
file: String = #file, function: String = #function, line: UInt = #line) {
247-
self.log(level: .critical, message(), metadata: metadata(), file: file, function: function, line: line)
256+
self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line)
248257
}
249258
}
250259

@@ -488,9 +497,18 @@ public struct MultiplexLogHandler: LogHandler {
488497
public func log(level: Logger.Level,
489498
message: Logger.Message,
490499
metadata: Logger.Metadata?,
491-
file: String, function: String, line: UInt) {
500+
source: String,
501+
file: String,
502+
function: String,
503+
line: UInt) {
492504
self.handlers.forEach { handler in
493-
handler.log(level: level, message: message, metadata: metadata, file: file, function: function, line: line)
505+
handler.log(level: level,
506+
message: message,
507+
metadata: metadata,
508+
source: source,
509+
file: file,
510+
function: function,
511+
line: line)
494512
}
495513
}
496514

@@ -618,7 +636,10 @@ public struct StreamLogHandler: LogHandler {
618636
public func log(level: Logger.Level,
619637
message: Logger.Message,
620638
metadata: Logger.Metadata?,
621-
file: String, function: String, line: UInt) {
639+
source: String,
640+
file: String,
641+
function: String,
642+
line: UInt) {
622643
let prettyMetadata = metadata?.isEmpty ?? true
623644
? self.prettyMetadata
624645
: self.prettify(self.metadata.merging(metadata!, uniquingKeysWith: { _, new in new }))
@@ -644,6 +665,20 @@ public struct StreamLogHandler: LogHandler {
644665
}
645666
}
646667

668+
extension Logger {
669+
@inlinable
670+
internal static func currentModule(filePath: String = #filePath) -> String {
671+
let utf8All = filePath.utf8
672+
return filePath.utf8.lastIndex(of: UInt8(ascii: "/")).flatMap { lastSlash -> Substring? in
673+
utf8All[..<lastSlash].lastIndex(of: UInt8(ascii: "/")).map { secondLastSlash -> Substring in
674+
filePath[utf8All.index(after: secondLastSlash) ..< lastSlash]
675+
}
676+
}.map {
677+
String($0)
678+
} ?? "n/a"
679+
}
680+
}
681+
647682
// Extension has to be done on explicit type rather than Logger.Metadata.Value as workaround for
648683
// https://bugs.swift.org/browse/SR-9686
649684
extension Logger.MetadataValue: ExpressibleByStringLiteral {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Logging API open source project
4+
//
5+
// Copyright (c) 2020 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+
15+
import XCTest
16+
/* NOT @testable */ import Logging
17+
18+
final class CompatibilityTest: XCTestCase {
19+
func testAllLogLevelsWorkWithOldSchoolLogHandlerButSourceIsNotPropagated() {
20+
let testLogging = OldSchoolTestLogging()
21+
22+
var logger = LoggerWithSource(Logger(label: "\(#function)",
23+
factory: testLogging.make),
24+
source: "my-fancy-source")
25+
logger.logLevel = .trace
26+
27+
logger.trace("yes: trace")
28+
logger.debug("yes: debug")
29+
logger.info("yes: info")
30+
logger.notice("yes: notice")
31+
logger.warning("yes: warning")
32+
logger.error("yes: error")
33+
logger.critical("yes: critical")
34+
35+
// Please note that the source is _not_ propagated (because the backend doesn't support it).
36+
testLogging.history.assertExist(level: .trace, message: "yes: trace", source: "no source")
37+
testLogging.history.assertExist(level: .debug, message: "yes: debug", source: "no source")
38+
testLogging.history.assertExist(level: .info, message: "yes: info", source: "no source")
39+
testLogging.history.assertExist(level: .notice, message: "yes: notice", source: "no source")
40+
testLogging.history.assertExist(level: .warning, message: "yes: warning", source: "no source")
41+
testLogging.history.assertExist(level: .error, message: "yes: error", source: "no source")
42+
testLogging.history.assertExist(level: .critical, message: "yes: critical", source: "no source")
43+
}
44+
45+
func testAllLogLevelsWorkWithOldSchoolLogHandlerWorks() {
46+
let testLogging = OldSchoolTestLogging()
47+
48+
var logger = Logger(label: "\(#function)", factory: testLogging.make)
49+
logger.logLevel = .trace
50+
51+
logger.trace("yes: trace")
52+
logger.debug("yes: debug")
53+
logger.info("yes: info")
54+
logger.notice("yes: notice")
55+
logger.warning("yes: warning")
56+
logger.error("yes: error")
57+
logger.critical("yes: critical", source: "any also with some new argument that isn't propagated")
58+
59+
// Please note that the source is _not_ propagated (because the backend doesn't support it).
60+
testLogging.history.assertExist(level: .trace, message: "yes: trace", source: "no source")
61+
testLogging.history.assertExist(level: .debug, message: "yes: debug", source: "no source")
62+
testLogging.history.assertExist(level: .info, message: "yes: info", source: "no source")
63+
testLogging.history.assertExist(level: .notice, message: "yes: notice", source: "no source")
64+
testLogging.history.assertExist(level: .warning, message: "yes: warning", source: "no source")
65+
testLogging.history.assertExist(level: .error, message: "yes: error", source: "no source")
66+
testLogging.history.assertExist(level: .critical, message: "yes: critical", source: "no source")
67+
}
68+
}
69+
70+
fileprivate struct OldSchoolTestLogging {
71+
private let _config = Config() // shared among loggers
72+
private let recorder = Recorder() // shared among loggers
73+
74+
func make(label: String) -> LogHandler {
75+
return OldSchoolLogHandler(label: label, config: self.config, recorder: self.recorder)
76+
}
77+
78+
var config: Config { return self._config }
79+
var history: History { return self.recorder }
80+
}
81+
82+
fileprivate struct OldSchoolLogHandler: LogHandler {
83+
var label: String
84+
let config: Config
85+
let recorder: Recorder
86+
87+
func make(label: String) -> LogHandler {
88+
return TestLogHandler(label: label, config: self.config, recorder: self.recorder)
89+
}
90+
91+
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {
92+
self.recorder.record(level: level, metadata: metadata, message: message, source: "no source")
93+
}
94+
95+
subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
96+
get {
97+
return self.metadata[metadataKey]
98+
}
99+
set {
100+
self.metadata[metadataKey] = newValue
101+
}
102+
}
103+
104+
var metadata: Logger.Metadata = [:]
105+
106+
var logLevel: Logger.Level = .info
107+
}

Tests/LoggingTests/LocalLoggingTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class LocalLoggerTest: XCTestCase {
2626
let context = Context()
2727
Struct1().doSomething(context: context)
2828
// test results
29-
logging.history.assertExist(level: .debug, message: "Struct1::doSomething")
29+
logging.history.assertExist(level: .debug, message: "Struct1::doSomething", source: "LoggingTests")
3030
logging.history.assertNotExist(level: .debug, message: "Struct1::doSomethingElse")
3131
logging.history.assertExist(level: .info, message: "Struct2::doSomething")
3232
logging.history.assertExist(level: .info, message: "Struct2::doSomethingElse")

Tests/LoggingTests/LoggingTest.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class LoggingTest: XCTestCase {
152152
"lazy": .stringConvertible(LazyMetadataBox { "rendered-at-first-use" })])
153153
}
154154

155-
private func dontEvaluateThisString(file: StaticString = #file, line: UInt = #line) -> Logger.Message {
155+
private func dontEvaluateThisString(file: StaticString = (#file), line: UInt = #line) -> Logger.Message {
156156
XCTFail("should not have been evaluated", file: file, line: line)
157157
return "should not have been evaluated"
158158
}
@@ -188,7 +188,7 @@ class LoggingTest: XCTestCase {
188188

189189
func testCustomFactory() {
190190
struct CustomHandler: LogHandler {
191-
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {}
191+
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, source: String, file: String, function: String, line: UInt) {}
192192

193193
subscript(metadataKey _: String) -> Logger.Metadata.Value? {
194194
get { return nil }
@@ -344,8 +344,8 @@ class LoggingTest: XCTestCase {
344344
}
345345

346346
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?,
347-
file: String, function: String, line: UInt) {
348-
self.recorder.record(level: level, metadata: metadata, message: message)
347+
source: String, file: String, function: String, line: UInt) {
348+
self.recorder.record(level: level, metadata: metadata, message: message, source: source)
349349
}
350350

351351
subscript(metadataKey metadataKey: String) -> Logger.Metadata.Value? {
@@ -567,6 +567,30 @@ class LoggingTest: XCTestCase {
567567

568568
logging.history.assertExist(level: .error, message: "errorDescription")
569569
}
570+
571+
func testAllLogLevelsWorkOnLoggerWithSource() {
572+
let testLogging = TestLogging()
573+
LoggingSystem.bootstrapInternal(testLogging.make)
574+
575+
var logger = LoggerWithSource(Logger(label: "\(#function)"), source: "my-fancy-source")
576+
logger.logLevel = .trace
577+
578+
logger.trace("yes: trace")
579+
logger.debug("yes: debug")
580+
logger.info("yes: info")
581+
logger.notice("yes: notice")
582+
logger.warning("yes: warning")
583+
logger.error("yes: error")
584+
logger.critical("yes: critical")
585+
586+
testLogging.history.assertExist(level: .trace, message: "yes: trace", source: "my-fancy-source")
587+
testLogging.history.assertExist(level: .debug, message: "yes: debug", source: "my-fancy-source")
588+
testLogging.history.assertExist(level: .info, message: "yes: info", source: "my-fancy-source")
589+
testLogging.history.assertExist(level: .notice, message: "yes: notice", source: "my-fancy-source")
590+
testLogging.history.assertExist(level: .warning, message: "yes: warning", source: "my-fancy-source")
591+
testLogging.history.assertExist(level: .error, message: "yes: error", source: "my-fancy-source")
592+
testLogging.history.assertExist(level: .critical, message: "yes: critical", source: "my-fancy-source")
593+
}
570594
}
571595

572596
extension Logger {

0 commit comments

Comments
 (0)