Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Bridge: Logging Bridge for SwiftLog #535

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
307a145
Bridge: Logging Bridge for SwiftLog
khushijain21 Apr 25, 2024
ac0143b
add more config options
khushijain21 Apr 26, 2024
057718b
corrected failures from build
khushijain21 Apr 26, 2024
307d2af
corrected failures from build
khushijain21 Apr 26, 2024
dc8b54f
attribute conversion
khushijain21 May 1, 2024
1dde24c
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
khushijain21 May 1, 2024
91c484f
set loglevel and metadata
khushijain21 May 1, 2024
4a79944
fix build errors
khushijain21 May 6, 2024
594a3fa
applied patch for build fail
khushijain21 May 13, 2024
92020c4
Added test for the LogHandler file
AkhigbeEromo Jun 4, 2024
5519a89
Merge branch 'main' into swiftlog
bryce-b Jun 6, 2024
b55bbcf
Update LogHandlerTests.swift
bryce-b Jun 6, 2024
173629c
Update Package.swift
bryce-b Jun 6, 2024
a5c1889
Update [email protected]
bryce-b Jun 6, 2024
630fdb7
Update [email protected]
bryce-b Jun 6, 2024
0335ae6
Update LogHandler.swift
bryce-b Jun 6, 2024
8e289a5
Update LogHandlerTests.swift
bryce-b Jun 6, 2024
61f3558
Update LogHandler.swift
bryce-b Jun 6, 2024
ff86f87
Merge branch 'main' into swiftlog
bryce-b Jun 6, 2024
5648f1f
Update LogHandlerTests.swift
bryce-b Jun 7, 2024
ad2b739
Added support for embedded arrays for AttributeValues.
bryce-b Jun 11, 2024
9358a49
resolved pr review requests
bryce-b Jun 11, 2024
5b372c3
restored typed arrays in AttributeValue object
bryce-b Jul 1, 2024
3b11987
Merge branch 'main' into swiftlog
bryce-b Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ let package = Package(
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
.library(name: "OTelSwiftLog" type: .static, targets: ["OTelSwiftLog"])
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
Expand All @@ -48,6 +49,10 @@ let package = Package(
dependencies: []),
.target(name: "OpenTelemetrySdk",
dependencies: ["OpenTelemetryApi"]),
.target(name: "OTelSwiftLog",
dependencies: ["OpenTelemetrySdk",
.product(name: "Logging", package: "swift-log")],
path: "Sources/Bridges/OTelSwiftLog"),
.target(name: "ResourceExtension",
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Instrumentation/SDKResourceExtension",
Expand Down Expand Up @@ -116,15 +121,17 @@ let package = Package(
.target(name: "PersistenceExporter",
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Exporters/Persistence"),
.testTarget(name: "OTelSwiftLogTests",
dependencies: ["OTelSwiftLog"],
path: "Tests/BridgesTests/OTelSwiftLog"),
.testTarget(name: "NetworkStatusTests",
dependencies: ["NetworkStatus", .product(name: "Reachability", package: "Reachability.swift")],
path: "Tests/InstrumentationTests/NetworkStatusTests"),
.testTarget(name: "OpenTelemetryApiTests",
dependencies: ["OpenTelemetryApi"],
path: "Tests/OpenTelemetryApiTests"),
.testTarget(name: "OpenTelemetrySdkTests",
dependencies: ["OpenTelemetryApi",
"OpenTelemetrySdk"],
dependencies: ["OpenTelemetrySdk"],
path: "Tests/OpenTelemetrySdkTests"),
.testTarget(name: "ResourceExtensionTests",
dependencies: ["ResourceExtension", "OpenTelemetrySdk"],
Expand Down Expand Up @@ -173,27 +180,27 @@ let package = Package(
dependencies: ["OpenTelemetryApi"],
path: "Examples/Logging Tracer"),
.target(name: "SimpleExporter",
dependencies: ["OpenTelemetrySdk", "JaegerExporter", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
dependencies: ["JaegerExporter", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
path: "Examples/Simple Exporter",
exclude: ["README.md"]),
.target(name: "OTLPExporter",
dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterGrpc", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
dependencies: ["OpenTelemetryProtocolExporterGrpc", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
path: "Examples/OTLP Exporter",
exclude: ["README.md"]),
.target(name: "OTLPHTTPExporter",
dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
dependencies: ["OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
path: "Examples/OTLP HTTP Exporter",
exclude: ["README.md"]),
.target(name: "PrometheusSample",
dependencies: ["OpenTelemetrySdk", "PrometheusExporter"],
dependencies: ["PrometheusExporter"],
path: "Examples/Prometheus Sample",
exclude: ["README.md"]),
.target(name: "DatadogSample",
dependencies: ["DatadogExporter"],
path: "Examples/Datadog Sample",
exclude: ["README.md"]),
.target(name: "StableMetricSample",
dependencies: ["OpenTelemetrySdk", "OpenTelemetryApi", "OpenTelemetryProtocolExporter", .product(name: "GRPC", package: "grpc-swift")],
dependencies: ["OpenTelemetryProtocolExporter", .product(name: "GRPC", package: "grpc-swift")],
path: "Examples/Stable Metric Sample",
exclude: ["README.md"]),
.target(name: "NetworkSample",
Expand Down
14 changes: 11 additions & 3 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let package = Package(
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
.library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]),
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
Expand All @@ -53,6 +54,10 @@ let package = Package(
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Instrumentation/SDKResourceExtension",
exclude: ["README.md"]),
.target(name: "OTelSwiftLog",
dependencies: ["OpenTelemetrySdk",
.product(name: "Logging", package: "swift-log")],
path: "Sources/Bridges/OTelSwiftLog"),
.target(name: "URLSessionInstrumentation",
dependencies: ["OpenTelemetrySdk", "NetworkStatus"],
path: "Sources/Instrumentation/URLSession",
Expand All @@ -70,8 +75,8 @@ let package = Package(
exclude: ["README.md"]),
.target(name: "OpenTracingShim",
dependencies: [
"OpenTelemetrySdk",
.product(name: "Opentracing", package: "opentracing-objc")
"OpenTelemetrySdk",
.product(name: "Opentracing", package: "opentracing-objc")
],
path: "Sources/Importers/OpenTracingShim",
exclude: ["README.md"]),
Expand Down Expand Up @@ -121,6 +126,9 @@ let package = Package(
.target(name: "PersistenceExporter",
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Exporters/Persistence"),
.testTarget(name: "OTelSwiftLogTests",
dependencies: ["OTelSwiftLog"],
path: "Tests/BridgesTests/OTelSwiftLog"),
.testTarget(name: "NetworkStatusTests",
dependencies: [
"NetworkStatus",
Expand All @@ -144,7 +152,7 @@ let package = Package(
path: "Tests/InstrumentationTests/URLSessionTests"),
.testTarget(name: "OpenTracingShimTests",
dependencies: ["OpenTracingShim",
"OpenTelemetrySdk"],
"OpenTelemetrySdk"],
path: "Tests/ImportersTests/OpenTracingShim"),
.testTarget(name: "SwiftMetricsShimTests",
dependencies: ["SwiftMetricsShim",
Expand Down
15 changes: 11 additions & 4 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let package = Package(
.library(name: "InMemoryExporter", type: .static, targets: ["InMemoryExporter"]),
.library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]),
.library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]),
.library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]),
.executable(name: "simpleExporter", targets: ["SimpleExporter"]),
.executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
.executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
Expand All @@ -48,6 +49,10 @@ let package = Package(
dependencies: []),
.target(name: "OpenTelemetrySdk",
dependencies: ["OpenTelemetryApi"]),
.target(name: "OTelSwiftLog",
dependencies: ["OpenTelemetrySdk",
.product(name: "Logging", package: "swift-log")],
path: "Sources/Bridges/OTelSwiftLog"),
.target(name: "ResourceExtension",
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Instrumentation/SDKResourceExtension",
Expand All @@ -68,8 +73,8 @@ let package = Package(
exclude: ["README.md"]),
.target(name: "OpenTracingShim",
dependencies: [
"OpenTelemetrySdk",
.product(name: "Opentracing", package: "opentracing-objc")
"OpenTelemetrySdk",
.product(name: "Opentracing", package: "opentracing-objc")
],
path: "Sources/Importers/OpenTracingShim",
exclude: ["README.md"]),
Expand Down Expand Up @@ -119,6 +124,9 @@ let package = Package(
.target(name: "PersistenceExporter",
dependencies: ["OpenTelemetrySdk"],
path: "Sources/Exporters/Persistence"),
.testTarget(name: "OTelSwiftLogTests",
dependencies: ["OTelSwiftLog"],
path: "Tests/BridgesTests/OTelSwiftLog"),
.testTarget(name: "NetworkStatusTests",
dependencies: [
"NetworkStatus",
Expand All @@ -128,8 +136,7 @@ let package = Package(
dependencies: ["OpenTelemetryApi"],
path: "Tests/OpenTelemetryApiTests"),
.testTarget(name: "OpenTelemetrySdkTests",
dependencies: ["OpenTelemetryApi",
"OpenTelemetrySdk"],
dependencies: ["OpenTelemetrySdk"],
path: "Tests/OpenTelemetrySdkTests"),
.testTarget(name: "ResourceExtensionTests",
dependencies: ["ResourceExtension", "OpenTelemetrySdk"],
Expand Down
151 changes: 151 additions & 0 deletions Sources/Bridges/OTelSwiftLog/LogHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import Foundation
import OpenTelemetryApi
import OpenTelemetrySdk
import Logging

// let the bridgename be the url of the package?
let bridgeName: String = "OTelSwiftLog"
let version: String = "1.0.0"

/// A custom log handler to translate swift logs into otel logs
struct OTelLogHandler: LogHandler {

/// Get or set the configured log level.
///
/// - note: `LogHandler`s must treat the log level as a value type. This means that the change in metadata must
/// only affect this very `LogHandler`. It is acceptable to provide some form of global log level override
/// that means a change in log level on a particular `LogHandler` might not be reflected in any
/// `LogHandler`.
public var logLevel: Logging.Logger.Level = .info

/// loggerProvider to use for the bridge.
private var loggerProvider : LoggerProvider
private var logger: OpenTelemetryApi.Logger

// Define metadata for this handler
public var metadata: Logging.Logger.Metadata = [:]
public subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? {
get {
return self.metadata[key]
}
set {
self.metadata[key] = newValue
}
}

/// create a new OtelLogHandler
/// - Parameter loggerProvider: The logger provider to use in the bridge. Defaults to the global logger provider.
/// - Parameter includeTraceContext : boolean flag used for the logger builder
/// - Parameter attributes: attributes to apply to the logger builder
public init(loggerProvider: LoggerProvider = OpenTelemetrySdk.LoggerProviderSdk(),
includeTraceContext : Bool = true,
attributes: [String:AttributeValue] = [String:AttributeValue]()) {

self.loggerProvider = loggerProvider
self.logger = self.loggerProvider.loggerBuilder(instrumentationScopeName: bridgeName)
.setInstrumentationVersion(version)
.setEventDomain("device")
.setIncludeTraceContext(true)
.setAttributes(attributes)
.setIncludeTraceContext(includeTraceContext)
.build()
}

public func log(level: Logging.Logger.Level,
message: Logging.Logger.Message,
metadata: Logging.Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt) {


// This converts log atrributes to otel attributes
var otelattributes: [String: AttributeValue] = [
"source": AttributeValue.string(source),
"file": AttributeValue.string(file),
"function": AttributeValue.string(function),
"line": AttributeValue.int(Int(line)),
]

// Convert metadata from the method parameter to AttributeValue and assign it to otelattributes
if let metadata = metadata {
let methodMetadata = convertMetadata(metadata)
otelattributes.merge(methodMetadata) { _, new in new }
}

// Convert metadata from the struct property to AttributeValue and merge it with otelattributes
let structMetadata = convertMetadata(self.metadata)
otelattributes.merge(structMetadata) { _, new in new }

// Build the log record and emit it
let event = self.logger.logRecordBuilder().setSeverity(convertSeverity(level: level))
.setBody(AttributeValue.string(message.description))
.setAttributes(otelattributes)

if let context = OpenTelemetry.instance.contextProvider.activeSpan?.context {
_ = event.setSpanContext(context)
}
event.emit()

}



}

func convertMetadata(_ metadata: Logging.Logger.Metadata) -> [String: AttributeValue] {
var convertedAttributes: [String: AttributeValue] = [:]

// Iterate over each key-value pair in the metadata dictionary
for (key, value) in metadata {
// Convert each value to AttributeValue
let attributeValue = convertToAttributeValue(value)

// Store the converted value with its corresponding key in the attributes dictionary
convertedAttributes[key] = attributeValue
}

return convertedAttributes
}

// Function to recursively convert nested dictionaries to AttributeValue
func convertToAttributeValue(_ value: Logging.Logger.Metadata.Value) -> AttributeValue {
switch value {
case .dictionary(let nestedDictionary):
// If value is a nested dictionary, recursively convert it
var nestedAttributes: [String: AttributeValue] = [:]
for (nestedKey, nestedValue) in nestedDictionary {
nestedAttributes[nestedKey] = convertToAttributeValue(nestedValue)
}
return AttributeValue.set(AttributeSet(labels: nestedAttributes))
case .array(let nestedArray):
// If value is a nested array, recursively convert it
let nestedValues = nestedArray.map { convertToAttributeValue($0) }
return AttributeValue.array(AttributeArray(values: nestedValues))
case .string(let str):
return AttributeValue(str)
case .stringConvertible(let strConvertable):
return AttributeValue(strConvertable.description)

}
}

func convertSeverity(level: Logging.Logger.Level) -> OpenTelemetryApi.Severity{
switch level {
case .trace:
return OpenTelemetryApi.Severity.trace
case .debug:
return OpenTelemetryApi.Severity.debug
case .info:
return OpenTelemetryApi.Severity.info
case .notice:
return OpenTelemetryApi.Severity.info2
case .warning:
return OpenTelemetryApi.Severity.warn
case .error:
return OpenTelemetryApi.Severity.error
case .critical:
return OpenTelemetryApi.Severity.error2 //should this be fatal instead?
}
}
70 changes: 70 additions & 0 deletions Sources/Bridges/OTelSwiftLog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
### How to use OTelLogHandler

1. Using Default Scope:

```swift
import Foundation
import Logging

// Initialize the OTelLogHandler without a custom scope (using default scope)
let otelLogHandler = OTelLogHandler()

// Create a Logger instance with the default log handler
var logger = Logger(label: "com.example.myapp")
logger.logLevel = .debug
logger.handler = otelLogHandler

// Log messages with various log levels
logger.debug("This is a debug message")

// Log with additional metadata
logger[metadataKey: "customKey"] = "customValue"
logger.info("Logging with additional metadata")
```

2. Using Custom Scope:
```swift
import Foundation
import Logging

// Initialize a custom instrumentation scope
let customScope = InstrumentationScope(
name: "MyCustomInstrumentationScope",
version: "1.0.0",
eventDomain: "MyEventDomain",
schemaUrl: "https://example.com/schema",
includeTraceContext: true,
attributes: ["customAttribute": AttributeValue.string("customValue")]
)

// Initialize the OTelLogHandler with custom scope
let otelLogHandler = OTelLogHandler(scope: customScope)

// Create a Logger instance with the custom log handler
var logger = Logger(label: "com.example.myapp")
logger.logLevel = .debug
logger.handler = otelLogHandler

// Log messages with various log levels
logger.debug("This is a debug message")
```

3. Using Custom Logger Provider:
```swift
import Foundation
import Logging

// Initialize a custom LoggerProvider
let customLoggerProvider = MyCustomLoggerProvider()

// Initialize the OTelLogHandler with custom LoggerProvider and default scope
let otelLogHandler = OTelLogHandler(loggerProvider: customLoggerProvider)

// Create a Logger instance with the custom log handler
var logger = Logger(label: "com.example.myapp")
logger.logLevel = .debug
logger.handler = otelLogHandler

// Log messages with various log levels
logger.debug("This is a debug message")
```
Loading
Loading