-
Notifications
You must be signed in to change notification settings - Fork 33
feat: Add OTel-Swift supported Tracing #910
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
Merged
+286
−21
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
9f8f532
feat: Add OTel-Swift supported Tracing
dayaffe e34b38e
raise minimum macOS to v12
dayaffe 0652e6c
Merge branch 'main' into day/implement-tracing-otel
dayaffe c738d11
add import for OpenTelemetryConcurrency for linux support
dayaffe fdf23b7
add linux and visionOS #if
dayaffe cbca5f8
only add opentelemetry on non-linux and non-vision os
dayaffe 1266afa
Merge branch 'main' into day/implement-tracing-otel
dayaffe d315e22
Merge branch 'main' into day/implement-tracing-otel
dayaffe 8e1d756
Merge branch 'main' into day/implement-tracing-otel
dayaffe c0d6252
only add telemetry dependency if correct os
dayaffe 27982f8
fix client runtime dependencies
dayaffe 39969cf
try again
dayaffe 07a1320
revert previous bad change
dayaffe 05e39a9
add preconcurrency flag
dayaffe 014ed05
Merge branch 'main' into day/implement-tracing-otel
dayaffe 3311c02
try making swift 6 compatible
dayaffe 914fa3b
reduce version to previous one we were using
dayaffe e72332f
add back preconcurrency
dayaffe 45f6a89
another try at concurrency
dayaffe 2be51b5
address comments
dayaffe e7a12d0
fix failures
dayaffe 6d3cdea
remove extra comma
dayaffe 95cb3b2
more fixes
dayaffe 093a18f
Merge branch 'main' into day/implement-tracing-otel
dayaffe 891c9ee
Merge branch 'main' into day/implement-tracing-otel
dayaffe 0167e76
Merge branch 'main' into day/implement-tracing-otel
dayaffe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
Sources/ClientRuntime/Telemetry/Providers/OpenTelemetry/OTelProvider.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
import Foundation | ||
|
||
// OpenTelemetrySdk specific imports | ||
@preconcurrency import protocol OpenTelemetrySdk.SpanExporter | ||
|
||
/// Namespace for the SDK Telemetry implementation. | ||
public enum OpenTelemetrySwift { | ||
sichanyoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The SDK TelemetryProviderOTel Implementation. | ||
/// | ||
/// - contextManager: no-op | ||
/// - loggerProvider: provides SwiftLoggers | ||
/// - meterProvider: no-op | ||
/// - tracerProvider: provides OTelTracerProvider with InMemoryExporter | ||
public static func provider(spanExporter: any SpanExporter) -> TelemetryProvider { | ||
return OpenTelemetrySwiftProvider(spanExporter: spanExporter) | ||
} | ||
|
||
public final class OpenTelemetrySwiftProvider: TelemetryProvider { | ||
public let contextManager: TelemetryContextManager | ||
public let loggerProvider: LoggerProvider | ||
public let meterProvider: MeterProvider | ||
public let tracerProvider: TracerProvider | ||
|
||
public init(spanExporter: SpanExporter) { | ||
self.contextManager = DefaultTelemetry.defaultContextManager | ||
self.loggerProvider = DefaultTelemetry.defaultLoggerProvider | ||
self.meterProvider = DefaultTelemetry.defaultMeterProvider | ||
self.tracerProvider = OTelTracerProvider(spanExporter: spanExporter) | ||
} | ||
} | ||
} | ||
#endif |
134 changes: 134 additions & 0 deletions
134
Sources/ClientRuntime/Telemetry/Providers/OpenTelemetry/OTelTracing.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
// OpenTelemetryApi specific imports | ||
@preconcurrency import protocol OpenTelemetryApi.Tracer | ||
@preconcurrency import protocol OpenTelemetryApi.Span | ||
@preconcurrency import enum OpenTelemetryApi.SpanKind | ||
@preconcurrency import enum OpenTelemetryApi.Status | ||
@preconcurrency import enum OpenTelemetryApi.AttributeValue | ||
|
||
// OpenTelemetrySdk specific imports | ||
@preconcurrency import class OpenTelemetrySdk.TracerProviderSdk | ||
@preconcurrency import class OpenTelemetrySdk.TracerProviderBuilder | ||
@preconcurrency import struct OpenTelemetrySdk.SimpleSpanProcessor | ||
@preconcurrency import protocol OpenTelemetrySdk.SpanExporter | ||
@preconcurrency import struct OpenTelemetrySdk.Resource | ||
|
||
// Smithy specific imports | ||
import struct Smithy.AttributeKey | ||
import struct Smithy.Attributes | ||
|
||
public typealias OpenTelemetryTracer = OpenTelemetryApi.Tracer | ||
public typealias OpenTelemetrySpanKind = OpenTelemetryApi.SpanKind | ||
public typealias OpenTelemetrySpan = OpenTelemetryApi.Span | ||
public typealias OpenTelemetryStatus = OpenTelemetryApi.Status | ||
|
||
// Trace | ||
public final class OTelTracerProvider: TracerProvider { | ||
private let sdkTracerProvider: TracerProviderSdk | ||
|
||
public init(spanExporter: SpanExporter) { | ||
self.sdkTracerProvider = TracerProviderBuilder() | ||
.add(spanProcessor: SimpleSpanProcessor(spanExporter: spanExporter)) | ||
.with(resource: Resource()) | ||
.build() | ||
} | ||
|
||
public func getTracer(scope: String) -> any Tracer { | ||
let tracer = self.sdkTracerProvider.get(instrumentationName: scope) | ||
return OTelTracerImpl(otelTracer: tracer) | ||
} | ||
} | ||
|
||
public final class OTelTracerImpl: Tracer { | ||
private let otelTracer: OpenTelemetryTracer | ||
|
||
public init(otelTracer: OpenTelemetryTracer) { | ||
self.otelTracer = otelTracer | ||
} | ||
|
||
public func createSpan( | ||
name: String, | ||
initialAttributes: Attributes?, spanKind: SpanKind, parentContext: (any TelemetryContext)? | ||
) -> any TraceSpan { | ||
let spanBuilder = self.otelTracer | ||
.spanBuilder(spanName: name) | ||
.setSpanKind(spanKind: spanKind.toOTelSpanKind()) | ||
|
||
initialAttributes?.getKeys().forEach { key in | ||
spanBuilder.setAttribute( | ||
key: key, | ||
value: (initialAttributes?.get(key: AttributeKey<String>(name: key)))! | ||
) | ||
} | ||
|
||
return OTelTraceSpanImpl(name: name, otelSpan: spanBuilder.startSpan()) | ||
} | ||
} | ||
|
||
private final class OTelTraceSpanImpl: TraceSpan { | ||
let name: String | ||
private let otelSpan: OpenTelemetrySpan | ||
|
||
public init(name: String, otelSpan: OpenTelemetrySpan) { | ||
self.name = name | ||
self.otelSpan = otelSpan | ||
} | ||
|
||
func emitEvent(name: String, attributes: Attributes?) { | ||
if let attributes = attributes, !(attributes.size == 0) { | ||
self.otelSpan.addEvent(name: name, attributes: attributes.toOtelAttributes()) | ||
} else { | ||
self.otelSpan.addEvent(name: name) | ||
} | ||
} | ||
|
||
func setAttribute<T>(key: AttributeKey<T>, value: T) { | ||
self.otelSpan.setAttribute(key: key.getName(), value: AttributeValue.init(value)) | ||
} | ||
|
||
func setStatus(status: TraceSpanStatus) { | ||
self.otelSpan.status = status.toOTelStatus() | ||
} | ||
|
||
func end() { | ||
self.otelSpan.end() | ||
} | ||
} | ||
|
||
extension SpanKind { | ||
func toOTelSpanKind() -> OpenTelemetrySpanKind { | ||
switch self { | ||
case .client: | ||
return .client | ||
case .consumer: | ||
return .consumer | ||
case .internal: | ||
return .internal | ||
case .producer: | ||
return .producer | ||
case .server: | ||
return .server | ||
} | ||
} | ||
} | ||
|
||
extension TraceSpanStatus { | ||
func toOTelStatus() -> OpenTelemetryStatus { | ||
switch self { | ||
case .error: | ||
return .error(description: "An error occured!") // status doesn't have error description | ||
case .ok: | ||
return .ok | ||
case .unset: | ||
return .unset | ||
} | ||
} | ||
} | ||
#endif |
55 changes: 55 additions & 0 deletions
55
Sources/ClientRuntime/Telemetry/Providers/OpenTelemetry/OTelUtils.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// Copyright Amazon.com Inc. or its affiliates. | ||
// All Rights Reserved. | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) | ||
// OpenTelemetryApi specific imports | ||
@preconcurrency import enum OpenTelemetryApi.AttributeValue | ||
@preconcurrency import class OpenTelemetryApi.AttributeArray | ||
|
||
// Smithy imports | ||
import struct Smithy.Attributes | ||
import struct Smithy.AttributeKey | ||
|
||
extension Attributes { | ||
public func toOtelAttributes() -> [String: AttributeValue] { | ||
let keys: [String] = self.getKeys() | ||
var otelKeys: [String: AttributeValue] = [:] | ||
|
||
guard !keys.isEmpty else { | ||
return [:] | ||
} | ||
|
||
keys.forEach { key in | ||
// Try to get the value as different types | ||
if let stringValue = self.get(key: AttributeKey<String>(name: key)) { | ||
otelKeys[key] = AttributeValue.string(stringValue) | ||
} else if let intValue = self.get(key: AttributeKey<Int>(name: key)) { | ||
otelKeys[key] = AttributeValue.int(intValue) | ||
} else if let doubleValue = self.get(key: AttributeKey<Double>(name: key)) { | ||
otelKeys[key] = AttributeValue.double(doubleValue) | ||
} else if let boolValue = self.get(key: AttributeKey<Bool>(name: key)) { | ||
otelKeys[key] = AttributeValue.bool(boolValue) | ||
} else if let arrayValue = self.get(key: AttributeKey<[String]>(name: key)) { | ||
let attributeArray = arrayValue.map { AttributeValue.string($0) } | ||
otelKeys[key] = AttributeValue.array(AttributeArray(values: attributeArray)) | ||
} else if let arrayValue = self.get(key: AttributeKey<[Int]>(name: key)) { | ||
let attributeArray = arrayValue.map { AttributeValue.int($0) } | ||
otelKeys[key] = AttributeValue.array(AttributeArray(values: attributeArray)) | ||
} else if let arrayValue = self.get(key: AttributeKey<[Double]>(name: key)) { | ||
let attributeArray = arrayValue.map { AttributeValue.double($0) } | ||
otelKeys[key] = AttributeValue.array(AttributeArray(values: attributeArray)) | ||
} else if let arrayValue = self.get(key: AttributeKey<[Bool]>(name: key)) { | ||
let attributeArray = arrayValue.map { AttributeValue.bool($0) } | ||
otelKeys[key] = AttributeValue.array(AttributeArray(values: attributeArray)) | ||
} | ||
// If none of the above types match, the value is skipped | ||
} | ||
|
||
return otelKeys | ||
} | ||
} | ||
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is because OTel-Swift is currently not working on non-Apple platforms?