From 2472eb45524818119f05e27658b8a58b9a153091 Mon Sep 17 00:00:00 2001 From: Martin Holman Date: Fri, 31 Jan 2025 16:41:44 -0800 Subject: [PATCH] Add baggage span processor --- Package.swift | 7 ++ Package@swift-5.6.swift | 7 ++ Package@swift-5.9.swift | 9 ++- .../Processors/BaggageSpanProcessor.swift | 39 ++++++++++ .../BaggageSpanProcessorTests.swift | 78 +++++++++++++++++++ 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Sources/Contrib/Processors/BaggageSpanProcessor.swift create mode 100644 Tests/ContribTests/Processors/BaggageSpanProcessorTests.swift diff --git a/Package.swift b/Package.swift index 7fe87b34..93265fcb 100644 --- a/Package.swift +++ b/Package.swift @@ -30,6 +30,7 @@ let package = Package( .library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]), .library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]), .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), + .library(name: "Contrib", targets: ["Contrib"]), .executable(name: "simpleExporter", targets: ["SimpleExporter"]), .executable(name: "OTLPExporter", targets: ["OTLPExporter"]), .executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]), @@ -129,6 +130,9 @@ let package = Package( .target(name: "PersistenceExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Persistence"), + .target(name: "Contrib", + dependencies: ["OpenTelemetryApi", + "OpenTelemetrySdk"]), .testTarget(name: "OTelSwiftLogTests", dependencies: ["OTelSwiftLog"], path: "Tests/BridgesTests/OTelSwiftLog"), @@ -185,6 +189,9 @@ let package = Package( .testTarget(name: "PersistenceExporterTests", dependencies: ["PersistenceExporter"], path: "Tests/ExportersTests/PersistenceExporter"), + .testTarget(name: "ContribTests", + dependencies: ["Contrib", + "InMemoryExporter"]), .target(name: "LoggingTracer", dependencies: ["OpenTelemetryApi"], path: "Examples/Logging Tracer"), diff --git a/Package@swift-5.6.swift b/Package@swift-5.6.swift index f37624eb..bd22e78d 100644 --- a/Package@swift-5.6.swift +++ b/Package@swift-5.6.swift @@ -31,6 +31,7 @@ let package = Package( .library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]), .library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]), .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), + .library(name: "Contrib", targets: ["Contrib"]), .executable(name: "simpleExporter", targets: ["SimpleExporter"]), .executable(name: "OTLPExporter", targets: ["OTLPExporter"]), .executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]), @@ -134,6 +135,9 @@ let package = Package( .target(name: "PersistenceExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Persistence"), + .target(name: "Contrib", + dependencies: ["OpenTelemetryApi", + "OpenTelemetrySdk"]), .testTarget(name: "OTelSwiftLogTests", dependencies: ["OTelSwiftLog"], path: "Tests/BridgesTests/OTelSwiftLog"), @@ -195,6 +199,9 @@ let package = Package( .testTarget(name: "PersistenceExporterTests", dependencies: ["PersistenceExporter"], path: "Tests/ExportersTests/PersistenceExporter"), + .testTarget(name: "ContribTests", + dependencies: ["Contrib", + "InMemoryExporter"]), .executableTarget( name: "LoggingTracer", dependencies: ["OpenTelemetryApi"], diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index b90efcaf..5fd0b3b7 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -25,6 +25,7 @@ let package = Package( .library(name: "InMemoryExporter", targets: ["InMemoryExporter"]), .library(name: "OTelSwiftLog", targets: ["OTelSwiftLog"]), .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), + .library(name: "Contrib", targets: ["Contrib"]), .executable(name: "ConcurrencyContext", targets: ["ConcurrencyContext"]), .executable(name: "loggingTracer", targets: ["LoggingTracer"]), ], @@ -85,6 +86,9 @@ let package = Package( .target(name: "PersistenceExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Persistence"), + .target(name: "Contrib", + dependencies: ["OpenTelemetryApi", + "OpenTelemetrySdk"]), .testTarget(name: "OTelSwiftLogTests", dependencies: ["OTelSwiftLog"], path: "Tests/BridgesTests/OTelSwiftLog"), @@ -117,6 +121,9 @@ let package = Package( .testTarget(name: "PersistenceExporterTests", dependencies: ["PersistenceExporter"], path: "Tests/ExportersTests/PersistenceExporter"), + .testTarget(name: "ContribTests", + dependencies: ["Contrib", + "InMemoryExporter"]), .executableTarget( name: "LoggingTracer", dependencies: ["OpenTelemetryApi"], @@ -295,7 +302,7 @@ if ProcessInfo.processInfo.environment["OTEL_ENABLE_SWIFTLINT"] != nil { package.dependencies.append(contentsOf: [ .package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.57.1") ]) - + for target in package.targets { target.plugins = [ .plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins"), diff --git a/Sources/Contrib/Processors/BaggageSpanProcessor.swift b/Sources/Contrib/Processors/BaggageSpanProcessor.swift new file mode 100644 index 00000000..27ec1731 --- /dev/null +++ b/Sources/Contrib/Processors/BaggageSpanProcessor.swift @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import Foundation +import OpenTelemetryApi +import OpenTelemetrySdk + +public struct BaggageSpanProcessor: SpanProcessor { + public let isStartRequired = true + public let isEndRequired = false + public let filter: (Entry) -> Bool + public var activeBaggage: () -> Baggage? = { + return OpenTelemetry.instance.contextProvider.activeBaggage + } + + public init(filter: @escaping (Entry) -> Bool) { + self.filter = filter + } + + public func onStart( + parentContext: SpanContext?, + span: any ReadableSpan + ) { + if let baggage = activeBaggage() { + let filteredEntries = baggage.getEntries().filter(self.filter) + for entry in filteredEntries { + span.setAttribute(key: entry.key.name, value: entry.value.string) + } + } + } + + public func onEnd(span: any ReadableSpan) {} + + public func shutdown(explicitTimeout: TimeInterval? = nil) {} + + public func forceFlush(timeout: TimeInterval? = nil) {} +} diff --git a/Tests/ContribTests/Processors/BaggageSpanProcessorTests.swift b/Tests/ContribTests/Processors/BaggageSpanProcessorTests.swift new file mode 100644 index 00000000..d003a5fb --- /dev/null +++ b/Tests/ContribTests/Processors/BaggageSpanProcessorTests.swift @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import InMemoryExporter +import OpenTelemetryApi +import OpenTelemetrySdk +import Contrib +import XCTest + +class BaggageSpanProcessorTests: XCTestCase { + func testProcessor() { + let processor = BaggageSpanProcessor(filter: { $0.key.name == "keepme" }) + let exporter = InMemoryExporter() + let simple = SimpleSpanProcessor(spanExporter: exporter) + OpenTelemetry.registerTracerProvider( + tracerProvider: TracerProviderBuilder().add(spanProcessor: processor) + .add(spanProcessor: simple).build() + ) + let tracer = OpenTelemetry.instance.tracerProvider.get( + instrumentationName: "test", + instrumentationVersion: "1.0.0" + ) + + guard let key = EntryKey(name: "test-key") else { + XCTFail() + return + } + + guard let keep = EntryKey(name: "keepme") else { + XCTFail("cannot create entry key") + return + } + + guard let value = EntryValue(string: "test-value") else { + XCTFail() + return + } + + let parent = tracer.spanBuilder(spanName: "parent").startSpan() + + // create two baggage items, one we will keep and one will + // be filtered out by the processor + let b = OpenTelemetry.instance.baggageManager.baggageBuilder() + .put(key: key, value: value, metadata: nil) + .put(key: keep, value: value, metadata: nil) + .build() + OpenTelemetry.instance.contextProvider.setActiveBaggage(b) + + let child = tracer.spanBuilder(spanName: "child").startSpan() + + child.end() + parent.end() + + simple.forceFlush() + + let spans = exporter.getFinishedSpanItems() + XCTAssertEqual(spans.count, 2) + + guard let pChild = spans.first(where: { $0.name == "child" }) else { + XCTFail("failed to find child span") + return + } + + XCTAssertTrue(spans.contains(where: { $0.name == "parent" })) + + XCTAssertEqual(pChild.attributes.count, 1) + + guard let attr = pChild.attributes.first else { + XCTFail("failed to get span attributes") + return + } + + XCTAssertEqual(attr.key, "keepme") + XCTAssertEqual(attr.value.description, "test-value") + } +}