-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add X-Ray specific MetadataProvider. (#14)
* Add X-Ray specific MetadataProvider. * Added MetadataProvider unit tests.
- Loading branch information
1 parent
11bfb3a
commit 2a15f21
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,42 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift OpenTelemetry open source project | ||
// | ||
// Copyright (c) 2021 Moritz Lang and the Swift OpenTelemetry project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Logging | ||
import ServiceContextModule | ||
|
||
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) | ||
extension Logger.MetadataProvider { | ||
/// A metadata provider exposing the current trace and span ID using X-Ray specific formatting. | ||
/// | ||
/// - Parameters: | ||
/// - traceIDKey: The metadata key of the trace ID. Defaults to "trace-id". | ||
/// - spanIDKey: The metadata key of the span ID. Defaults to "span-id". | ||
/// - Returns: A metadata provider ready to use with Logging. | ||
public static func otelXRay(traceIDKey: String = "trace-id", spanIDKey: String = "span-id") -> Logger.MetadataProvider { | ||
.init { | ||
guard let spanContext = ServiceContext.current?.spanContext else { return [:] } | ||
|
||
let traceIDBytes = spanContext.traceID.hexBytes | ||
let timestampBytes = traceIDBytes[0 ..< 8] | ||
let randomBytes = traceIDBytes[8...] | ||
|
||
return [ | ||
traceIDKey: "1-\(String(decoding: timestampBytes, as: UTF8.self))-\(String(decoding: randomBytes, as: UTF8.self))", | ||
spanIDKey: "\(spanContext.spanID)", | ||
] | ||
} | ||
} | ||
|
||
/// A metadata provider exposing the current trace and span ID using X-Ray specific formatting. | ||
public static let otelXRay = Logger.MetadataProvider.otelXRay() | ||
} |
130 changes: 130 additions & 0 deletions
130
Tests/OpenTelemetryXRayTests/MetadataProviderTests.swift
This file contains 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,130 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift OpenTelemetry open source project | ||
// | ||
// Copyright (c) 2021 Moritz Lang and the Swift OpenTelemetry project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
@testable import Logging | ||
@testable import OpenTelemetryXRay | ||
@testable import OpenTelemetry | ||
import ServiceContextModule | ||
import XCTest | ||
|
||
final class MetadataProviderTests: XCTestCase { | ||
func test_providesMetadataFromSpanContext_withDefaultLabels() throws { | ||
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { | ||
throw XCTSkip("Task locals are not supported on this platform.") | ||
} | ||
|
||
let stream = InterceptingStream() | ||
var logger = Logger(label: "test") | ||
logger.handler = StreamLogHandler(label: "test", stream: stream, metadataProvider: .otelXRay) | ||
|
||
var generator = XRayIDGenerator() | ||
|
||
let spanContext = OTel.SpanContext( | ||
traceID: generator.generateTraceID(), | ||
spanID: generator.generateSpanID(), | ||
traceFlags: .sampled, | ||
isRemote: true | ||
) | ||
|
||
var context = ServiceContext.topLevel | ||
context.spanContext = spanContext | ||
ServiceContext.$current.withValue(context) { | ||
logger.info("This is a test message", metadata: ["explicit": "42"]) | ||
} | ||
|
||
XCTAssertEqual(stream.strings.count, 1) | ||
let message = try XCTUnwrap(stream.strings.first) | ||
|
||
let traceIDBytes = spanContext.traceID.hexBytes | ||
let timestampBytes = traceIDBytes[0 ..< 8] | ||
let randomBytes = traceIDBytes[8...] | ||
let expectedTraceId = "1-\(String(decoding: timestampBytes, as: UTF8.self))-\(String(decoding: randomBytes, as: UTF8.self))" | ||
|
||
XCTAssertTrue(message.contains("span-id=\(spanContext.spanID)")) | ||
XCTAssertTrue(message.contains("trace-id=\(expectedTraceId)")) | ||
XCTAssertTrue(message.contains("explicit=42")) | ||
XCTAssertTrue(message.contains("This is a test message")) | ||
} | ||
|
||
func test_providesMetadataFromSpanContext_withCustomLabels() throws { | ||
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { | ||
throw XCTSkip("Task locals are not supported on this platform.") | ||
} | ||
|
||
let stream = InterceptingStream() | ||
var logger = Logger(label: "test") | ||
let metadataProvider = Logger.MetadataProvider.otelXRay(traceIDKey: "custom_trace_id", spanIDKey: "custom_span_id") | ||
logger.handler = StreamLogHandler(label: "test", stream: stream, metadataProvider: metadataProvider) | ||
|
||
var generator = XRayIDGenerator() | ||
|
||
let spanContext = OTel.SpanContext( | ||
traceID: generator.generateTraceID(), | ||
spanID: generator.generateSpanID(), | ||
traceFlags: .sampled, | ||
isRemote: true | ||
) | ||
|
||
var context = ServiceContext.topLevel | ||
context.spanContext = spanContext | ||
ServiceContext.$current.withValue(context) { | ||
logger.info("This is a test message", metadata: ["explicit": "42"]) | ||
} | ||
|
||
XCTAssertEqual(stream.strings.count, 1) | ||
let message = try XCTUnwrap(stream.strings.first) | ||
|
||
let traceIDBytes = spanContext.traceID.hexBytes | ||
let timestampBytes = traceIDBytes[0 ..< 8] | ||
let randomBytes = traceIDBytes[8...] | ||
let expectedTraceId = "1-\(String(decoding: timestampBytes, as: UTF8.self))-\(String(decoding: randomBytes, as: UTF8.self))" | ||
|
||
XCTAssertTrue(message.contains("custom_span_id=\(spanContext.spanID)")) | ||
XCTAssertTrue(message.contains("custom_trace_id=\(expectedTraceId)")) | ||
XCTAssertTrue(message.contains("explicit=42")) | ||
XCTAssertTrue(message.contains("This is a test message")) | ||
} | ||
|
||
func test_doesNotProvideMetadataWithoutSpanContext() throws { | ||
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { | ||
throw XCTSkip("Task locals are not supported on this platform.") | ||
} | ||
|
||
let stream = InterceptingStream() | ||
var logger = Logger(label: "test") | ||
let metadataProvider = Logger.MetadataProvider.otelXRay | ||
logger.handler = StreamLogHandler(label: "test", stream: stream, metadataProvider: metadataProvider) | ||
|
||
logger.info("This is a test message", metadata: ["explicit": "42"]) | ||
|
||
XCTAssertEqual(stream.strings.count, 1) | ||
let message = try XCTUnwrap(stream.strings.first) | ||
|
||
XCTAssertFalse(message.contains("trace-id")) | ||
XCTAssertFalse(message.contains("span-id")) | ||
XCTAssertTrue(message.contains("explicit=42")) | ||
XCTAssertTrue(message.contains("This is a test message")) | ||
} | ||
} | ||
|
||
final class InterceptingStream: TextOutputStream { | ||
var interceptedText: String? | ||
var strings = [String]() | ||
|
||
func write(_ string: String) { | ||
strings.append(string) | ||
interceptedText = (interceptedText ?? "") + string | ||
} | ||
} | ||
|
||
extension InterceptingStream: @unchecked Sendable {} |