@logbrew/react-native 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LogBrewReactNative.podspec +25 -3
- package/README.md +106 -3
- package/apple-native-diagnostics.d.ts +52 -0
- package/apple-native-diagnostics.js +286 -0
- package/expo.cjs +46 -0
- package/expo.d.ts +16 -0
- package/expo.js +5 -0
- package/fatal-replay.cjs +12 -1
- package/global-errors.cjs +3 -1
- package/index.cjs +84 -2
- package/index.d.cts +4 -0
- package/index.d.ts +4 -0
- package/index.js +84 -2
- package/index.native.d.ts +4 -0
- package/index.native.js +14 -1
- package/ios/AppleDiagnostics/LBRNAppleDiagnosticsModule.h +4 -0
- package/ios/AppleDiagnostics/LBRNAppleDiagnosticsModule.mm +56 -0
- package/ios/AppleDiagnostics/LBRNAppleNativeDiagnostics.swift +293 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/AutomaticTelemetryContext.swift +72 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DeliveryEngine.swift +270 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DeliveryEngineAutomatic.swift +331 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DeliveryEngineDurable.swift +148 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DeliveryEngineQueue.swift +343 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DeliveryLifecycle.swift +70 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DurableDeliveryStore.swift +258 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/DurableDeliveryStoreRecovery.swift +330 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/EventEncoding.swift +114 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/IssueDiagnostics.swift +385 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/LifecycleTrace.swift +138 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/LogBrewClient.swift +358 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/LogBrewLogger.swift +262 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/LogBrewTrace.swift +357 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/Metadata.swift +86 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/NativeStackFrame.swift +27 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/ProductTimeline.swift +228 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/PublicTypes.swift +334 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/TelemetryContext.swift +197 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/TelemetryContextValidation.swift +337 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/TraceEvidence.swift +79 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/Transport.swift +204 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/URLSessionTrace.swift +277 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/URLSessionTracer.swift +151 -0
- package/ios/GeneratedAppleDiagnostics/LogBrew/Validation.swift +165 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/CrashEngine.swift +125 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/CrashReportSanitizer.swift +139 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/CrashStorageDirectory.swift +124 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeArtifactIdentity.swift +137 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeCrashCapture.swift +229 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeCrashPublic.swift +386 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeCrashReplay.swift +317 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeHangIncidentStore.swift +321 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeHangWatchdog.swift +386 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeHangWatchdogContracts.swift +84 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeHangWatchdogSystem.swift +139 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeMainThreadStackCapture.swift +84 -0
- package/ios/GeneratedAppleDiagnostics/LogBrewCrash/NativeStackFrameSanitizer.swift +170 -0
- package/ios/GeneratedAppleDiagnostics/SOURCE-MANIFEST.json +43 -0
- package/package.json +43 -5
- package/promise-rejections.cjs +3 -1
- package/scripts/sync-apple-native-sources.mjs +93 -0
- package/src/NativeLogBrewAppleDiagnostics.ts +12 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
// Generated by scripts/sync-apple-native-sources.mjs.
|
|
2
|
+
// Edit the canonical Swift source, then regenerate this package boundary.
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
public final class LogBrewClient: @unchecked Sendable {
|
|
6
|
+
private let engine: DeliveryEngine
|
|
7
|
+
private let baseContext: TelemetryContext?
|
|
8
|
+
private let breadcrumbStore = IssueBreadcrumbStore()
|
|
9
|
+
|
|
10
|
+
public static func create(
|
|
11
|
+
apiKey: String,
|
|
12
|
+
sdkName: String,
|
|
13
|
+
sdkVersion: String,
|
|
14
|
+
maxRetries: Int = 2,
|
|
15
|
+
context: TelemetryContext? = nil,
|
|
16
|
+
includeAutomaticContext: Bool = true,
|
|
17
|
+
) throws -> LogBrewClient {
|
|
18
|
+
try LogBrewClient(
|
|
19
|
+
config: ClientConfig(
|
|
20
|
+
apiKey: apiKey,
|
|
21
|
+
sdkName: sdkName,
|
|
22
|
+
sdkVersion: sdkVersion,
|
|
23
|
+
maxRetries: maxRetries,
|
|
24
|
+
context: context,
|
|
25
|
+
includeAutomaticContext: includeAutomaticContext,
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public init(config: ClientConfig) throws {
|
|
31
|
+
try requireNonEmpty("api_key", config.apiKey)
|
|
32
|
+
try requireNonEmpty("sdk_name", config.sdkName)
|
|
33
|
+
try requireNonEmpty("sdk_version", config.sdkVersion)
|
|
34
|
+
baseContext = try mergeTelemetryContexts(
|
|
35
|
+
config.includeAutomaticContext ? automaticTelemetryContext() : nil,
|
|
36
|
+
config.context,
|
|
37
|
+
)
|
|
38
|
+
engine = DeliveryEngine(
|
|
39
|
+
apiKey: config.apiKey,
|
|
40
|
+
sdk: SDKInfo(name: config.sdkName, language: "swift", version: config.sdkVersion),
|
|
41
|
+
maxRetries: config.maxRetries,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public func pendingEvents() -> Int {
|
|
46
|
+
engine.pendingEvents()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public func previewJSON() throws -> String {
|
|
50
|
+
try engine.previewJSON()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public func release(_ id: String, timestamp: String, attributes: ReleaseAttributes) throws {
|
|
54
|
+
let context = try resolvedContext(event: attributes.context)
|
|
55
|
+
try pushEvent(.release(validateRelease(attributes.withContext(context))), id: id, timestamp: timestamp)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public func environment(_ id: String, timestamp: String, attributes: EnvironmentAttributes) throws {
|
|
59
|
+
let context = try resolvedContext(event: attributes.context)
|
|
60
|
+
try pushEvent(.environment(validateEnvironment(attributes.withContext(context))), id: id, timestamp: timestamp)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public func issue(_ id: String, timestamp: String, attributes: IssueAttributes) throws {
|
|
64
|
+
let context = try resolvedContext(event: attributes.context)
|
|
65
|
+
let attributes = issueAttributes(attributes, adding: breadcrumbStore.snapshot())
|
|
66
|
+
.withContext(context)
|
|
67
|
+
.withCorrelationMetadata()
|
|
68
|
+
try pushEvent(.issue(validateIssue(attributes)), id: id, timestamp: timestamp)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@_spi(CrashReplay)
|
|
72
|
+
public func issueDetached(_ id: String, timestamp: String, attributes: IssueAttributes) throws {
|
|
73
|
+
try pushEvent(.issue(validateIssue(attributes)), id: id, timestamp: timestamp)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public func log(_ id: String, timestamp: String, attributes: LogAttributes) throws {
|
|
77
|
+
let context = try resolvedContext(event: attributes.context)
|
|
78
|
+
try pushEvent(
|
|
79
|
+
.log(validateLog(attributes.withContext(context).withCorrelationMetadata())),
|
|
80
|
+
id: id,
|
|
81
|
+
timestamp: timestamp,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public func span(_ id: String, timestamp: String, attributes: SpanAttributes) throws {
|
|
86
|
+
let inherited = try resolvedContext(event: attributes.context)
|
|
87
|
+
let sampled = inherited?.trace.flatMap { trace in
|
|
88
|
+
trace.traceId.caseInsensitiveCompare(attributes.traceId) == .orderedSame ? trace.sampled : nil
|
|
89
|
+
}
|
|
90
|
+
let spanTrace = telemetryTraceContext(
|
|
91
|
+
traceId: attributes.traceId,
|
|
92
|
+
spanId: attributes.spanId,
|
|
93
|
+
parentSpanId: attributes.parentSpanId,
|
|
94
|
+
sampled: sampled,
|
|
95
|
+
)
|
|
96
|
+
let context = try mergeTelemetryContexts(
|
|
97
|
+
contextWithoutTrace(inherited),
|
|
98
|
+
spanTrace.map { TelemetryContext(trace: $0) },
|
|
99
|
+
)
|
|
100
|
+
try pushEvent(.span(validateSpan(attributes.withContext(context))), id: id, timestamp: timestamp)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public func action(_ id: String, timestamp: String, attributes: ActionAttributes) throws {
|
|
104
|
+
let context = try resolvedContext(event: attributes.context)
|
|
105
|
+
try pushEvent(
|
|
106
|
+
.action(validateAction(attributes.withContext(context).withCorrelationMetadata())),
|
|
107
|
+
id: id,
|
|
108
|
+
timestamp: timestamp,
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public func metric(_ id: String, timestamp: String, attributes: MetricAttributes) throws {
|
|
113
|
+
let context = try resolvedContext(event: attributes.context)
|
|
114
|
+
try pushEvent(
|
|
115
|
+
.metric(validateMetric(attributes.withContext(context).withCorrelationMetadata())),
|
|
116
|
+
id: id,
|
|
117
|
+
timestamp: timestamp,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Retain one validated breadcrumb for subsequent issues, evicting the oldest after 64 entries.
|
|
122
|
+
public func addBreadcrumb(_ breadcrumb: IssueBreadcrumb) throws {
|
|
123
|
+
try breadcrumbStore.add(breadcrumb)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Clear the client-owned breadcrumb history without changing already queued issues.
|
|
127
|
+
public func clearBreadcrumbs() {
|
|
128
|
+
breadcrumbStore.clear()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public func flush(transport: any Transport) throws -> TransportResponse {
|
|
132
|
+
try engine.flush(transport: transport)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@_spi(CrashReplay)
|
|
136
|
+
public func flushEvent(
|
|
137
|
+
_ eventID: String,
|
|
138
|
+
transport: any Transport,
|
|
139
|
+
) throws -> Bool {
|
|
140
|
+
try requireNonEmpty("event id", eventID)
|
|
141
|
+
return try engine.flushEvent(eventID, transport: transport)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public func shutdown(transport: any Transport) throws -> TransportResponse {
|
|
145
|
+
try engine.shutdown(transport: transport)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public func startAutomaticDelivery(
|
|
149
|
+
transport: any Transport,
|
|
150
|
+
options: AutomaticDeliveryOptions = AutomaticDeliveryOptions(),
|
|
151
|
+
) throws {
|
|
152
|
+
try engine.startAutomaticDelivery(transport: transport, options: options)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public func recoverAutomaticDelivery() throws {
|
|
156
|
+
try engine.recoverAutomaticDelivery()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public func stopAutomaticDelivery() {
|
|
160
|
+
engine.stopAutomaticDelivery()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public func deliveryHealth() -> DeliveryHealth {
|
|
164
|
+
engine.health()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public func enableDurableDelivery(options: DurableDeliveryOptions) throws {
|
|
168
|
+
try engine.enableDurableDelivery(options: options)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public func purgeDurableDelivery() throws {
|
|
172
|
+
try engine.purgeDurableDelivery()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public func flush() throws -> TransportResponse {
|
|
176
|
+
try engine.flushOwnedTransport()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public func shutdown() throws -> TransportResponse {
|
|
180
|
+
try engine.shutdownOwnedTransport()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private func pushEvent(_ attributes: EventAttributes, id: String, timestamp: String) throws {
|
|
184
|
+
try requireNonEmpty("event id", id)
|
|
185
|
+
try requireTimestamp(timestamp)
|
|
186
|
+
try engine.enqueue(Event(type: attributes.eventType, timestamp: timestamp, id: id, attributes: attributes))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private func resolvedContext(event: TelemetryContext?) throws -> TelemetryContext? {
|
|
190
|
+
var context = try mergeTelemetryContexts(baseContext, LogBrewTelemetry.current)
|
|
191
|
+
if let activeTrace = LogBrewTrace.current {
|
|
192
|
+
context = try mergeTelemetryContexts(
|
|
193
|
+
context,
|
|
194
|
+
TelemetryContext(trace: telemetryTraceContext(activeTrace)),
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
return try mergeTelemetryContexts(context, event)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private extension IssueAttributes {
|
|
202
|
+
func withContext(_ context: TelemetryContext?) -> IssueAttributes {
|
|
203
|
+
IssueAttributes(
|
|
204
|
+
title: title,
|
|
205
|
+
level: level,
|
|
206
|
+
message: message,
|
|
207
|
+
exception: exception,
|
|
208
|
+
stackFrames: stackFrames,
|
|
209
|
+
breadcrumbs: breadcrumbs,
|
|
210
|
+
breadcrumbsTruncated: breadcrumbsTruncated,
|
|
211
|
+
metadata: metadata,
|
|
212
|
+
context: context,
|
|
213
|
+
nativeStackFrames: nativeStackFrames,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
func withCorrelationMetadata() -> IssueAttributes {
|
|
218
|
+
IssueAttributes(
|
|
219
|
+
title: title,
|
|
220
|
+
level: level,
|
|
221
|
+
message: message,
|
|
222
|
+
exception: exception,
|
|
223
|
+
stackFrames: stackFrames,
|
|
224
|
+
breadcrumbs: breadcrumbs,
|
|
225
|
+
breadcrumbsTruncated: breadcrumbsTruncated,
|
|
226
|
+
metadata: correlationMetadata(metadata, context: context),
|
|
227
|
+
context: context,
|
|
228
|
+
nativeStackFrames: nativeStackFrames,
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private extension LogAttributes {
|
|
234
|
+
func withContext(_ context: TelemetryContext?) -> LogAttributes {
|
|
235
|
+
LogAttributes(
|
|
236
|
+
message: message,
|
|
237
|
+
level: level,
|
|
238
|
+
logger: logger,
|
|
239
|
+
metadata: metadata,
|
|
240
|
+
context: context,
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
func withCorrelationMetadata() -> LogAttributes {
|
|
245
|
+
LogAttributes(
|
|
246
|
+
message: message,
|
|
247
|
+
level: level,
|
|
248
|
+
logger: logger,
|
|
249
|
+
metadata: correlationMetadata(metadata, context: context),
|
|
250
|
+
context: context,
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private extension ActionAttributes {
|
|
256
|
+
func withContext(_ context: TelemetryContext?) -> ActionAttributes {
|
|
257
|
+
ActionAttributes(name: name, status: status, metadata: metadata, context: context)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
func withCorrelationMetadata() -> ActionAttributes {
|
|
261
|
+
ActionAttributes(
|
|
262
|
+
name: name,
|
|
263
|
+
status: status,
|
|
264
|
+
metadata: correlationMetadata(metadata, context: context),
|
|
265
|
+
context: context,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private extension MetricAttributes {
|
|
271
|
+
func withContext(_ context: TelemetryContext?) -> MetricAttributes {
|
|
272
|
+
MetricAttributes(
|
|
273
|
+
name: name,
|
|
274
|
+
kind: kind,
|
|
275
|
+
value: value,
|
|
276
|
+
unit: unit,
|
|
277
|
+
temporality: temporality,
|
|
278
|
+
metadata: metadata,
|
|
279
|
+
context: context,
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
func withCorrelationMetadata() -> MetricAttributes {
|
|
284
|
+
MetricAttributes(
|
|
285
|
+
name: name,
|
|
286
|
+
kind: kind,
|
|
287
|
+
value: value,
|
|
288
|
+
unit: unit,
|
|
289
|
+
temporality: temporality,
|
|
290
|
+
metadata: correlationMetadata(metadata, context: context),
|
|
291
|
+
context: context,
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private extension ReleaseAttributes {
|
|
297
|
+
func withContext(_ context: TelemetryContext?) -> ReleaseAttributes {
|
|
298
|
+
ReleaseAttributes(version: version, commit: commit, notes: notes, metadata: metadata, context: context)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private extension EnvironmentAttributes {
|
|
303
|
+
func withContext(_ context: TelemetryContext?) -> EnvironmentAttributes {
|
|
304
|
+
EnvironmentAttributes(name: name, region: region, metadata: metadata, context: context)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private extension SpanAttributes {
|
|
309
|
+
func withContext(_ context: TelemetryContext?) -> SpanAttributes {
|
|
310
|
+
SpanAttributes(
|
|
311
|
+
name: name,
|
|
312
|
+
traceId: traceId,
|
|
313
|
+
spanId: spanId,
|
|
314
|
+
parentSpanId: parentSpanId,
|
|
315
|
+
status: status,
|
|
316
|
+
durationMs: durationMs,
|
|
317
|
+
events: events,
|
|
318
|
+
links: links,
|
|
319
|
+
metadata: metadata,
|
|
320
|
+
context: context,
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private func correlationMetadata(_ metadata: Metadata?, context: TelemetryContext?) -> Metadata? {
|
|
326
|
+
guard let trace = context?.trace else {
|
|
327
|
+
return metadata
|
|
328
|
+
}
|
|
329
|
+
var output = metadata ?? [:]
|
|
330
|
+
output["traceId"] = .string(trace.traceId)
|
|
331
|
+
if let spanId = trace.spanId {
|
|
332
|
+
output["spanId"] = .string(spanId)
|
|
333
|
+
}
|
|
334
|
+
if let parentSpanId = trace.parentSpanId {
|
|
335
|
+
output["parentSpanId"] = .string(parentSpanId)
|
|
336
|
+
}
|
|
337
|
+
if let sampled = trace.sampled {
|
|
338
|
+
output["traceSampled"] = .bool(sampled)
|
|
339
|
+
}
|
|
340
|
+
if let active = LogBrewTrace.current, active.traceId == trace.traceId, active.spanId == trace.spanId {
|
|
341
|
+
output["traceFlags"] = .string(active.traceFlags)
|
|
342
|
+
}
|
|
343
|
+
return output
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private func contextWithoutTrace(_ context: TelemetryContext?) -> TelemetryContext? {
|
|
347
|
+
guard let context,
|
|
348
|
+
context.resource != nil || context.session != nil || context.subject != nil || context.tags != nil
|
|
349
|
+
else {
|
|
350
|
+
return nil
|
|
351
|
+
}
|
|
352
|
+
return TelemetryContext(
|
|
353
|
+
resource: context.resource,
|
|
354
|
+
session: context.session,
|
|
355
|
+
subject: context.subject,
|
|
356
|
+
tags: context.tags,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Generated by scripts/sync-apple-native-sources.mjs.
|
|
2
|
+
// Edit the canonical Swift source, then regenerate this package boundary.
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
public enum LogBrewLoggerLevel: String, Sendable {
|
|
6
|
+
case trace
|
|
7
|
+
case debug
|
|
8
|
+
case info
|
|
9
|
+
case notice
|
|
10
|
+
case warning
|
|
11
|
+
case error
|
|
12
|
+
case fault
|
|
13
|
+
case critical
|
|
14
|
+
|
|
15
|
+
var logBrewLevel: LogLevel {
|
|
16
|
+
switch self {
|
|
17
|
+
case .trace, .debug:
|
|
18
|
+
.info
|
|
19
|
+
case .info, .notice:
|
|
20
|
+
.info
|
|
21
|
+
case .warning:
|
|
22
|
+
.warning
|
|
23
|
+
case .error, .fault:
|
|
24
|
+
.error
|
|
25
|
+
case .critical:
|
|
26
|
+
.critical
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public final class LogBrewLogger {
|
|
32
|
+
private let client: LogBrewClient
|
|
33
|
+
private let loggerName: String
|
|
34
|
+
private let subsystem: String?
|
|
35
|
+
private let category: String?
|
|
36
|
+
private let eventIDPrefix: String
|
|
37
|
+
private let baseMetadata: Metadata
|
|
38
|
+
private let transport: (any Transport)?
|
|
39
|
+
private let flushOnLog: Bool
|
|
40
|
+
private let timestampProvider: () -> String
|
|
41
|
+
private let onError: ((any Error) -> Void)?
|
|
42
|
+
private let lock = NSLock()
|
|
43
|
+
private var nextEventNumber = 0
|
|
44
|
+
|
|
45
|
+
public init(
|
|
46
|
+
client: LogBrewClient,
|
|
47
|
+
loggerName: String? = nil,
|
|
48
|
+
subsystem: String? = nil,
|
|
49
|
+
category: String? = nil,
|
|
50
|
+
eventIDPrefix: String = "swift_log",
|
|
51
|
+
metadata: Metadata? = nil,
|
|
52
|
+
transport: (any Transport)? = nil,
|
|
53
|
+
flushOnLog: Bool = false,
|
|
54
|
+
timestampProvider: (() -> String)? = nil,
|
|
55
|
+
onError: ((any Error) -> Void)? = nil,
|
|
56
|
+
) throws {
|
|
57
|
+
if let loggerName {
|
|
58
|
+
try requireNonEmpty("logger name", loggerName)
|
|
59
|
+
}
|
|
60
|
+
if let subsystem {
|
|
61
|
+
try requireNonEmpty("logger subsystem", subsystem)
|
|
62
|
+
}
|
|
63
|
+
if let category {
|
|
64
|
+
try requireNonEmpty("logger category", category)
|
|
65
|
+
}
|
|
66
|
+
try requireNonEmpty("event id prefix", eventIDPrefix)
|
|
67
|
+
|
|
68
|
+
self.client = client
|
|
69
|
+
self.loggerName = loggerName ?? category ?? subsystem ?? "swift-logger"
|
|
70
|
+
self.subsystem = subsystem
|
|
71
|
+
self.category = category
|
|
72
|
+
self.eventIDPrefix = eventIDPrefix
|
|
73
|
+
baseMetadata = metadata ?? [:]
|
|
74
|
+
self.transport = transport
|
|
75
|
+
self.flushOnLog = flushOnLog
|
|
76
|
+
self.timestampProvider = timestampProvider ?? Self.defaultTimestamp
|
|
77
|
+
self.onError = onError
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public func log(
|
|
81
|
+
_ level: LogBrewLoggerLevel,
|
|
82
|
+
_ message: @autoclosure () -> String,
|
|
83
|
+
id: String? = nil,
|
|
84
|
+
timestamp: String? = nil,
|
|
85
|
+
metadata: Metadata? = nil,
|
|
86
|
+
error: (any Error)? = nil,
|
|
87
|
+
) {
|
|
88
|
+
capture(
|
|
89
|
+
level,
|
|
90
|
+
message(),
|
|
91
|
+
call: LogBrewLoggerCall(id: id, timestamp: timestamp, metadata: metadata, error: error),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public func trace(
|
|
96
|
+
_ message: @autoclosure () -> String,
|
|
97
|
+
id: String? = nil,
|
|
98
|
+
timestamp: String? = nil,
|
|
99
|
+
metadata: Metadata? = nil,
|
|
100
|
+
) {
|
|
101
|
+
log(.trace, message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public func debug(
|
|
105
|
+
_ message: @autoclosure () -> String,
|
|
106
|
+
id: String? = nil,
|
|
107
|
+
timestamp: String? = nil,
|
|
108
|
+
metadata: Metadata? = nil,
|
|
109
|
+
) {
|
|
110
|
+
log(.debug, message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public func info(
|
|
114
|
+
_ message: @autoclosure () -> String,
|
|
115
|
+
id: String? = nil,
|
|
116
|
+
timestamp: String? = nil,
|
|
117
|
+
metadata: Metadata? = nil,
|
|
118
|
+
) {
|
|
119
|
+
log(.info, message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public func notice(
|
|
123
|
+
_ message: @autoclosure () -> String,
|
|
124
|
+
id: String? = nil,
|
|
125
|
+
timestamp: String? = nil,
|
|
126
|
+
metadata: Metadata? = nil,
|
|
127
|
+
) {
|
|
128
|
+
log(.notice, message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public func warning(
|
|
132
|
+
_ message: @autoclosure () -> String,
|
|
133
|
+
id: String? = nil,
|
|
134
|
+
timestamp: String? = nil,
|
|
135
|
+
metadata: Metadata? = nil,
|
|
136
|
+
) {
|
|
137
|
+
log(.warning, message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public func warn(
|
|
141
|
+
_ message: @autoclosure () -> String,
|
|
142
|
+
id: String? = nil,
|
|
143
|
+
timestamp: String? = nil,
|
|
144
|
+
metadata: Metadata? = nil,
|
|
145
|
+
) {
|
|
146
|
+
warning(message(), id: id, timestamp: timestamp, metadata: metadata)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public func error(
|
|
150
|
+
_ message: @autoclosure () -> String,
|
|
151
|
+
id: String? = nil,
|
|
152
|
+
timestamp: String? = nil,
|
|
153
|
+
metadata: Metadata? = nil,
|
|
154
|
+
error: (any Error)? = nil,
|
|
155
|
+
) {
|
|
156
|
+
log(.error, message(), id: id, timestamp: timestamp, metadata: metadata, error: error)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public func fault(
|
|
160
|
+
_ message: @autoclosure () -> String,
|
|
161
|
+
id: String? = nil,
|
|
162
|
+
timestamp: String? = nil,
|
|
163
|
+
metadata: Metadata? = nil,
|
|
164
|
+
error: (any Error)? = nil,
|
|
165
|
+
) {
|
|
166
|
+
log(.fault, message(), id: id, timestamp: timestamp, metadata: metadata, error: error)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public func critical(
|
|
170
|
+
_ message: @autoclosure () -> String,
|
|
171
|
+
id: String? = nil,
|
|
172
|
+
timestamp: String? = nil,
|
|
173
|
+
metadata: Metadata? = nil,
|
|
174
|
+
error: (any Error)? = nil,
|
|
175
|
+
) {
|
|
176
|
+
log(.critical, message(), id: id, timestamp: timestamp, metadata: metadata, error: error)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public func flush() {
|
|
180
|
+
guard let transport, client.pendingEvents() > 0 else {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
lock.lock()
|
|
185
|
+
defer {
|
|
186
|
+
lock.unlock()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
do {
|
|
190
|
+
_ = try client.flush(transport: transport)
|
|
191
|
+
} catch {
|
|
192
|
+
onError?(error)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private func capture(_ level: LogBrewLoggerLevel, _ message: String, call: LogBrewLoggerCall) {
|
|
197
|
+
lock.lock()
|
|
198
|
+
defer {
|
|
199
|
+
lock.unlock()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
do {
|
|
203
|
+
try client.log(
|
|
204
|
+
call.id ?? nextEventID(),
|
|
205
|
+
timestamp: call.timestamp ?? timestampProvider(),
|
|
206
|
+
attributes: LogAttributes(
|
|
207
|
+
message: message,
|
|
208
|
+
level: level.logBrewLevel,
|
|
209
|
+
logger: loggerName,
|
|
210
|
+
metadata: logMetadata(level: level, metadata: call.metadata, error: call.error),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
if flushOnLog, let transport {
|
|
214
|
+
_ = try client.flush(transport: transport)
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
onError?(error)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private func nextEventID() -> String {
|
|
222
|
+
nextEventNumber += 1
|
|
223
|
+
return "\(eventIDPrefix)_\(nextEventNumber)"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private func logMetadata(
|
|
227
|
+
level: LogBrewLoggerLevel,
|
|
228
|
+
metadata: Metadata?,
|
|
229
|
+
error: (any Error)?,
|
|
230
|
+
) -> Metadata {
|
|
231
|
+
var merged = baseMetadata
|
|
232
|
+
metadata?.forEach { key, value in
|
|
233
|
+
merged[key] = value
|
|
234
|
+
}
|
|
235
|
+
merged["source"] = "swift"
|
|
236
|
+
merged["swiftLogLevel"] = .string(level.rawValue)
|
|
237
|
+
if let subsystem {
|
|
238
|
+
merged["swiftSubsystem"] = .string(subsystem)
|
|
239
|
+
}
|
|
240
|
+
if let category {
|
|
241
|
+
merged["swiftCategory"] = .string(category)
|
|
242
|
+
}
|
|
243
|
+
if let error {
|
|
244
|
+
merged["swiftErrorType"] = .string(String(reflecting: type(of: error)))
|
|
245
|
+
merged["swiftErrorDescription"] = .string(error.localizedDescription)
|
|
246
|
+
}
|
|
247
|
+
return merged
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private static func defaultTimestamp() -> String {
|
|
251
|
+
let formatter = ISO8601DateFormatter()
|
|
252
|
+
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
253
|
+
return formatter.string(from: Date())
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private struct LogBrewLoggerCall {
|
|
258
|
+
let id: String?
|
|
259
|
+
let timestamp: String?
|
|
260
|
+
let metadata: Metadata?
|
|
261
|
+
let error: (any Error)?
|
|
262
|
+
}
|