Skip to content

Commit 8f19f89

Browse files
bryce-bkhushijain21AkhigbeEromo
authored
Swiftlog (#558)
* Bridge: Logging Bridge for SwiftLog * add more config options * corrected failures from build * corrected failures from build * attribute conversion * set loglevel and metadata * fix build errors * applied patch for build fail * Added test for the LogHandler file * Update LogHandlerTests.swift * Update Package.swift * Update [email protected] * Update [email protected] * Update LogHandler.swift fixed log emitter * Update LogHandlerTests.swift * Update LogHandler.swift access change to accommodate tests. * removed unsafe build flags (#545) * Update LogHandlerTests.swift Added Tests * Added support for embedded arrays for AttributeValues. * resolved pr review requests * restored typed arrays in AttributeValue object * removed dependency on SDK from OtelSwiftLog * upped test timeouts to solve inconsistent failures * changed iphone simulator * disabled flaky tests --------- Co-authored-by: khushijain21 <[email protected]> Co-authored-by: EROMOSELE AKHIGBE <[email protected]>
1 parent 7bad8ae commit 8f19f89

File tree

21 files changed

+782
-377
lines changed

21 files changed

+782
-377
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ PROJECT_NAME="opentelemetry-swift-Package"
22

33
XCODEBUILD_OPTIONS_IOS=\
44
-configuration Debug \
5-
-destination platform='iOS Simulator,name=iPhone 14,OS=latest' \
5+
-destination platform='iOS Simulator,name=iPhone 15,OS=latest' \
66
-scheme $(PROJECT_NAME) \
77
-workspace .
88

Package.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ let package = Package(
2828
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
2929
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
3030
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
31+
.library(name: "OTelSwiftLog" type: .static, targets: ["OTelSwiftLog"])
3132
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
3233
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
3334
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
@@ -48,6 +49,10 @@ let package = Package(
4849
dependencies: []),
4950
.target(name: "OpenTelemetrySdk",
5051
dependencies: ["OpenTelemetryApi"]),
52+
.target(name: "OTelSwiftLog",
53+
dependencies: ["OpenTelemetryApigi",
54+
.product(name: "Logging", package: "swift-log")],
55+
path: "Sources/Bridges/OTelSwiftLog"),
5156
.target(name: "ResourceExtension",
5257
dependencies: ["OpenTelemetrySdk"],
5358
path: "Sources/Instrumentation/SDKResourceExtension",
@@ -116,15 +121,17 @@ let package = Package(
116121
.target(name: "PersistenceExporter",
117122
dependencies: ["OpenTelemetrySdk"],
118123
path: "Sources/Exporters/Persistence"),
124+
.testTarget(name: "OTelSwiftLogTests",
125+
dependencies: ["OTelSwiftLog"],
126+
path: "Tests/BridgesTests/OTelSwiftLog"),
119127
.testTarget(name: "NetworkStatusTests",
120128
dependencies: ["NetworkStatus", .product(name: "Reachability", package: "Reachability.swift")],
121129
path: "Tests/InstrumentationTests/NetworkStatusTests"),
122130
.testTarget(name: "OpenTelemetryApiTests",
123131
dependencies: ["OpenTelemetryApi"],
124132
path: "Tests/OpenTelemetryApiTests"),
125133
.testTarget(name: "OpenTelemetrySdkTests",
126-
dependencies: ["OpenTelemetryApi",
127-
"OpenTelemetrySdk"],
134+
dependencies: ["OpenTelemetrySdk"],
128135
path: "Tests/OpenTelemetrySdkTests"),
129136
.testTarget(name: "ResourceExtensionTests",
130137
dependencies: ["ResourceExtension", "OpenTelemetrySdk"],
@@ -173,27 +180,27 @@ let package = Package(
173180
dependencies: ["OpenTelemetryApi"],
174181
path: "Examples/Logging Tracer"),
175182
.target(name: "SimpleExporter",
176-
dependencies: ["OpenTelemetrySdk", "JaegerExporter", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
183+
dependencies: ["JaegerExporter", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
177184
path: "Examples/Simple Exporter",
178185
exclude: ["README.md"]),
179186
.target(name: "OTLPExporter",
180-
dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterGrpc", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
187+
dependencies: ["OpenTelemetryProtocolExporterGrpc", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
181188
path: "Examples/OTLP Exporter",
182189
exclude: ["README.md"]),
183190
.target(name: "OTLPHTTPExporter",
184-
dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
191+
dependencies: ["OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
185192
path: "Examples/OTLP HTTP Exporter",
186193
exclude: ["README.md"]),
187194
.target(name: "PrometheusSample",
188-
dependencies: ["OpenTelemetrySdk", "PrometheusExporter"],
195+
dependencies: ["PrometheusExporter"],
189196
path: "Examples/Prometheus Sample",
190197
exclude: ["README.md"]),
191198
.target(name: "DatadogSample",
192199
dependencies: ["DatadogExporter"],
193200
path: "Examples/Datadog Sample",
194201
exclude: ["README.md"]),
195202
.target(name: "StableMetricSample",
196-
dependencies: ["OpenTelemetrySdk", "OpenTelemetryApi", "OpenTelemetryProtocolExporter", .product(name: "GRPC", package: "grpc-swift")],
203+
dependencies: ["OpenTelemetryProtocolExporter", .product(name: "GRPC", package: "grpc-swift")],
197204
path: "Examples/Stable Metric Sample",
198205
exclude: ["README.md"]),
199206
.target(name: "NetworkSample",

[email protected]

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ let package = Package(
2929
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
3030
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
3131
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
32+
.library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]),
3233
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
3334
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
3435
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
@@ -53,6 +54,10 @@ let package = Package(
5354
dependencies: ["OpenTelemetrySdk"],
5455
path: "Sources/Instrumentation/SDKResourceExtension",
5556
exclude: ["README.md"]),
57+
.target(name: "OTelSwiftLog",
58+
dependencies: ["OpenTelemetryApi",
59+
.product(name: "Logging", package: "swift-log")],
60+
path: "Sources/Bridges/OTelSwiftLog"),
5661
.target(name: "URLSessionInstrumentation",
5762
dependencies: ["OpenTelemetrySdk", "NetworkStatus"],
5863
path: "Sources/Instrumentation/URLSession",
@@ -70,8 +75,8 @@ let package = Package(
7075
exclude: ["README.md"]),
7176
.target(name: "OpenTracingShim",
7277
dependencies: [
73-
"OpenTelemetrySdk",
74-
.product(name: "Opentracing", package: "opentracing-objc")
78+
"OpenTelemetrySdk",
79+
.product(name: "Opentracing", package: "opentracing-objc")
7580
],
7681
path: "Sources/Importers/OpenTracingShim",
7782
exclude: ["README.md"]),
@@ -121,6 +126,9 @@ let package = Package(
121126
.target(name: "PersistenceExporter",
122127
dependencies: ["OpenTelemetrySdk"],
123128
path: "Sources/Exporters/Persistence"),
129+
.testTarget(name: "OTelSwiftLogTests",
130+
dependencies: ["OTelSwiftLog"],
131+
path: "Tests/BridgesTests/OTelSwiftLog"),
124132
.testTarget(name: "NetworkStatusTests",
125133
dependencies: [
126134
"NetworkStatus",
@@ -144,7 +152,7 @@ let package = Package(
144152
path: "Tests/InstrumentationTests/URLSessionTests"),
145153
.testTarget(name: "OpenTracingShimTests",
146154
dependencies: ["OpenTracingShim",
147-
"OpenTelemetrySdk"],
155+
"OpenTelemetrySdk"],
148156
path: "Tests/ImportersTests/OpenTracingShim"),
149157
.testTarget(name: "SwiftMetricsShimTests",
150158
dependencies: ["SwiftMetricsShim",

[email protected]

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ let package = Package(
2929
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
3030
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
3131
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
32+
.library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]),
3233
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
3334
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
3435
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
@@ -48,6 +49,10 @@ let package = Package(
4849
dependencies: []),
4950
.target(name: "OpenTelemetrySdk",
5051
dependencies: ["OpenTelemetryApi"]),
52+
.target(name: "OTelSwiftLog",
53+
dependencies: ["OpenTelemetryApi",
54+
.product(name: "Logging", package: "swift-log")],
55+
path: "Sources/Bridges/OTelSwiftLog"),
5156
.target(name: "ResourceExtension",
5257
dependencies: ["OpenTelemetrySdk"],
5358
path: "Sources/Instrumentation/SDKResourceExtension",
@@ -68,8 +73,8 @@ let package = Package(
6873
exclude: ["README.md"]),
6974
.target(name: "OpenTracingShim",
7075
dependencies: [
71-
"OpenTelemetrySdk",
72-
.product(name: "Opentracing", package: "opentracing-objc")
76+
"OpenTelemetrySdk",
77+
.product(name: "Opentracing", package: "opentracing-objc")
7378
],
7479
path: "Sources/Importers/OpenTracingShim",
7580
exclude: ["README.md"]),
@@ -119,6 +124,9 @@ let package = Package(
119124
.target(name: "PersistenceExporter",
120125
dependencies: ["OpenTelemetrySdk"],
121126
path: "Sources/Exporters/Persistence"),
127+
.testTarget(name: "OTelSwiftLogTests",
128+
dependencies: ["OTelSwiftLog"],
129+
path: "Tests/BridgesTests/OTelSwiftLog"),
122130
.testTarget(name: "NetworkStatusTests",
123131
dependencies: [
124132
"NetworkStatus",
@@ -128,8 +136,7 @@ let package = Package(
128136
dependencies: ["OpenTelemetryApi"],
129137
path: "Tests/OpenTelemetryApiTests"),
130138
.testTarget(name: "OpenTelemetrySdkTests",
131-
dependencies: ["OpenTelemetryApi",
132-
"OpenTelemetrySdk"],
139+
dependencies: ["OpenTelemetrySdk"],
133140
path: "Tests/OpenTelemetrySdkTests"),
134141
.testTarget(name: "ResourceExtensionTests",
135142
dependencies: ["ResourceExtension", "OpenTelemetrySdk"],
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import Foundation
2+
import OpenTelemetryApi
3+
import Logging
4+
5+
// let the bridgename be the url of the package?
6+
let bridgeName: String = "OTelSwiftLog"
7+
let version: String = "1.0.0"
8+
9+
/// A custom log handler to translate swift logs into otel logs
10+
struct OTelLogHandler: LogHandler {
11+
12+
/// Get or set the configured log level.
13+
///
14+
/// - note: `LogHandler`s must treat the log level as a value type. This means that the change in metadata must
15+
/// only affect this very `LogHandler`. It is acceptable to provide some form of global log level override
16+
/// that means a change in log level on a particular `LogHandler` might not be reflected in any
17+
/// `LogHandler`.
18+
public var logLevel: Logging.Logger.Level = .info
19+
20+
/// loggerProvider to use for the bridge.
21+
private var loggerProvider : LoggerProvider
22+
private var logger: OpenTelemetryApi.Logger
23+
24+
// Define metadata for this handler
25+
public var metadata: Logging.Logger.Metadata = [:]
26+
public subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? {
27+
get {
28+
return self.metadata[key]
29+
}
30+
set {
31+
self.metadata[key] = newValue
32+
}
33+
}
34+
35+
/// create a new OtelLogHandler
36+
/// - Parameter loggerProvider: The logger provider to use in the bridge. Defaults to the global logger provider.
37+
/// - Parameter includeTraceContext : boolean flag used for the logger builder
38+
/// - Parameter attributes: attributes to apply to the logger builder
39+
public init(loggerProvider: LoggerProvider = OpenTelemetryApi.DefaultLoggerProvider.instance,
40+
includeTraceContext : Bool = true,
41+
attributes: [String:AttributeValue] = [String:AttributeValue]()) {
42+
43+
self.loggerProvider = loggerProvider
44+
self.logger = self.loggerProvider.loggerBuilder(instrumentationScopeName: bridgeName)
45+
.setInstrumentationVersion(version)
46+
.setEventDomain("device")
47+
.setIncludeTraceContext(true)
48+
.setAttributes(attributes)
49+
.setIncludeTraceContext(includeTraceContext)
50+
.build()
51+
}
52+
53+
public func log(level: Logging.Logger.Level,
54+
message: Logging.Logger.Message,
55+
metadata: Logging.Logger.Metadata?,
56+
source: String,
57+
file: String,
58+
function: String,
59+
line: UInt) {
60+
61+
62+
// This converts log atrributes to otel attributes
63+
var otelattributes: [String: AttributeValue] = [
64+
"source": AttributeValue.string(source),
65+
"file": AttributeValue.string(file),
66+
"function": AttributeValue.string(function),
67+
"line": AttributeValue.int(Int(line)),
68+
]
69+
70+
// Convert metadata from the method parameter to AttributeValue and assign it to otelattributes
71+
if let metadata = metadata {
72+
let methodMetadata = convertMetadata(metadata)
73+
otelattributes.merge(methodMetadata) { _, new in new }
74+
}
75+
76+
// Convert metadata from the struct property to AttributeValue and merge it with otelattributes
77+
let structMetadata = convertMetadata(self.metadata)
78+
otelattributes.merge(structMetadata) { _, new in new }
79+
80+
// Build the log record and emit it
81+
let event = self.logger.logRecordBuilder().setSeverity(convertSeverity(level: level))
82+
.setBody(AttributeValue.string(message.description))
83+
.setAttributes(otelattributes)
84+
85+
if let context = OpenTelemetry.instance.contextProvider.activeSpan?.context {
86+
_ = event.setSpanContext(context)
87+
}
88+
event.emit()
89+
90+
}
91+
92+
93+
94+
}
95+
96+
func convertMetadata(_ metadata: Logging.Logger.Metadata) -> [String: AttributeValue] {
97+
var convertedAttributes: [String: AttributeValue] = [:]
98+
99+
// Iterate over each key-value pair in the metadata dictionary
100+
for (key, value) in metadata {
101+
// Convert each value to AttributeValue
102+
let attributeValue = convertToAttributeValue(value)
103+
104+
// Store the converted value with its corresponding key in the attributes dictionary
105+
convertedAttributes[key] = attributeValue
106+
}
107+
108+
return convertedAttributes
109+
}
110+
111+
// Function to recursively convert nested dictionaries to AttributeValue
112+
func convertToAttributeValue(_ value: Logging.Logger.Metadata.Value) -> AttributeValue {
113+
switch value {
114+
case .dictionary(let nestedDictionary):
115+
// If value is a nested dictionary, recursively convert it
116+
var nestedAttributes: [String: AttributeValue] = [:]
117+
for (nestedKey, nestedValue) in nestedDictionary {
118+
nestedAttributes[nestedKey] = convertToAttributeValue(nestedValue)
119+
}
120+
return AttributeValue.set(AttributeSet(labels: nestedAttributes))
121+
case .array(let nestedArray):
122+
// If value is a nested array, recursively convert it
123+
let nestedValues = nestedArray.map { convertToAttributeValue($0) }
124+
return AttributeValue.array(AttributeArray(values: nestedValues))
125+
case .string(let str):
126+
return AttributeValue(str)
127+
case .stringConvertible(let strConvertable):
128+
return AttributeValue(strConvertable.description)
129+
130+
}
131+
}
132+
133+
func convertSeverity(level: Logging.Logger.Level) -> OpenTelemetryApi.Severity{
134+
switch level {
135+
case .trace:
136+
return OpenTelemetryApi.Severity.trace
137+
case .debug:
138+
return OpenTelemetryApi.Severity.debug
139+
case .info:
140+
return OpenTelemetryApi.Severity.info
141+
case .notice:
142+
return OpenTelemetryApi.Severity.info2
143+
case .warning:
144+
return OpenTelemetryApi.Severity.warn
145+
case .error:
146+
return OpenTelemetryApi.Severity.error
147+
case .critical:
148+
return OpenTelemetryApi.Severity.error2 //should this be fatal instead?
149+
}
150+
}

0 commit comments

Comments
 (0)