|  | 
|  | 1 | +import Foundation | 
|  | 2 | +@preconcurrency import OpenTelemetryApi | 
|  | 3 | +@preconcurrency import OpenTelemetrySdk | 
|  | 4 | +@preconcurrency import OpenTelemetryProtocolExporterHttp | 
|  | 5 | +@preconcurrency import StdoutExporter | 
|  | 6 | + | 
|  | 7 | +import SignPostIntegration | 
|  | 8 | + | 
|  | 9 | + | 
|  | 10 | + | 
|  | 11 | +public actor InstrumentationManager { | 
|  | 12 | +    private let openTelemetry: OpenTelemetry | 
|  | 13 | +    private let tracerProvider: TracerProvider | 
|  | 14 | +     | 
|  | 15 | +    private let serviceName: String | 
|  | 16 | +    private let serviceVersion: String | 
|  | 17 | +     | 
|  | 18 | +    private let sessionManager: SessionManager | 
|  | 19 | +     | 
|  | 20 | +    public init( | 
|  | 21 | +        serviceName: String = "launchdarkly-observability-swift", | 
|  | 22 | +        serviceVersion: String = "1.0.0", | 
|  | 23 | +        openTelemetry: OpenTelemetry = OpenTelemetry.instance, | 
|  | 24 | +        sessionManager: SessionManager = SessionManager() | 
|  | 25 | +    ) { | 
|  | 26 | +        let url = URL(string: "https://otel.observability.app.launchdarkly.com:4318/v1/traces")! | 
|  | 27 | +        let otlpHttpTraceExporter = OtlpHttpTraceExporter( | 
|  | 28 | +            endpoint: url, | 
|  | 29 | +            envVarHeaders: [ | 
|  | 30 | +                ("X-LaunchDarkly-Project", "sdk-465cf811-71a3-42ee-8a9f-e325b6ed3a26") | 
|  | 31 | +            ] | 
|  | 32 | +        ) | 
|  | 33 | +         | 
|  | 34 | +         | 
|  | 35 | +        let stdoutExporter = StdoutSpanExporter() | 
|  | 36 | +        let spanExporter = MultiSpanExporter(spanExporters: [otlpHttpTraceExporter, stdoutExporter]) | 
|  | 37 | + | 
|  | 38 | +        let spanProcessor = SimpleSpanProcessor(spanExporter: spanExporter) | 
|  | 39 | +         | 
|  | 40 | +        // TODO: api and sdk keys as env. variables | 
|  | 41 | +        let resource = Resource( | 
|  | 42 | +            attributes: [ | 
|  | 43 | +                ResourceAttributes.serviceName.rawValue: AttributeValue.string(serviceName), | 
|  | 44 | +                ResourceAttributes.serviceVersion.rawValue: AttributeValue.string(serviceVersion), | 
|  | 45 | +                ResourceAttributes.telemetrySdkName.rawValue: AttributeValue.string("swift-launchdarkly-observability"), | 
|  | 46 | +                ResourceAttributes.telemetrySdkLanguage.rawValue: AttributeValue.string("swift"), | 
|  | 47 | +                "highlight.project_id": AttributeValue.string("sdk-465cf811-71a3-42ee-8a9f-e325b6ed3a26"), | 
|  | 48 | +                "highlight.session_id": AttributeValue.string(UUID().uuidString), | 
|  | 49 | +                "X-LaunchDarkly-Project": AttributeValue.string("6830e8e9e63ae80ddde9384b") | 
|  | 50 | +            ] | 
|  | 51 | +        ) | 
|  | 52 | +         | 
|  | 53 | +        let traceProvider = TracerProviderBuilder() | 
|  | 54 | +            .add(spanProcessor: spanProcessor) | 
|  | 55 | +            .with(resource: resource) | 
|  | 56 | +            .build() | 
|  | 57 | +        OpenTelemetry.registerTracerProvider(tracerProvider: traceProvider) | 
|  | 58 | +         | 
|  | 59 | +         | 
|  | 60 | +        OpenTelemetry.registerPropagators( | 
|  | 61 | +            textPropagators: [ | 
|  | 62 | +                W3CTraceContextPropagator(), | 
|  | 63 | +                B3Propagator(), | 
|  | 64 | +                JaegerPropagator(), | 
|  | 65 | +            ], | 
|  | 66 | +            baggagePropagator: W3CBaggagePropagator() | 
|  | 67 | +        ) | 
|  | 68 | +         | 
|  | 69 | +        self.serviceName = serviceName | 
|  | 70 | +        self.serviceVersion = serviceVersion | 
|  | 71 | +        self.openTelemetry = openTelemetry | 
|  | 72 | +        self.tracerProvider = traceProvider | 
|  | 73 | +        self.sessionManager = sessionManager | 
|  | 74 | +         | 
|  | 75 | +        if #available(iOS 15.0, macOS 12, tvOS 15.0, watchOS 8.0, *) { | 
|  | 76 | +            let tracerProviderSDK = OpenTelemetry.instance.tracerProvider as? TracerProviderSdk | 
|  | 77 | +            tracerProviderSDK?.addSpanProcessor(OSSignposterIntegration()) | 
|  | 78 | +        } else { | 
|  | 79 | +            let tracerProviderSDK = OpenTelemetry.instance.tracerProvider as? TracerProviderSdk | 
|  | 80 | +            tracerProviderSDK?.addSpanProcessor(SignPostIntegration()) | 
|  | 81 | +        } | 
|  | 82 | +    } | 
|  | 83 | +     | 
|  | 84 | +    private func getTracer() -> Tracer { | 
|  | 85 | +        tracerProvider.get(instrumentationName: serviceName, instrumentationVersion: serviceVersion) | 
|  | 86 | +    } | 
|  | 87 | +     | 
|  | 88 | +    let sampleKey = "sampleKey" | 
|  | 89 | +    let sampleValue = "sampleValue" | 
|  | 90 | +} | 
|  | 91 | + | 
|  | 92 | +// TODO: Remove test code | 
|  | 93 | +extension InstrumentationManager { | 
|  | 94 | +    public func createSpans() { | 
|  | 95 | +        let tracer = getTracer() | 
|  | 96 | +        let parentSpan1 = tracer.spanBuilder(spanName: "main").setSpanKind(spanKind: .client).startSpan() | 
|  | 97 | +        parentSpan1.setAttribute(key: sampleKey, value: sampleValue) | 
|  | 98 | +//        ("X-LaunchDarkly-Project", "sdk-465cf811-71a3-42ee-8a9f-e325b6ed3a26") | 
|  | 99 | +//        parentSpan1.setAttribute(key: "X-LaunchDarkly-Project", value: .string("sdk-465cf811-71a3-42ee-8a9f-e325b6ed3a26")) | 
|  | 100 | +        openTelemetry.contextProvider.setActiveSpan(parentSpan1) | 
|  | 101 | +        for _ in 1 ... 3 { | 
|  | 102 | +            doWork() | 
|  | 103 | +        } | 
|  | 104 | +        Thread.sleep(forTimeInterval: 0.5) | 
|  | 105 | +         | 
|  | 106 | +        let parentSpan2 = tracer.spanBuilder(spanName: "another").setSpanKind(spanKind: .client).setActive(true).startSpan() | 
|  | 107 | +        parentSpan2.setAttribute(key: sampleKey, value: sampleValue) | 
|  | 108 | +        // do more Work | 
|  | 109 | +        for _ in 1 ... 3 { | 
|  | 110 | +            doWork() | 
|  | 111 | +        } | 
|  | 112 | +        Thread.sleep(forTimeInterval: 0.5) | 
|  | 113 | +         | 
|  | 114 | +        parentSpan2.end() | 
|  | 115 | +        parentSpan1.end() | 
|  | 116 | +    } | 
|  | 117 | + | 
|  | 118 | +    func doWork() { | 
|  | 119 | +        let tracer = getTracer() | 
|  | 120 | +        let childSpan = tracer.spanBuilder(spanName: "doWork").setSpanKind(spanKind: .client).startSpan() | 
|  | 121 | +        childSpan.setAttribute(key: sampleKey, value: sampleValue) | 
|  | 122 | +        Thread.sleep(forTimeInterval: Double.random(in: 0 ..< 10) / 100) | 
|  | 123 | +        childSpan.end() | 
|  | 124 | +    } | 
|  | 125 | +} | 
0 commit comments