Skip to content

Commit 6173049

Browse files
authored
+span additional withSpan to ease entering async world from non-async world (#64)
* +span additional withSpan to ease entering async world from non-async world * add test * fix API, always require Baggage, people should pass .topLevel
1 parent f0d6746 commit 6173049

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

Sources/Tracing/Tracer.swift

+31
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,36 @@ extension Tracer {
150150
throw error // rethrow
151151
}
152152
}
153+
154+
/// Execute the given async operation within a newly created `Span`,
155+
/// started as a child of the passed in `Baggage` or as a root span if `nil`.
156+
///
157+
/// DO NOT `end()` the passed in span manually. It will be ended automatically when the `operation` returns.
158+
///
159+
/// - Parameters:
160+
/// - operationName: The name of the operation being traced. This may be a handler function, database call, ...
161+
/// - baggage: The baggage to be used for the newly created span. It may be obtained by the user manually from the `Baggage.current`,
162+
// task local and modified before passing into this function. The baggage will be made the current task-local baggage for the duration of the `operation`.
163+
/// - kind: The `SpanKind` of the `Span` to be created. Defaults to `.internal`.
164+
/// - operation: operation to wrap in a span start/end and execute immediately
165+
/// - Returns: the value returned by `operation`
166+
/// - Throws: the error the `operation` has thrown (if any)
167+
public func withSpan<T>(
168+
_ operationName: String,
169+
baggage: Baggage,
170+
ofKind kind: SpanKind = .internal,
171+
_ operation: (Span) async throws -> T
172+
) async rethrows -> T {
173+
let span = self.startSpan(operationName, baggage: baggage, ofKind: kind)
174+
defer { span.end() }
175+
do {
176+
return try await Baggage.$current.withValue(span.baggage) {
177+
try await operation(span)
178+
}
179+
} catch {
180+
span.recordError(error)
181+
throw error // rethrow
182+
}
183+
}
153184
}
154185
#endif

Tests/TracingTests/TracerTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extension TracerTests {
3333
("testWithSpan_automaticBaggagePropagation_sync", testWithSpan_automaticBaggagePropagation_sync),
3434
("testWithSpan_automaticBaggagePropagation_sync_throws", testWithSpan_automaticBaggagePropagation_sync_throws),
3535
("testWithSpan_automaticBaggagePropagation_async", testWithSpan_automaticBaggagePropagation_async),
36+
("testWithSpan_enterFromNonAsyncCode_passBaggage_asyncOperation", testWithSpan_enterFromNonAsyncCode_passBaggage_asyncOperation),
3637
("testWithSpan_automaticBaggagePropagation_async_throws", testWithSpan_automaticBaggagePropagation_async_throws),
3738
]
3839
}

Tests/TracingTests/TracerTests.swift

+34
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,40 @@ final class TracerTests: XCTestCase {
187187
#endif
188188
}
189189

190+
func testWithSpan_enterFromNonAsyncCode_passBaggage_asyncOperation() throws {
191+
#if swift(>=5.5) && canImport(_Concurrency)
192+
guard #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) else {
193+
throw XCTSkip("Task locals are not supported on this platform.")
194+
}
195+
196+
let tracer = TestTracer()
197+
InstrumentationSystem.bootstrapInternal(tracer)
198+
defer {
199+
InstrumentationSystem.bootstrapInternal(nil)
200+
}
201+
202+
var spanEnded = false
203+
tracer.onEndSpan = { _ in spanEnded = true }
204+
205+
func operation(span: Span) async -> String {
206+
"world"
207+
}
208+
209+
self.testAsync {
210+
var fromNonAsyncWorld = Baggage.topLevel
211+
fromNonAsyncWorld.traceID = "1234-5678"
212+
let value = await tracer.withSpan("hello", baggage: fromNonAsyncWorld) { (span: Span) -> String in
213+
XCTAssertEqual(span.baggage.traceID, Baggage.current?.traceID)
214+
XCTAssertEqual(span.baggage.traceID, fromNonAsyncWorld.traceID)
215+
return await operation(span: span)
216+
}
217+
218+
XCTAssertEqual(value, "world")
219+
XCTAssertTrue(spanEnded)
220+
}
221+
#endif
222+
}
223+
190224
func testWithSpan_automaticBaggagePropagation_async_throws() throws {
191225
#if swift(>=5.5) && canImport(_Concurrency)
192226
guard #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) else {

0 commit comments

Comments
 (0)