forked from apple/swift-log
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInMemoryLogHandler.swift
More file actions
181 lines (162 loc) · 5.44 KB
/
InMemoryLogHandler.swift
File metadata and controls
181 lines (162 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Logging API open source project
//
// Copyright (c) 2018-2022 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
//
//===----------------------------------------------------------------------===//
public import Logging
/// A custom log handler which just collects logs into memory.
/// You can then retrieve an array of those log entries.
/// Example use cases include testing and buffering.
///
/// # Usage
/// ```swift
/// let logHandler = InMemoryLogHandler()
/// let logger = Logger(
/// label: "MyApp",
/// factory: { _ in
/// logHandler
/// }
/// )
/// // Use logger to emit some logs
/// someFunction(logger: logger)
///
/// // Retrieve what was logged
/// let logEntries = logger.entries
/// ```
///
public struct InMemoryLogHandler: LogHandler {
public var metadata: Logger.Metadata = [:]
public var metadataProvider: Logger.MetadataProvider?
public var logLevel: Logger.Level = .info
private let logStore: LogStore
/// A struct representing a log entry.
public struct Entry: Sendable, Equatable {
/// The level we logged at.
public var level: Logger.Level
/// The message which was logged.
public var message: Logger.Message
/// The error which was logged.
public var error: (any Error)?
/// The metadata which was logged.
public var metadata: Logger.Metadata
public init(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata) {
self.level = level
self.message = message
self.metadata = metadata
}
public init(level: Logger.Level, message: Logger.Message, error: (any Error)?, metadata: Logger.Metadata) {
self.level = level
self.message = message
self.error = error
self.metadata = metadata
}
public static func == (lhs: InMemoryLogHandler.Entry, rhs: InMemoryLogHandler.Entry) -> Bool {
lhs.level == rhs.level
&& lhs.message == rhs.message
&& errorsEqual(lhs.error, rhs.error)
&& lhs.metadata == rhs.metadata
}
private static func errorsEqual(_ lhs: (any Error)?, _ rhs: (any Error)?) -> Bool {
switch (lhs, rhs) {
case (nil, nil):
return true
case let (l?, r?):
return "\(l)" == "\(r)" && String(reflecting: type(of: l)) == String(reflecting: type(of: r))
default:
return false
}
}
}
private final class LogStore: @unchecked Sendable {
private var _entries: [Entry] = []
private let lock = Lock()
fileprivate func append(
level: Logger.Level,
message: Logger.Message,
error: (any Error)?,
metadata: Logger.Metadata
) {
self.lock.withLockVoid {
self._entries.append(
Entry(
level: level,
message: message,
error: error,
metadata: metadata
)
)
}
}
fileprivate func clear() {
self.lock.withLockVoid {
_entries.removeAll()
}
}
var entries: [Entry] {
self.lock.withLock { self._entries }
}
}
private init(logStore: LogStore) {
self.logStore = logStore
}
/// Create a new ``InMemoryLogHandler``.
public init() {
self.init(logStore: .init())
}
public func log(event: LogEvent) {
// Start with the metadata provider..
var mergedMetadata: Logger.Metadata = self.metadataProvider?.get() ?? [:]
// ..merge in self.metadata, overwriting existing keys
mergedMetadata = mergedMetadata.merging(self.metadata) { $1 }
// ..merge in metadata from this log call, overwriting existing keys
mergedMetadata = mergedMetadata.merging(event.metadata ?? [:]) { $1 }
// ..merge in error as metadata, overwriting existing keys
self.logStore.append(level: event.level, message: event.message, error: event.error, metadata: mergedMetadata)
}
@available(*, deprecated, renamed: "log(event:)")
public func log(
level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt
) {
self.log(
event: LogEvent(
level: level,
message: message,
metadata: metadata,
source: source,
file: file,
function: function,
line: line
)
)
}
public subscript(metadataKey key: String) -> Logger.Metadata.Value? {
get {
self.metadata[key]
}
set {
self.metadata[key] = newValue
}
}
/// All logs that have been collected.
public var entries: [Entry] {
self.logStore.entries
}
/// Clear all entries.
public func clear() {
self.logStore.clear()
}
}