From 5dbdb4f439815d5353e9d9893cbaacc09c5f22ac Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 26 May 2023 22:02:18 +0200 Subject: [PATCH 1/2] Adopt ServiceContext 1.0 **Motivation:** The baggage type ended up more useful than anticipated and we're renaming it to ServiceContext for the 1.0 release. Many libraries and production systems and up passing context around which may not necessarily be related to tracing, which made us make this naming change. Functionally ServiceContext and Baggage are the same, but the way we talk about context propagation in general makes more sense if we call this a context stype. **Modifications:** Use the new type; The `Baggage` still exists in the `InstrumentationBaggage` module so people can still use it if they wanted to but it is a TYPEALIAS to the ServiceContext. --- Docs/SwiftDistributedTracing/empty.swift | 2 +- .../run-nio-alloc-counter-tests.sh | 2 +- .../test_01_resources/shared.swift | 2 +- ...001_pass_around_static_strings_small.swift | 2 +- ...002_pass_around_static_strings_large.swift | 2 +- Package.swift | 14 +- README.md | 563 +----------------- .../Sources/Onboarding/Clock+Extensions.swift | 4 +- .../Dinner/Sources/Onboarding/Dinner.swift | 85 ++- Samples/Dinner/Sources/Onboarding/Model.swift | 6 +- Samples/Dinner/Sources/Onboarding/main.swift | 7 +- Sources/Instrumentation/Instrument.swift | 16 +- .../InstrumentationSystem.swift | 8 +- .../Instrumentation/MultiplexInstrument.swift | 14 +- Sources/Instrumentation/NoOpInstrument.swift | 6 +- .../Docs.docc/Guides/ImplementATracer.md | 124 ++-- .../Docs.docc/Guides/InstrumentYourLibrary.md | 106 ++-- .../Docs.docc/Guides/TraceYourApplication.md | 20 +- Sources/Tracing/Docs.docc/Tracer.md | 4 +- Sources/Tracing/NoOpTracer.swift | 22 +- Sources/Tracing/SpanProtocol.swift | 26 +- Sources/Tracing/Tracer.swift | 130 ++-- Sources/Tracing/TracerProtocol+Legacy.swift | 156 ++--- Sources/Tracing/TracerProtocol.swift | 92 +-- Sources/Tracing/TracingTime.swift | 2 +- .../SpanAttributesDSLBenchmark.swift | 6 +- .../InstrumentTests.swift | 32 +- .../InstrumentationSystemTests.swift | 10 +- .../ActorTracingTests.swift | 2 +- .../DynamicTracepointTracerTests.swift | 50 +- .../SpanTests.swift | 28 +- .../TestTracer.swift | 30 +- .../TracedLock.swift | 14 +- .../TracedLockTests.swift | 34 +- .../TracerTests+swift57.swift | 16 +- .../TracerTests.swift | 60 +- .../TracerTimeTests.swift | 0 .../TracingInstrumentationSystemTests.swift | 0 Tests/TracingTests/LegacyBaggageTests.swift | 32 + scripts/validate_license_headers.sh | 2 +- 40 files changed, 618 insertions(+), 1113 deletions(-) rename Tests/{TracingTests => TracingLegacyTests}/ActorTracingTests.swift (96%) rename Tests/{TracingTests => TracingLegacyTests}/DynamicTracepointTracerTests.swift (87%) rename Tests/{TracingTests => TracingLegacyTests}/SpanTests.swift (94%) rename Tests/{TracingTests => TracingLegacyTests}/TestTracer.swift (86%) rename Tests/{TracingTests => TracingLegacyTests}/TracedLock.swift (76%) rename Tests/{TracingTests => TracingLegacyTests}/TracedLockTests.swift (85%) rename Tests/{TracingTests => TracingLegacyTests}/TracerTests+swift57.swift (88%) rename Tests/{TracingTests => TracingLegacyTests}/TracerTests.swift (87%) rename Tests/{TracingTests => TracingLegacyTests}/TracerTimeTests.swift (100%) rename Tests/{TracingTests => TracingLegacyTests}/TracingInstrumentationSystemTests.swift (100%) create mode 100644 Tests/TracingTests/LegacyBaggageTests.swift diff --git a/Docs/SwiftDistributedTracing/empty.swift b/Docs/SwiftDistributedTracing/empty.swift index 2f2036f..10626c5 100644 --- a/Docs/SwiftDistributedTracing/empty.swift +++ b/Docs/SwiftDistributedTracing/empty.swift @@ -16,4 +16,4 @@ // This module is left purposefully empty of any source files, as it serves // only as a "landing page" for the documentation. This is in-place until docc // gains the ability to support package-wide documentation. -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // \ No newline at end of file +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // diff --git a/IntegrationTests/tests_01_performance/test_01_resources/run-nio-alloc-counter-tests.sh b/IntegrationTests/tests_01_performance/test_01_resources/run-nio-alloc-counter-tests.sh index 5e79cb1..277f661 100755 --- a/IntegrationTests/tests_01_performance/test_01_resources/run-nio-alloc-counter-tests.sh +++ b/IntegrationTests/tests_01_performance/test_01_resources/run-nio-alloc-counter-tests.sh @@ -53,7 +53,7 @@ fi "$here/../../allocation-counter-tests-framework/run-allocation-counter.sh" \ -p "$here/../../.." \ - -m Baggage \ + -m ServiceContext \ -m Instrumentation \ -m NIOInstrumentation \ -s "$here/shared.swift" \ diff --git a/IntegrationTests/tests_01_performance/test_01_resources/shared.swift b/IntegrationTests/tests_01_performance/test_01_resources/shared.swift index f17f884..b22cf3d 100644 --- a/IntegrationTests/tests_01_performance/test_01_resources/shared.swift +++ b/IntegrationTests/tests_01_performance/test_01_resources/shared.swift @@ -27,7 +27,7 @@ //===----------------------------------------------------------------------===// import Foundation -import InstrumentationBaggage +import ServiceContextModule @inline(never) func take1(context: BaggageContext) -> Int { diff --git a/IntegrationTests/tests_01_performance/test_01_resources/test_001_pass_around_static_strings_small.swift b/IntegrationTests/tests_01_performance/test_01_resources/test_001_pass_around_static_strings_small.swift index 013bb4a..04fcf09 100644 --- a/IntegrationTests/tests_01_performance/test_01_resources/test_001_pass_around_static_strings_small.swift +++ b/IntegrationTests/tests_01_performance/test_01_resources/test_001_pass_around_static_strings_small.swift @@ -26,7 +26,7 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule func run(identifier: String) { measure(identifier: identifier) { diff --git a/IntegrationTests/tests_01_performance/test_01_resources/test_002_pass_around_static_strings_large.swift b/IntegrationTests/tests_01_performance/test_01_resources/test_002_pass_around_static_strings_large.swift index 148dd8b..60fd51e 100644 --- a/IntegrationTests/tests_01_performance/test_01_resources/test_002_pass_around_static_strings_large.swift +++ b/IntegrationTests/tests_01_performance/test_01_resources/test_002_pass_around_static_strings_large.swift @@ -26,7 +26,7 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule func run(identifier: String) { measure(identifier: identifier) { diff --git a/Package.swift b/Package.swift index 435c511..7ded2da 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,8 @@ let package = Package( .library(name: "Tracing", targets: ["Tracing"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-distributed-tracing-baggage.git", .upToNextMinor(from: "0.4.1")), + // .package(url: "https://github.com/apple/swift-service-context.git", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-service-context.git", branch: "main"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), ], targets: [ @@ -24,7 +25,7 @@ let package = Package( .target( name: "Instrumentation", dependencies: [ - .product(name: "InstrumentationBaggage", package: "swift-distributed-tracing-baggage"), + .product(name: "ServiceContextModule", package: "swift-service-context"), ] ), .testTarget( @@ -49,6 +50,13 @@ let package = Package( .target(name: "Tracing"), ] ), + .testTarget( + name: "TracingLegacyTests", + dependencies: [ + .target(name: "Tracing"), + .product(name: "InstrumentationBaggage", package: "swift-service-context"), + ] + ), // ==== -------------------------------------------------------------------------------------------------------- // MARK: Performance / Benchmarks @@ -56,7 +64,7 @@ let package = Package( .executableTarget( name: "_TracingBenchmarks", dependencies: [ - .product(name: "InstrumentationBaggage", package: "swift-distributed-tracing-baggage"), + .product(name: "ServiceContextModule", package: "swift-service-context"), .target(name: "Tracing"), .target(name: "_TracingBenchmarkTools"), ] diff --git a/README.md b/README.md index 00330cc..dd122be 100644 --- a/README.md +++ b/README.md @@ -6,43 +6,11 @@ This is a collection of Swift libraries enabling the instrumentation of server s While Swift Distributed Tracing allows building all kinds of _instruments_, which can co-exist in applications transparently, its primary use is instrumenting multi-threaded and distributed systems with Distributed Traces. -> Warning: The docs below, showcasing the 0.3.x series of the logging integration are **deprecated** thanks to the latest inclusion of [metadata providers in swift-log](https://github.com/apple/swift-log/pull/238). With the introduction of [task local values in Swift](https://developer.apple.com/documentation/swift/tasklocal), and metadata providers in swift-log, the `LoggingContext` pattern showcased below has become an _anti-pattern_. Please give us a moment to finish the [new documentation PR #69](https://github.com/apple/swift-distributed-tracing/pull/69), which will explain the new integration style in detail. -> -> Tracer APIs will not change substantially, as we're closing up on announcing version 1.0. Please look forward to beta releases very soon! - ---- - -This project uses the context progagation type defined independently in: - -- 🧳 [swift-distributed-tracing-baggage](https://github.com/apple/swift-distributed-tracing-baggage) -- [`Baggage`](https://apple.github.io/swift-distributed-tracing-baggage/docs/current/InstrumentationBaggage/Structs/Baggage.html) (zero dependencies) - --- -## Table of Contents +This project uses the context propagation type defined independently in: -* [Compatibility](#compatibility) - + [Tracing Backends](#tracing-backends) - + [Libraries & Frameworks](#libraries--frameworks) -* [Getting Started](#getting-started) - + [Dependencies & Tracer backend](#dependencies--tracer-backend) - + [Benefiting from instrumented libraries/frameworks](#benefiting-from-instrumented-libraries-frameworks) - + [Instrumenting your code](#instrumenting-your-code) - + [More examples](#more-examples) -* [In-Depth Guide](#in-depth-guide) -* In-Depth Guide for **Application Developers** - + [Setting up instruments & tracers](#setting-up-instruments--tracers) - + [Bootstrapping the InstrumentationSystem](#bootstrapping-the-instrumentationsystem) - + [Context propagation](#passing-context-objects) - + [Creating context objects](#creating-context-objects--and-when-not-to-do-so-) - + [Working with `Span`s](#spans) -* In-Depth Guide for: **Library/Framework developers** - + [Instrumenting your software](#library-framework-developers--instrumenting-your-software) - + [Extracting & injecting Baggage](#extracting--injecting-baggage) - + [Tracing your library](#tracing-your-library) -* In-Depth Guide for: **Instrument developers** - + [Creating an `Instrument`](#instrument-developers--creating-an-instrument) - + [Creating a `Tracer`](#creating-a--tracer-) -* [Contributing](#contributing) +- [swift-service-context](https://github.com/apple/swift-service-context) -- [`ServiceContext`](https://swiftpackageindex.com/apple/swift-service-context/main/documentation/servicecontextmodule/servicecontext) (zero dependencies) --- @@ -57,11 +25,11 @@ The purpose of the tracing package is to serve as common API for all tracer and Compatible `Tracer` implementations: -| Library | Status | Description | -| ------- | ------ | ----------- | -| [@slashmo](https://github.com/slashmo) / [**OpenTelemetry** Swift](https://github.com/slashmo/opentelemetry-swift) | Complete | Exports spans to OpenTelemetry Collector; **X-Ray** & **Jaeger** propagation available via extensions. | -| [@pokrywka](https://github.com/pokryfka) / [AWS **xRay** SDK Swift](https://github.com/pokryfka/aws-xray-sdk-swift) | Complete (?) | ... | -| _Your library?_ | ... | [Get in touch!](https://forums.swift.org/c/server/43) | +| Library | Status | Description | +| ------- |----------------------------| ----------- | +| [@slashmo](https://github.com/slashmo) / [**OpenTelemetry** Swift](https://github.com/slashmo/opentelemetry-swift) | Update for 1.0 in progress | Exports spans to OpenTelemetry Collector; **X-Ray** & **Jaeger** propagation available via extensions. | +| [@pokrywka](https://github.com/pokryfka) / [AWS **xRay** SDK Swift](https://github.com/pokryfka/aws-xray-sdk-swift) | Not updated for 1.0 | ... | +| _Your library?_ | ... | [Get in touch!](https://forums.swift.org/c/server/43) | If you know of any other library please send in a [pull request](https://github.com/apple/swift-distributed-tracing/compare) to add it to the list, thank you! @@ -69,522 +37,21 @@ If you know of any other library please send in a [pull request](https://github. As this API package was just released, no projects have yet fully adopted it, the following table for not serves as reference to prior work in adopting tracing work. As projects move to adopt tracing completely, the table will be used to track adoption phases of the various libraries. -| Library | Integrates | Status | -| ------- | ---------- | ------ | -| AsyncHTTPClient | Tracing | Old* [Proof of Concept PR](https://github.com/swift-server/async-http-client/pull/289) | -| Swift gRPC | Tracing | Old* [Proof of Concept PR](https://github.com/grpc/grpc-swift/pull/941) | -| Swift AWS Lambda Runtime | Tracing | Old* [Proof of Concept PR](https://github.com/swift-server/swift-aws-lambda-runtime/pull/167) | -| Swift NIO | Baggage | Old* [Proof of Concept PR](https://github.com/apple/swift-nio/pull/1574) | -| RediStack (Redis) | Tracing | Signalled intent to adopt tracing. | -| Soto AWS Client | Tracing | Signalled intent to adopt tracing. | -| _Your library?_ | ... | [Get in touch!](https://forums.swift.org/c/server/43) | - -> `*` Note that this package was initially developed as a Google Summer of Code project, during which a number of Proof of Concept PR were opened to a number of projects. -> -> These projects are likely to adopt the, now official, Swift Distributed Tracing package in the shape as previewed in those PRs, however they will need updating. Please give the library developers time to adopt the new APIs (or help them by submitting a PR doing so!). +| Library | Integrates | Status | +|--------------------------|----------------|-------------------------------------------------------| +| AsyncHTTPClient | Tracing | Pending | +| Swift gRPC | Tracing | Pending | +| _Your library?_ | ... | [Get in touch!](https://forums.swift.org/c/server/43) | If you know of any other library please send in a [pull request](https://github.com/apple/swift-distributed-tracing/compare) to add it to the list, thank you! --- -## Getting Started - -In this short getting started example, we'll go through bootstrapping, immediately benefiting from tracing, and instrumenting our own synchronous and asynchronous APIs. The following sections will explain all the pieces of the API in more depth. When in doubt, you may want to refer to the [OpenTelemetry](https://opentelemetry.io), [Zipkin](https://zipkin.io), or [Jaeger](https://www.jaegertracing.io) documentations because all the concepts for different tracers are quite similar. - -### Dependencies & Tracer backend - -In order to use tracing you will need to bootstrap a tracing backend ([available backends](#backends)). - -When developing an *application* locate the specific tracer library you would like to use and add it as an dependency directly: - -```swift -.package(url: "", // the specific tracer - ] -), -``` - -Then (in an application, libraries should _never_ invoke `bootstrap`), you will want to bootstrap the specific tracer you want to use in your application. A `Tracer` is a type of `Instrument` and can be offered used to globally bootstrap the tracing system, like this: - - -```swift -import Tracing // the tracing API -import AwesomeTracing // the specific tracer - -InstrumentationSystem.bootstrap(AwesomeTracing()) -``` - -If you don't bootstrap (or other instrument) the default no-op tracer is used, which will result in no trace data being collected. - -### Benefiting from instrumented libraries/frameworks - -**Automatically reported spans**: When using an already instrumented library, e.g. an HTTP Server which automatically emits spans internally, this is all you have to do to enable tracing. It should now automatically record and emit spans using your configured backend. - -**Using baggage and logging context**: The primary transport type for tracing metadata is called `Baggage`, and the primary type used to pass around baggage context and loggers is `LoggingContext`. Logging context combines baggage context values with a smart `Logger` that automatically includes any baggage values ("trace metadata") when it is used for logging. For example, when using an instrumented HTTP server, the API could look like this: - -```swift -SomeHTTPLibrary.handle { (request, context) in - context.logger.info("Wow, tracing!") // automatically includes tracing metadata such as "trace-id" - return try doSomething(request context: context) -} -``` - -In this snippet, we use the context logger to log a very useful message. However it is even more useful than it seems at first sight: if a tracer was installed and extracted tracing information from the incoming request, it would automatically log our message _with_ the trace information, allowing us to co-relate all log statements made during handling of this specific request: - -``` -05:46:38 example-trace-id=1111-23-1234556 info: Wow tracing! -05:46:38 example-trace-id=9999-22-9879797 info: Wow tracing! -05:46:38 example-trace-id=9999-22-9879797 user=Alice info: doSomething() for user Alice -05:46:38 example-trace-id=1111-23-1234556 user=Charlie info: doSomething() for user Charlie -05:46:38 example-trace-id=1111-23-1234556 user=Charlie error: doSomething() could not complete request! -05:46:38 example-trace-id=9999-22-9879797 user=alice info: doSomething() completed -``` - -Thanks to tracing, and trace identifiers, even if not using tracing visualization libraries, we can immediately co-relate log statements and know that the request `1111-23-1234556` has failed. Since our application can also _add_ values to the context, we can quickly notice that the error seems to occur for the user `Charlie` and not for user `Alice`. Perhaps the user Charlie has exceeded some quotas, does not have permissions or we have a bug in parsing names that include the letter `h`? We don't know _yet_, but thanks to tracing we can much quicker begin our investigation. - -**Passing context to client libraries**: When using client libraries that support distributed tracing, they will accept a `Baggage.LoggingContext` type as their _last_ parameter in many calls. - -When using client libraries that support distributed tracing, they will accept a `Baggage.LoggingContext` type as their _last_ parameter in many calls. Please refer to [Context argument naming/positioning](#context-propagation-by-explicit-loggingcontext-passing) in the [Context propagation](#context-propagation-by-explicit-loggingcontext-passing) section of this readme to learn more about how to properly pass context values around. - -### Instrumenting your code - -Adding a span to synchronous functions can be achieved like this: - -```swift -func handleRequest(_ op: String, context: LoggingContext) -> String { - let tracer = InstrumentationSystem.tracer - let span = tracer.startSpan(operationName: "handleRequest(\(name))", context: context) - defer { span.end() } - - return "done:\(op)" -} -``` - -Throwing can be handled by either recording errors manually into a span by calling `span.recordError(error:)`, or by wrapping a potentially throwing operation using the `withSpan(operation:context:body:)` function, which automatically records any thrown error and ends the span at the end of the body closure scope: - -```swift -func handleRequest(_ op: String, context: LoggingContext) -> String { - return try InstrumentationSystem.tracer - .withSpan(operationName: "handleRequest(\(name))", context: context) { - return try dangerousOperation() - } -} -``` - -If this function were asynchronous, and returning a [Swift NIO](https://github.com/apple/swift-nio) `EventLoopFuture`, -we need to end the span when the future completes. We can do so in its `onComplete`: - -```swift -func handleRequest(_ op: String, context: LoggingContext) -> EventLoopFuture { - let tracer = InstrumentationSystem.tracer - let span = tracer.startSpan(operationName: "handleRequest(\(name))", context: context) - - let future: EventLoopFuture = someOperation(op) - future.whenComplete { _ in - span.end() // oh no, ignored errors! - } - - return future -} -``` - -This is better, however we ignored the possibility that the future perhaps has failed. If this happens, we would like to report the span as _errored_ because then it will show up as such in tracing backends and we can then easily search for failed operations etc. - -To do this within the future we could manually invoke the `span.recordError` API before ending the span like this: - -```swift -func handleRequest(_ op: String, context: LoggingContext) -> EventLoopFuture { - let tracer = InstrumentationSystem.tracer - let span = tracer.startSpan(operationName: "handleRequest(\(name))", context: context) - - let future: EventLoopFuture = someOperation(op) - future.whenComplete { result in - switch result { - case .failure(let error): span.recordError(error) - case .success(let value): // ... record additional *attributes* into the span - } - span.end() - } - - return future -} -``` - -While this is verbose, this is only the low-level building blocks that this library provides, higher level helper utilities can be - -> Eventually convenience wrappers will be provided, automatically wrapping future types etc. We welcome such contributions, but likely they should live in `swift-distributed-tracing-extras`. - -Once a system, or multiple systems have been instrumented, a Tracer has been selected and your application runs and emits some trace information, you will be able to inspect how your application is behaving by looking at one of the various trace UIs, such as e.g. Zipkin: - -![Simple example trace in Zipkin Web UI](Sources/Tracing/Docs.docc/Resources/zipkin_trace.png) - -### More examples - -It sometimes is easier to grasp the usage of tracing by looking at a "real" application - which is why we have implemented an example application, spanning multiple nodes and using various databases - tracing through all of them. You can view the example application here: [slashmo/swift-tracing-examples](https://github.com/slashmo/swift-tracing-examples/tree/main/hotrod). - -### Future work: Tracing asynchronous functions - -> ⚠️ This section refers to in-development upcoming Swift Concurrency features and can be tried out using nightly snapshots of the Swift toolchain. - -With Swift's ongoing work towards asynchronous functions, actors, and tasks, tracing in Swift will become more pleasant than it is today. - -Firstly, a lot of the callback heavy code will be folded into normal control flow, which is easy and correct to integrate with tracing like this: - -```swift -func perform(context: LoggingContext) async -> String { - let span = InstrumentationSystem.tracer.startSpan(operationName: #function, context: context) - defer { span.end() } - - return await someWork() -} -``` - - -## In-Depth Guide - -When instrumenting server applications there are typically three parties involved: - -1. [Application developers](#application-developers-setting-up-instruments) creating server-side applications -2. [Library/Framework developers](#libraryframework-developers-instrumenting-your-software) providing building blocks to create these applications -3. [Instrument developers](#instrument-developers-creating-an-instrument) providing tools to collect distributed metadata about your application - -For applications to be instrumented correctly these three parts have to play along nicely. - -## Application Developers - -### Setting up instruments & tracers - -As an end-user building server applications you get to choose what instruments to use to instrument your system. Here's -all the steps you need to take to get up and running: - -Add a package dependency for this repository in your `Package.swift` file, and one for the specific instrument you want -to use, in this case `FancyInstrument`: - -```swift -.package(url: "https://github.com/apple/swift-distributed-tracing.git", .branch("main")), -.package(url: "", from: "<4.2.0>"), -``` - -To your main target, add a dependency on the `Instrumentation library` and the instrument you want to use: - -```swift -.target( - name: "MyApplication", - dependencies: [ - "FancyInstrument" - ] -), -``` - -### Bootstrapping the `InstrumentationSystem` - -Instead of providing each instrumented library with a specific instrument explicitly, you *bootstrap* the -`InstrumentationSystem` which acts as a singleton that libraries/frameworks access when calling out to the configured -`Instrument`: - -```swift -InstrumentationSystem.bootstrap(FancyInstrument()) -``` - -#### Recommended bootstrap order - -Swift offers developers a suite of observability libraries: logging, metrics and tracing. Each of those systems offers a `bootstrap` function. It is useful to stick to a recommended boot order in order to achieve predictable initialization of applications and sub-systems. - -Specifically, it is recommended to bootstrap systems in the following order: - -1. [Swift Log](https://github.com/apple/swift-log#default-logger-behavior)'s `LoggingSystem` -2. [Swift Metrics](https://github.com/apple/swift-metrics#selecting-a-metrics-backend-implementation-applications-only)' `MetricsSystem` -3. Swift Tracing's `InstrumentationSystem` -4. Finally, any other parts of your application +## Reference Documentation -This is because tracing systems may attempt to emit metrics about their status etc. +Please refer to the **[reference documentation](https://swiftpackageindex.com/apple/swift-distributed-tracing/documentation/tracing)** for detailed guides about adopting distributed tracing in your applications, libraries and frameworks. -#### Bootstrapping multiple instruments using MultiplexInstrument - -It is important to note that `InstrumentationSystem.bootstrap(_: Instrument)` must only be called once. In case you -want to bootstrap the system to use multiple instruments, you group them in a `MultiplexInstrument` first, which you -then pass along to the `bootstrap` method like this: - -```swift -InstrumentationSystem.bootstrap(MultiplexInstrument([FancyInstrument(), OtherFancyInstrument()])) -``` - -`MultiplexInstrument` will then call out to each instrument it has been initialized with. - - - -### Context propagation, by explicit `LoggingContext` passing - -> `LoggingContext` naming has been carefully selected and it reflects the type's purpose and utility: It binds a [Swift Log `Logger`](https://github.com/apple/swift-log) with an associated distributed tracing [Baggage](https://github.com/apple/swift-distributed-tracing-baggage). -> -> It _also_ is used for tracing, by tracers reaching in to read or modify the carried baggage. - -For instrumentation and tracing to work, certain pieces of metadata (usually in the form of identifiers), must be -carried throughout the entire system–including across process and service boundaries. Because of that, it's essential -for a context object to be passed around your application and the libraries/frameworks you depend on, but also carried -over asynchronous boundaries like an HTTP call to another service of your app. - -`LoggingContext` should always be passed around explicitly. - -Libraries which support tracing are expected to accept a `LoggingContext` parameter, which can be passed through the entire application. Make sure to always pass along the context that's previously handed to you. E.g., when making an HTTP request using `AsyncHTTPClient` in a `NIO` handler, you can use the `ChannelHandlerContext`s `baggage` property to access the `LoggingContext`. - -#### Context argument naming/positioning - -> 💡 This general style recommendation has been ironed out together with the Swift standard library, core team, the SSWG as well as members of the community. Please respect these recommendations when designing APIs such that all APIs are able to "feel the same" yielding a great user experience for our end users ❤️ -> -> It is possible that the ongoing Swift Concurrency efforts, and "Task Local" values will resolve this explicit context passing problem, however until these arrive in the language, please adopt the "context is the last parameter" style as outlined here. - -Propagating baggage context through your system is to be done explicitly, meaning as a parameter in function calls, following the "flow" of execution. - -When passing baggage context explicitly we strongly suggest sticking to the following style guideline: - -- Assuming the general parameter ordering of Swift function is as follows (except DSL exceptions): - 1. Required non-function parameters (e.g. `(url: String)`), - 2. Defaulted non-function parameters (e.g. `(mode: Mode = .default)`), - 3. Required function parameters, including required trailing closures (e.g. `(onNext elementHandler: (Value) -> ())`), - 4. Defaulted function parameters, including optional trailing closures (e.g. `(onComplete completionHandler: (Reason) -> ()) = { _ in }`). -- Logging Context should be passed as **the last parameter in the required non-function parameters group in a function declaration**. - -This way when reading the call side, users of these APIs can learn to "ignore" or "skim over" the context parameter and the method signature remains human-readable and “Swifty”. - -Examples: - -- `func request(_ url: URL,` **`context: LoggingContext`** `)`, which may be called as `httpClient.request(url, context: context)` -- `func handle(_ request: RequestObject,` **`context: LoggingContext`** `)` - - if a "framework context" exists and _carries_ the baggage context already, it is permitted to pass that context - together with the baggage; - - it is _strongly recommended_ to store the baggage context as `baggage` property of `FrameworkContext`, and conform `FrameworkContext` to `LoggingContext` in such cases, in order to avoid the confusing spelling of `context.context`, and favoring the self-explanatory `context.baggage` spelling when the baggage is contained in a framework context object. -- `func receiveMessage(_ message: Message, context: FrameworkContext)` -- `func handle(element: Element,` **`context: LoggingContext`** `, settings: Settings? = nil)` - - before any defaulted non-function parameters -- `func handle(element: Element,` **`context: LoggingContext`** `, settings: Settings? = nil, onComplete: () -> ())` - - before defaulted parameters, which themselfes are before required function parameters -- `func handle(element: Element,` **`context: LoggingContext`** `, onError: (Error) -> (), onComplete: (() -> ())? = nil)` - -In case there are _multiple_ "framework-ish" parameters, such as passing a NIO `EventLoop` or similar, we suggest: - -- `func perform(_ work: Work, for user: User,` _`frameworkThing: Thing, eventLoop: NIO.EventLoop,`_ **`context: LoggingContext`** `)` - - pass the baggage as **last** of such non-domain specific parameters as it will be _by far more_ omnipresent than any - specific framework parameter - as it is expected that any framework should be accepting a context if it can do so. - While not all libraries are necessarily going to be implemented using the same frameworks. - -We feel it is important to preserve Swift's human-readable nature of function definitions. In other words, we intend to -keep the read-out-loud phrasing of methods to remain _"request that URL (ignore reading out loud the context parameter)"_ -rather than _"request (ignore this context parameter when reading) that URL"_. - -#### When to use what context type? - -Generally libraries should favor accepting the general `LoggingContext` type, and **not** attempt to wrap it, as it will result in difficult to compose APIs between multiple libraries. Because end users are likely going to be combining various libraries in a single application, it is important that they can "just pass along" the same context object through all APIs, regardless which other library they are calling into. - -Frameworks may need to be more opinionated here, and e.g. already have some form of "per request context" contextual object which they will conform to `LoggingContext`. _Within_ such framework it is fine and expected to accept and pass the explicit `SomeFrameworkContext`, however when designing APIs which may be called _by_ other libraries, such framework should be able to accept a generic `LoggingContext` rather than its own specific type. - -#### Existing context argument - -When adapting an existing library/framework to support `LoggingContext` and it already has a "framework context" which is expected to be passed through "everywhere", we suggest to follow these guidelines for adopting LoggingContext: - -1. Add a `Baggage` as a property called `baggage` to your own `context` type, so that the call side for your - users becomes `context.baggage` (rather than the confusing `context.context`) -2. If you cannot or it would not make sense to carry baggage inside your framework's context object, pass (and accept (!)) the `LoggingContext` in your framework functions like follows: -- if they take no framework context, accept a `context: LoggingContext` which is the same guideline as for all other cases -- if they already _must_ take a context object and you are out of words (or your API already accepts your framework context as "context"), pass the baggage as **last** parameter (see above) yet call the parameter `baggage` to disambiguate your `context` object from the `baggage` context object. - -Examples: - -- `Lambda.Context` may contain `baggage` and a `logger` and should be able to conform to `LoggingContext` - - passing context to a `Lambda.Context` unaware library becomes: `http.request(url: "...", context: context)`. -- `ChannelHandlerContext` offers a way to set/get baggage on the underlying channel via `context.baggage = ...` - - this context is not passed outside a handler, but within it may be passed as is, and the baggage may be accessed on it directly through it. - - Example: https://github.com/apple/swift-nio/pull/1574 - -### Creating context objects (and when not to do so) - -Generally application developers _should not_ create new context objects, but rather keep passing on a context value that they were given by e.g. the web framework invoking the their code. - -If really necessary, or for the purposes of testing, one can create a baggage or context using one of the two factory functions: - -- [`DefaultLoggingContext.topLevel(logger:)`](https://github.com/apple/swift-distributed-tracing-baggage/blob/main/Sources/Baggage/LoggingContext.swift) or [`Baggage.topLevel`](https://github.com/apple/swift-distributed-tracing-baggage-core/blob/main/Sources/CoreBaggage/Baggage.swift) - which creates an empty context/baggage, without any values. It should _not_ be used too frequently, and as the name implies in applications it only should be used on the "top level" of the application, or at the beginning of a contextless (e.g. timer triggered) event processing. -- [`DefaultLoggingContext.TODO(logger:reason:)`](https://github.com/apple/swift-distributed-tracing-baggage/blob/main/Sources/Baggage/LoggingContext.swift) or [`Baggage.TODO`](https://github.com/apple/swift-distributed-tracing-baggage-core/blob/main/Sources/CoreBaggage/Baggage.swift) - which should be used to mark a parameter where "before this code goes into production, a real context should be passed instead." An application can be run with `-DBAGGAGE_CRASH_TODOS` to cause the application to crash whenever a TODO context is still in use somewhere, making it easy to diagnose and avoid breaking context propagation by accidentally leaving in a `TODO` context in production. - -Please refer to the respective functions documentation for details. - -If using a framework which itself has a "`...Context`" object you may want to inspect it for similar factory functions, as `LoggingContext` is a protocol, that may be conformed to by frameworks to provide a smoother user experience. - - -### Working with `Span`s - -The primary purpose of this API is to start and end so-called `Span` types. - -Spans form hierarchies with their parent spans, and end up being visualized using various tools, usually in a format similar to gant charts. So for example, if we had multiple operations that compose making dinner, they would be modelled as child spans of a main `makeDinner` span. Any sub tasks are again modelled as child spans of any given operation, and so on, resulting in a trace view similar to: - -``` ->-o-o-o----- makeDinner ----------------o---------------x [15s] - \-|-|- chopVegetables--------x | [2s] - | | \- chop -x | | [1s] - | | \--- chop -x | [1s] - \-|- marinateMeat -----------x | [3s] - \- preheatOven -----------------x | [10s] - \--cook---------x [5s] -``` - -The above trace is achieved by starting and ending spans in all the mentioned functions, for example, like this: - -```swift -let tracer: any Tracer - -func makeDinner(context: LoggingContext) async throws -> Meal { - tracer.withSpan(operationName: "makeDinner", context) { - let veggiesFuture = try chopVegetables(context: span.context) - let meatFuture = marinateMeat(context: span.context) - let ovenFuture = try preheatOven(temperature: 350, context: span.context) - ... - return cook(veggies, meat, oven) - } -} -``` - -> ❗️ It is tremendously important to **always `end()` a started `Span`**! make sure to end any started span on _every_ code path, including error paths -> -> Failing to do so is an error, and a tracer *may* decide to either crash the application or log warnings when an not-ended span is deinitialized. - - -## Library/Framework developers: Instrumenting your software - -### Extracting & injecting Baggage - -When hitting boundaries like an outgoing HTTP request you call out to the [configured instrument(s)](#Bootstrapping-the-Instrumentation-System): - -An HTTP client e.g. should inject the given `LoggingContext` into the HTTP headers of its outbound request: - -```swift -func get(url: String, context: LoggingContext) { - var request = HTTPRequest(url: url) - InstrumentationSystem.instrument.inject( - context.baggage, - into: &request.headers, - using: HTTPHeadersInjector() - ) -} -``` - -On the receiving side, an HTTP server should use the following `Instrument` API to extract the HTTP headers of the given -`HTTPRequest` into: - -```swift -func handler(request: HTTPRequest, context: LoggingContext) { - InstrumentationSystem.instrument.extract( - request.headers, - into: &context.baggage, - using: HTTPHeadersExtractor() - ) - // ... -} -``` - -> In case your library makes use of the `NIOHTTP1.HTTPHeaders` type we already have an `HTTPHeadersInjector` & -`HTTPHeadersExtractor` available as part of the `NIOInstrumentation` library. - -For your library/framework to be able to carry `LoggingContext` across asynchronous boundaries, it's crucial that you carry the context throughout your entire call chain in order to avoid dropping metadata. - -### Tracing your library - -When your library/framework can benefit from tracing, you should make use of it by integrating the `Tracing` library. - -In order to work with the tracer [configured by the end-user](#Bootstrapping-the-Instrumentation-System), it adds a property to `InstrumentationSystem` that gives you back a `Tracer`. You can then use that tracer to start `Span`s. In an HTTP client you e.g. -should start a `Span` when sending the outgoing HTTP request: - -```swift -func get(url: String, context: LoggingContext) { - var request = HTTPRequest(url: url) - - // inject the request headers into the baggage as explained above - - // start a span for the outgoing request - let tracer = InstrumentationSystem.tracer - var span = tracer.startSpan(named: "HTTP GET", context: context, ofKind: .client) - - // set attributes on the span - span.attributes.http.method = "GET" - // ... - - self.execute(request).always { _ in - // set some more attributes & potentially record an error - - // end the span - span.end() - } -} -``` - -> ⚠️ Make sure to ALWAYS end spans. Ensure that all paths taken by the code will result in ending the span. -> Make sure that error cases also set the error attribute and end the span. - -> In the above example we used the semantic `http.method` attribute that gets exposed via the -`TracingOpenTelemetrySupport` library. - -## Instrument developers: Creating an instrument - -Creating an instrument means adopting the `Instrument` protocol (or `Tracer` in case you develop a tracer). -`Instrument` is part of the `Instrumentation` library & `Tracing` contains the `Tracer` protocol. - -`Instrument` has two requirements: - -1. A method to inject values inside a `LoggingContext` into a generic carrier (e.g. HTTP headers) -2. A method to extract values from a generic carrier (e.g. HTTP headers) and store them in a `LoggingContext` - -The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to -act on the provided information or to add additional information to be carried across these boundaries. - -> Check out the [`Baggage` documentation](https://github.com/apple/swift-distributed-tracing-baggage) for more information on -how to retrieve values from the `LoggingContext` and how to set values on it. - -### Creating a Tracer - -When creating a tracer you need to create two types: - -1. Your tracer conforming to `Tracer` -2. A span class conforming to `Span` - -> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it. - -### Defining, injecting and extracting Baggage - -```swift -import Tracing - -private enum TraceIDKey: BaggageKey { - typealias Value = String -} - -extension Baggage { - var traceID: String? { - get { - return self[TraceIDKey.self] - } - set { - self[TraceIDKey.self] = newValue - } - } -} - -var context = DefaultLoggingContext.topLevel(logger: ...) -context.baggage.traceID = "4bf92f3577b34da6a3ce929d0e0e4736" -print(context.baggage.traceID ?? "new trace id") -``` +--- ## Contributing diff --git a/Samples/Dinner/Sources/Onboarding/Clock+Extensions.swift b/Samples/Dinner/Sources/Onboarding/Clock+Extensions.swift index 55f8620..7d0b38c 100644 --- a/Samples/Dinner/Sources/Onboarding/Clock+Extensions.swift +++ b/Samples/Dinner/Sources/Onboarding/Clock+Extensions.swift @@ -25,5 +25,5 @@ //===----------------------------------------------------------------------===// func sleep(for duration: ContinuousClock.Duration) async { - try? await Task.sleep(until: ContinuousClock.now + duration, clock: .continuous) -} \ No newline at end of file + try? await Task.sleep(until: ContinuousClock.now + duration, clock: .continuous) +} diff --git a/Samples/Dinner/Sources/Onboarding/Dinner.swift b/Samples/Dinner/Sources/Onboarding/Dinner.swift index f52d641..537601a 100644 --- a/Samples/Dinner/Sources/Onboarding/Dinner.swift +++ b/Samples/Dinner/Sources/Onboarding/Dinner.swift @@ -24,67 +24,66 @@ // //===----------------------------------------------------------------------===// - import Tracing func makeDinner() async throws -> Meal { - try await InstrumentationSystem.tracer.withSpan("makeDinner") { _ in - await sleep(for: .milliseconds(200)) + try await InstrumentationSystem.tracer.withSpan("makeDinner") { _ in + await sleep(for: .milliseconds(200)) - async let veggies = try chopVegetables() - async let meat = marinateMeat() - async let oven = preheatOven(temperature: 350) - // ... - return try await cook(veggies, meat, oven) - } + async let veggies = try chopVegetables() + async let meat = marinateMeat() + async let oven = preheatOven(temperature: 350) + // ... + return try await cook(veggies, meat, oven) + } } func chopVegetables() async throws -> [Vegetable] { - try await otelChopping1.tracer().withSpan("chopVegetables") { _ in - // Chop the vegetables...! - // - // However, since chopping is a very difficult operation, - // one chopping task can be performed at the same time on a single service! - // (Imagine that... we cannot parallelize these two tasks, and need to involve another service). - async let carrot = try chop(.carrot, tracer: otelChopping1.tracer()) - async let potato = try chop(.potato, tracer: otelChopping2.tracer()) - return try await [carrot, potato] - } + try await otelChopping1.tracer().withSpan("chopVegetables") { _ in + // Chop the vegetables...! + // + // However, since chopping is a very difficult operation, + // one chopping task can be performed at the same time on a single service! + // (Imagine that... we cannot parallelize these two tasks, and need to involve another service). + async let carrot = try chop(.carrot, tracer: otelChopping1.tracer()) + async let potato = try chop(.potato, tracer: otelChopping2.tracer()) + return try await [carrot, potato] + } } func chop(_ vegetable: Vegetable, tracer: any Tracer) async throws -> Vegetable { - await tracer.withSpan("chop-\(vegetable)") { _ in - await sleep(for: .seconds(5)) - // ... - return vegetable // "chopped" - } + await tracer.withSpan("chop-\(vegetable)") { _ in + await sleep(for: .seconds(5)) + // ... + return vegetable // "chopped" + } } func marinateMeat() async -> Meat { - await sleep(for: .milliseconds(620)) + await sleep(for: .milliseconds(620)) - return await InstrumentationSystem.tracer.withSpan("marinateMeat") { _ in - await sleep(for: .seconds(3)) - // ... - return Meat() - } + return await InstrumentationSystem.tracer.withSpan("marinateMeat") { _ in + await sleep(for: .seconds(3)) + // ... + return Meat() + } } func preheatOven(temperature: Int) async -> Oven { - await InstrumentationSystem.tracer.withSpan("preheatOven") { _ in - // ... - await sleep(for: .seconds(6)) - return Oven() - } + await InstrumentationSystem.tracer.withSpan("preheatOven") { _ in + // ... + await sleep(for: .seconds(6)) + return Oven() + } } func cook(_: Any, _: Any, _: Any) async -> Meal { - await InstrumentationSystem.tracer.withSpan("cook") { span in - span.addEvent("children-asking-if-done-already") - await sleep(for: .seconds(3)) - span.addEvent("children-asking-if-done-already-again") - await sleep(for: .seconds(2)) - // ... - return Meal() - } + await InstrumentationSystem.tracer.withSpan("cook") { span in + span.addEvent("children-asking-if-done-already") + await sleep(for: .seconds(3)) + span.addEvent("children-asking-if-done-already-again") + await sleep(for: .seconds(2)) + // ... + return Meal() + } } diff --git a/Samples/Dinner/Sources/Onboarding/Model.swift b/Samples/Dinner/Sources/Onboarding/Model.swift index 0574946..2ff0b77 100644 --- a/Samples/Dinner/Sources/Onboarding/Model.swift +++ b/Samples/Dinner/Sources/Onboarding/Model.swift @@ -24,12 +24,10 @@ // //===----------------------------------------------------------------------===// - struct Meal: Sendable {} struct Meat: Sendable {} struct Oven: Sendable {} enum Vegetable: Sendable { - case potato - case carrot + case potato + case carrot } - diff --git a/Samples/Dinner/Sources/Onboarding/main.swift b/Samples/Dinner/Sources/Onboarding/main.swift index e5c214e..1f3e9b7 100644 --- a/Samples/Dinner/Sources/Onboarding/main.swift +++ b/Samples/Dinner/Sources/Onboarding/main.swift @@ -24,7 +24,6 @@ // //===----------------------------------------------------------------------===// - import Logging import NIO import OpenTelemetry @@ -36,9 +35,9 @@ import Tracing let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) LoggingSystem.bootstrap { label in - var handler = StreamLogHandler.standardOutput(label: label) - handler.logLevel = .trace - return handler + var handler = StreamLogHandler.standardOutput(label: label) + handler.logLevel = .trace + return handler } // ==== ---------------------------------------------------------------------------------------------------------------- diff --git a/Sources/Instrumentation/Instrument.swift b/Sources/Instrumentation/Instrument.swift index 5ee8724..976b0ba 100644 --- a/Sources/Instrumentation/Instrument.swift +++ b/Sources/Instrumentation/Instrument.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule /// Typealias used to simplify Support of old Swift versions which do not have `Sendable` defined. #if swift(>=5.6.0) @@ -51,22 +51,22 @@ public protocol Injector: _SwiftInstrumentationSendable { /// Conforming types are usually cross-cutting tools like tracers. They are agnostic of what specific `Carrier` is used /// to propagate metadata across boundaries, but instead just specify what values to use for which keys. public protocol Instrument: _SwiftInstrumentationSendable { - /// Extract values from a `Carrier` by using the given extractor and inject them into the given `Baggage`. + /// Extract values from a `Carrier` by using the given extractor and inject them into the given `ServiceContext`. /// It's quite common for `Instrument`s to come up with new values if they weren't passed along in the given `Carrier`. /// /// - Parameters: /// - carrier: The `Carrier` that was used to propagate values across boundaries. - /// - baggage: The `Baggage` into which these values should be injected. + /// - context: The `ServiceContext` into which these values should be injected. /// - extractor: The ``Extractor`` that extracts values from the given `Carrier`. - func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Extract.Carrier == Carrier - /// Extract values from a `Baggage` and inject them into the given `Carrier` using the given ``Injector``. + /// Extract values from a `ServiceContext` and inject them into the given `Carrier` using the given ``Injector``. /// /// - Parameters: - /// - baggage: The `Baggage` from which relevant information will be extracted. + /// - context: The `ServiceContext` from which relevant information will be extracted. /// - carrier: The `Carrier` into which this information will be injected. - /// - injector: The ``Injector`` used to inject extracted `Baggage` into the given `Carrier`. - func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + /// - injector: The ``Injector`` used to inject extracted `ServiceContext` into the given `Carrier`. + func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Inject.Carrier == Carrier } diff --git a/Sources/Instrumentation/InstrumentationSystem.swift b/Sources/Instrumentation/InstrumentationSystem.swift index e25e388..acb9eaa 100644 --- a/Sources/Instrumentation/InstrumentationSystem.swift +++ b/Sources/Instrumentation/InstrumentationSystem.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule /// `InstrumentationSystem` is a global facility where the default cross-cutting tool can be configured. /// It is set up just once in a given program to select the desired ``Instrument`` implementation. @@ -22,7 +22,7 @@ import InstrumentationBaggage /// /// # Access the Instrument /// ``instrument``: Returns whatever you passed to ``bootstrap(_:)`` as an ``Instrument``. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public enum InstrumentationSystem { private static let lock = ReadWriteLock() private static var _instrument: Instrument = NoOpInstrument() @@ -62,9 +62,9 @@ public enum InstrumentationSystem { } } -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext extension InstrumentationSystem { - /// :nodoc: INTERNAL API: Do Not Use + /// INTERNAL API: Do Not Use public static func _findInstrument(where predicate: (Instrument) -> Bool) -> Instrument? { self.lock.withReaderLock { if let multiplex = self._instrument as? MultiplexInstrument { diff --git a/Sources/Instrumentation/MultiplexInstrument.swift b/Sources/Instrumentation/MultiplexInstrument.swift index 44461e7..3a56807 100644 --- a/Sources/Instrumentation/MultiplexInstrument.swift +++ b/Sources/Instrumentation/MultiplexInstrument.swift @@ -12,17 +12,17 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule /// A pseudo-``Instrument`` that may be used to instrument using multiple other ``Instrument``s across a -/// common `Baggage`. +/// common `ServiceContext`. public struct MultiplexInstrument { private var instruments: [Instrument] /// Create a ``MultiplexInstrument``. /// /// - Parameter instruments: An array of ``Instrument``s, each of which will be used to ``Instrument/inject(_:into:using:)`` or ``Instrument/extract(_:into:using:)`` - /// through the same `Baggage`. + /// through the same `ServiceContext`. public init(_ instruments: [Instrument]) { self.instruments = instruments } @@ -35,15 +35,15 @@ extension MultiplexInstrument { } extension MultiplexInstrument: Instrument { - public func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + public func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier { - self.instruments.forEach { $0.inject(baggage, into: &carrier, using: injector) } + self.instruments.forEach { $0.inject(context, into: &carrier, using: injector) } } - public func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + public func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier { - self.instruments.forEach { $0.extract(carrier, into: &baggage, using: extractor) } + self.instruments.forEach { $0.extract(carrier, into: &context, using: extractor) } } } diff --git a/Sources/Instrumentation/NoOpInstrument.swift b/Sources/Instrumentation/NoOpInstrument.swift index ddfd3f8..965abf0 100644 --- a/Sources/Instrumentation/NoOpInstrument.swift +++ b/Sources/Instrumentation/NoOpInstrument.swift @@ -12,19 +12,19 @@ // //===----------------------------------------------------------------------===// -import InstrumentationBaggage +import ServiceContextModule /// A "no op" implementation of an ``Instrument``. public struct NoOpInstrument: Instrument { public init() {} - public func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + public func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier { // no-op } - public func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + public func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier { // no-op diff --git a/Sources/Tracing/Docs.docc/Guides/ImplementATracer.md b/Sources/Tracing/Docs.docc/Guides/ImplementATracer.md index 397949e..b4933b1 100644 --- a/Sources/Tracing/Docs.docc/Guides/ImplementATracer.md +++ b/Sources/Tracing/Docs.docc/Guides/ImplementATracer.md @@ -19,13 +19,13 @@ In order to implement an instrument you need to implement the `Instrument` proto `Instrument` has two requirements: -1. An `Instrument/extract(_:into:using:)` method, which extracts values from a generic carrier (e.g. HTTP headers) and store them into a `Baggage` instance -2. An `Instrument/inject(_:into:using:)` method, which takes values from the `Baggage` to inject them into a generic carrier (e.g. HTTP headers) +1. An `Instrument/extract(_:into:using:)` method, which extracts values from a generic carrier (e.g. HTTP headers) and store them into a `ServiceContext` instance +2. An `Instrument/inject(_:into:using:)` method, which takes values from the `ServiceContext` to inject them into a generic carrier (e.g. HTTP headers) The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to act on the provided information or to add additional information to be carried across these boundaries. -> The [`Baggage` documentation](https://github.com/apple/swift-distributed-tracing-baggage) type is declared in the swift-distributed-tracing-baggage package. +> The [`ServiceContext`](https://swiftpackageindex.com/apple/swift-service-context/documentation/servicecontextmodule) type is declared in the swift-service-context package. ### Creating a `Tracer` @@ -36,20 +36,20 @@ When creating a tracer you will need to implement two types: > ``Span`` largely resembles span concept as defined [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), however this package does not enforce certain rules specified in there - that is left up to a specific open telemetry tracer implementation. -### Defining, injecting and extracting Baggage +### Defining, injecting and extracting `ServiceContext` -In order to be able to extract and inject values into the `Baggage` which is the value that is "carried around" across asynchronous contexts, -we need to declare a `BaggageKey`. Baggage generally acts as a type-safe dictionary and declaring the keys this way allows us to perform lookups +In order to be able to extract and inject values into the `ServiceContext` which is the value that is "carried around" across asynchronous contexts, +we need to declare a `ServiceContextKey`. ServiceContext generally acts as a type-safe dictionary and declaring the keys this way allows us to perform lookups returning the expected type of values: ```swift import Tracing -private enum TraceIDKey: BaggageKey { +private enum TraceIDKey: ServiceContextKey { typealias Value = String } -extension Baggage { +extension ServiceContext { var traceID: String? { get { return self[TraceIDKey.self] @@ -64,17 +64,17 @@ extension Baggage { Which is then to be used like this: ```swift -var baggage = Barrage.current ?? Baggage.topLevel -baggage.traceID = "4bf92f3577b34da6a3ce929d0e0e4736" -print(baggage.traceID ?? "") +var context = Barrage.current ?? ServiceContext.topLevel +context.traceID = "4bf92f3577b34da6a3ce929d0e0e4736" +print(context.traceID ?? "") ``` -Here we used a `Baggage.current` to "_pick up_" the task-local baggage value, if present, -and if not present we created a new "top level" baggage item. This API specifically is +Here we used a `ServiceContext.current` to "_pick up_" the task-local context value, if present, +and if not present we created a new "top level" context item. This API specifically is named "top level" to imply that this should be used when first initiating a top level -context propagation baggage -- whenever possible, prefer to pick up the `current` baggage. +context propagation context -- whenever possible, prefer to pick up the `current` context. -### Injecting and extracting Baggage +### Injecting and extracting ServiceContext When hitting boundaries like an outgoing HTTP request the library will call out to the [configured instrument(s)](#Bootstrapping-the-Instrumentation-System): @@ -83,9 +83,9 @@ For example, an imaginary HTTP client library making a GET request would look so ```swift func get(url: String) { var request = HTTPRequest(url: url) - if let baggage = Baggage.current { + if let context = ServiceContext.current { InstrumentationSystem.instrument.inject( - baggage, + context, into: &request.headers, using: HTTPHeadersInjector() ) @@ -95,19 +95,19 @@ func get(url: String) { ``` On the receiving side, an HTTP server should use the following `Instrument` API to extract the HTTP headers of the given -`HTTPRequest` _into_ the baggage and then wrap invoking user code (or the "next" call in a middleware setup) with `Baggage.withValue` -which sets the Baggage.current task local value: +`HTTPRequest` _into_ the context and then wrap invoking user code (or the "next" call in a middleware setup) with `ServiceContext.withValue` +which sets the ServiceContext.current task local value: ```swift func handler(request: HTTPRequest) async throws { - var requestBaggage = Baggage.current ?? .topLevel + var requestBaggage = ServiceContext.current ?? .topLevel InstrumentationSystem.instrument.extract( request.headers, into: &requestBaggage, using: HTTPHeadersExtractor() ) - try await Baggage.withValue(requestBaggage) { + try await ServiceContext.withValue(requestBaggage) { // invoke user code ... } } @@ -116,54 +116,58 @@ func handler(request: HTTPRequest) async throws { > In case your library makes use of the `NIOHTTP1.HTTPHeaders` type we already have an `HTTPHeadersInjector` and `HTTPHeadersExtractor` available as part of the `NIOInstrumentation` library. -For your library/framework to be able to carry `Baggage` across asynchronous boundaries, it's crucial that you carry the context throughout your entire call chain in order to avoid dropping metadata. +For your library/framework to be able to carry `ServiceContext` across asynchronous boundaries, it's crucial that you carry the context throughout your entire call chain in order to avoid dropping metadata. -## Creating an instrument - -Creating an instrument means adopting the `Instrument` protocol (or `Tracer` in case you develop a tracer). -`Instrument` is part of the `Instrumentation` library & `Tracing` contains the `Tracer` protocol. - -`Instrument` has two requirements: - -1. A method to inject values inside a `Baggage` into a generic carrier (e.g. HTTP headers) -2. A method to extract values from a generic carrier (e.g. HTTP headers) and store them in a `Baggage` - -The two methods will be called by instrumented libraries/frameworks at asynchronous boundaries, giving you a chance to -act on the provided information or to add additional information to be carried across these boundaries. - -> Check out the [`Baggage` documentation](https://github.com/apple/swift-distributed-tracing-baggage) for more information on -how to retrieve values from the `Baggage` and how to set values on it. +### Starting and ending spans -### Creating a `Tracer` +The primary goal and user-facing API of a ``Tracer`` is to create spans. -When creating a tracer you need to create two types: +While you will need to implement all methods of the tracer protocol, the most important one is `startSpan`: -1. Your tracer conforming to `Tracer` -2. A span class conforming to `Span` +```swift +#if swift(>=5.7.0) +extension MyTracer: Tracer { + func startSpan( + _ operationName: String, + context: @autoclosure () -> ServiceContext, + ofKind kind: SpanKind, + at instant: @autoclosure () -> Instant, + function: String, + file fileID: String, + line: UInt + ) -> MySpan { + let span = MySpan( + operationName: operationName, + startTime: instant(), + context: context(), + kind: kind, + onEnd: self.onEndSpan + ) + self.spans.append(span) + return span + } +} +#endif +``` -> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it. +If you can require Swift 5.7 prefer doing so, and return the concrete ``Span`` type from the `startSpan` method. +This allows users who decide to use your tracer explicitly, and not via the global bootstrapped system to avoid +wrapping tracers in existentials which can be beneficial in some situations. -### Defining, injecting and extracting Baggage +Next, eventually the user started span will be ended and the `Span/end()` method will be invoked: ```swift -import Tracing - -private enum TraceIDKey: BaggageKey { - typealias Value = String -} +public struct MySpan: Tracing.Span { + // ... implement all protocol requirements of Span ... -extension Baggage { - var traceID: String? { - get { - return self[TraceIDKey.self] + public func end(at instant: @autoclosure () -> Instant) { + // store the `endInstant` + self.tracer.emit(self) } - set { - self[TraceIDKey.self] = newValue - } - } } - -var context = Baggage.topLevel(logger: ...) -context.baggage.traceID = "4bf92f3577b34da6a3ce929d0e0e4736" -print(context.baggage.traceID ?? "new trace id") ``` + +It is possible to implement a span as a struct or a class, but a ``Span`` **must exhibit reference type behavior**. +In other words, adding an attribute to one reference of a span must be visible in other references to it. + +The ability to implement a span using a struct comes in handy when implementing a "Noop" (do nothing) span and avoids heap allocations. Normal spans though generally will be backed by `class` based storage and should flush themselfes to the owning tracer once the span has been ended \ No newline at end of file diff --git a/Sources/Tracing/Docs.docc/Guides/InstrumentYourLibrary.md b/Sources/Tracing/Docs.docc/Guides/InstrumentYourLibrary.md index 70110e9..81b3098 100644 --- a/Sources/Tracing/Docs.docc/Guides/InstrumentYourLibrary.md +++ b/Sources/Tracing/Docs.docc/Guides/InstrumentYourLibrary.md @@ -15,16 +15,16 @@ Other examples of libraries which would benefit _the most_ from being instrument - any other library which can emit meaningful span information about tasks it is performing. The most important libraries to instrument are "edge" libraries, which serve to connect between systems, because -it is them who must inject and extract contextual baggage metadata to enable distributed trace ``Span`` propagation. +it is them who must inject and extract context metadata to enable distributed trace ``Span`` propagation. Following those, any database or other complex library which may be able to emit useful information about its internals are also good candidates to being instrumented. Note that libraries may do so optionally, or hide the "verboseness" of such traces behind options, or only attach information if a ``Span`` is already active etc. Please review your library's documentation to learn more about it has integrated tracing support. -### Propagating baggage metadata +### Propagating context metadata -When crossing boundaries between processes, such as making or receiving an HTTP request, the library responsible for doing so should invoke instrumentation in order to inject or extract the contextual baggage metadata into/from the "carrier" type (such as the `HTTPResponse`) type. +When crossing boundaries between processes, such as making or receiving an HTTP request, the library responsible for doing so should invoke instrumentation in order to inject or extract the context metadata into/from the "carrier" type (such as the `HTTPResponse`) type. #### Handling outbound requests @@ -35,7 +35,7 @@ When a library makes an "outgoing" request or message interaction, it should inv │ Specific Tracer / Instrument │ └──────────────────────────────────┘ │ - instrument.inject(baggage, into: request, using: httpInjector) + instrument.inject(context, into: request, using: httpInjector) │ ┌────────▼──────┐ ┌───────────────┤ Tracing API ├────────────┐ ┌────┐ @@ -51,15 +51,15 @@ When a library makes an "outgoing" request or message interaction, it should inv > Note: A library _itself_ cannot really know what information to propagate, since that depends on the used tracing or instrumentation system. The library does however understand its carrier type, and thus can implement the `Instrumentation/Injector` protocol. -For example, an HTTP client e.g. should inject the current baggage (which could be carrying trace ``Span`` information) into the HTTP headers as follows: +For example, an HTTP client e.g. should inject the current context (which could be carrying trace ``Span`` information) into the HTTP headers as follows: ```swift func get(url: String) -> HTTPResponse { var request = HTTPRequest(url: url) - if let baggage = Baggage.current { + if let context = ServiceContext.current { InstrumentationSystem.instrument.inject( - baggage, + context, into: &request, using: HTTPRequestInjector() ) @@ -71,7 +71,7 @@ func get(url: String) -> HTTPResponse { As you can see, the library does not know anything about what tracing system or instrumentation is installed, because it cannot know that ahead of time. -All it has to do is query for the current [task-local](https://developer.apple.com/documentation/swift/tasklocal) `Baggage` value, and if one is present, call on the instrumentation system to inject it into the request. +All it has to do is query for the current [task-local](https://developer.apple.com/documentation/swift/tasklocal) `ServiceContext` value, and if one is present, call on the instrumentation system to inject it into the request. Since neither the tracing API, nor the specific tracer backend are aware of this library's specific `HTTPRequest` type, we also need to implement an `Instrumentation/Injector` which takes on the responsibility of adding the metadata into the carrier type (which in our case is the `HTTPRequest`). An injector could for example be implemented like this: @@ -85,11 +85,11 @@ struct HTTPRequestInjector: Injector { Once the metadata has been injected, the request--including all the additional metadata--is sent over the network. -> Note: The actual logic of deciding what baggage values to inject depend on the tracer implementation, and thus we are not covering it in this _end-user_ focused guide. Refer to if you'd like to learn about implementing a ``Tracer``. +> Note: The actual logic of deciding what context values to inject depend on the tracer implementation, and thus we are not covering it in this _end-user_ focused guide. Refer to if you'd like to learn about implementing a ``Tracer``. #### Handling inbound requests -On the receiving side, an HTTP server needs to perform the inverse operation, as it receives the request from the network and forms an `HTTPRequest` object. Before it is passed it to user-code, it must extract any trace metadata from the request headers into the `Baggage`. +On the receiving side, an HTTP server needs to perform the inverse operation, as it receives the request from the network and forms an `HTTPRequest` object. Before it is passed it to user-code, it must extract any trace metadata from the request headers into the `ServiceContext`. ``` ┌──────────────────────────────────┐ @@ -97,7 +97,7 @@ On the receiving side, an HTTP server needs to perform the inverse operation, as └──────────────────────────────────┘ ▲ │ - instrument.extract(request, into: &baggage, using: httpExtractor) + instrument.extract(request, into: &context, using: httpExtractor) │ ┌──────▼──────┐ ┌────┐ ┌──────────────┤ Tracing API ├────────────┐ @@ -112,18 +112,18 @@ On the receiving side, an HTTP server needs to perform the inverse operation, as └──── ``` -This is very similar to what we were doing on the outbound side, but the roles of baggage and request are somewhat reversed: we're extracting values from the carrier into the baggage. The code performing this task could look something like this: +This is very similar to what we were doing on the outbound side, but the roles of context and request are somewhat reversed: we're extracting values from the carrier into the context. The code performing this task could look something like this: ```swift func handler(request: HTTPRequest) async { // we are beginning a new "top level" context - the beginning of a request - - // and thus start from a fresh, empty, top-level baggage: - var baggage = Baggage.topLevel + // and thus start from a fresh, empty, top-level context: + var context = ServiceContext.topLevel - // populate the baggage by extracting interesting metadata from the incoming request: + // populate the context by extracting interesting metadata from the incoming request: InstrumentationSystem.instrument.extract( request, - into: &baggage, + into: &context, using: HTTPRequestExtractor() ) @@ -143,16 +143,16 @@ struct HTTPRequestExtract: Instrumentation.Extractor { Which exact keys will be asked for depends on the tracer implementation, thus we don't present this part of the implementation in part of the guide. For example, a tracer most likely would look for, and extract, values for keys such as `trace-id` and `span-id`. Note though that the exact semantics and keys used by various tracers differ, which is why we have to leave the decision of what to extract up to tracer implementations. -Next, your library should "*restore*" the contextual baggage, this is performed by setting the baggage task-local value around calling into user code, like this: +Next, your library should "*restore*" the context, this is performed by setting the context task-local value around calling into user code, like this: ```swift func handler(request: HTTPRequest) async { // ... - InstrumentationSystem.instrument.extract(..., into: &baggage, ...) + InstrumentationSystem.instrument.extract(..., into: &context, ...) // ... - // wrap user code with binding the Baggage task local: - await Baggage.withValue(baggage) { + // wrap user code with binding the ServiceContext task local: + await ServiceContext.withValue(context) { await userCode(request) } @@ -163,13 +163,13 @@ func handler(request: HTTPRequest) async { } ``` -This sets the task-local value `Baggage.current` which is used by [swift-log](https://github.com/apple/swift-log), as well as ``Tracer`` APIs in order to later "*pick up*" the baggage and e.g. include it in log statements, or start new trace spans using the information stored in the baggage. +This sets the task-local value `ServiceContext.current` which is used by [swift-log](https://github.com/apple/swift-log), as well as ``Tracer`` APIs in order to later "*pick up*" the context and e.g. include it in log statements, or start new trace spans using the information stored in the context. -> Note: The end goal here being that when end-users of your library write `log.info("Hello")` the logger is able to pick up the contextual baggage information and include the e.g. the `trace-id` in such log statement automatically! This way, every log made during the handling of this request would include the `trace-id` automatically, e.g. like this: +> Note: The end goal here being that when end-users of your library write `log.info("Hello")` the logger is able to pick up the context information and include the e.g. the `trace-id` in such log statement automatically! This way, every log made during the handling of this request would include the `trace-id` automatically, e.g. like this: > > `12:43:32 info [trace-id=463a...13ad] Logging during handling of a request!` -If your library makes multiple calls to user-code as part of handling the same request, you may consider if restoring the baggage around all of these callbacks is beneficial. For example, if a library had callbacks such as: +If your library makes multiple calls to user-code as part of handling the same request, you may consider if restoring the context around all of these callbacks is beneficial. For example, if a library had callbacks such as: ```swift /// Example library protocol, offering multiple callbacks, all semantically part of the same request handling. @@ -183,83 +183,83 @@ protocol SampleHTTPHandler { } ``` -You may want to restore the baggage once around both those calls, or if that is not possible, restore it every time when calling back into user code, e.g. like this: +You may want to restore the context once around both those calls, or if that is not possible, restore it every time when calling back into user code, e.g. like this: ```swift actor MySampleServer { - var baggage: Baggage = .topLevel - var userCode: SampleHTTPHandler + var context: ServiceContext = .topLevel + var userHandler: SampleHTTPHandler func onHeaders(headers: HTTPHeaders) async { - await Baggage.withValue(self.baggage) { + await ServiceContext.withValue(self.context) { await userHandler.requestHeaders(headers) } } func onBodyPart(part: ByteBuffer) async { - await Baggage.withValue(self.baggage) { + await ServiceContext.withValue(self.context) { await userHandler.requestHeaders(headers) } } } ``` -While this code is very simple for illustration purposes, and it may seem surprising why there are two separate places where we need to call into user-code separately, in practice such situations can happen when using asynchronous network or database libraries which offer their API in terms of callbacks. Always consider if and when to restore baggage such that it makes sense for the end user. +While this code is very simple for illustration purposes, and it may seem surprising why there are two separate places where we need to call into user-code separately, in practice such situations can happen when using asynchronous network or database libraries which offer their API in terms of callbacks. Always consider if and when to restore context such that it makes sense for the end user. ### Starting Trace Spans in Your Library -The above steps are enough if you wanted to provide contextual baggage propagation. It already enables techniques such as **correlation ids** which can be set once, in one system, and then carried through to any downstream services the code makes calls from while the baggage is set. +The above steps are enough if you wanted to provide context propagation. It already enables techniques such as **correlation ids** which can be set once, in one system, and then carried through to any downstream services the code makes calls from while the context is set. Many libraries also have the opportunity to start trace spans themselfes, on behalf of users, in pieces of the library that can provide useful insight in the behavior or the library in production. For example, the `HTTPServer` can start spans as soon as it begins handling HTTP requests, and this way provide a parent span to any spans the user-code would be creating itself. -Let us revisit the previous sample `HTTPServer` which restored baggage around invoking the user-code, and further extend it to start a span including basic information about the `HTTPRequest` being handled: +Let us revisit the previous sample `HTTPServer` which restored context around invoking the user-code, and further extend it to start a span including basic information about the `HTTPRequest` being handled: ```swift // SUB-OPTIMAL EXAMPLE: func handler(request: HTTPRequest) async { - // 1) extract trace information into baggage... - InstrumentationSystem.instrument.extract(..., into: &baggage, ...) + // 1) extract trace information into context... + InstrumentationSystem.instrument.extract(..., into: &context, ...) // ... - // 2) restore baggage, using a task-local: - await Baggage.withValue(baggage) { - // 3) start span, using contextual baggage (which may contain trace-ids already): + // 2) restore context, using a task-local: + await ServiceContext.withValue(context) { + // 3) start span, using context (which may contain trace-ids already): await withSpan("\(request.path)") { span in // 3.1) Set useful attributes:on the span: span.attributes["http.method"] = request.method // ... // See also: Open Telemetry typed attributes in swift-distributed-tracing-extras - // 4) user code will have the apropriate Span baggage restored: + // 4) user code will have the apropriate Span context restored: await userCode(request) } } } ``` -This is introducing multiple layers of nesting, and we have un-necessarily restored, picked-up, and restored the baggage again. In order to avoid this duplicate work, it is beneficial to use the ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` overload, which also accepts a `Baggage` as parameter, rather than picking it up from the task-local value: +This is introducing multiple layers of nesting, and we have un-necessarily restored, picked-up, and restored the context again. In order to avoid this duplicate work, it is beneficial to use the ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` overload, which also accepts a `ServiceContext` as parameter, rather than picking it up from the task-local value: ```swift // BETTER func handler(request: HTTPRequest) async { - // 1) extract trace information into baggage: - InstrumentationSystem.instrument.extract(..., into: &baggage, ...) + // 1) extract trace information into context: + InstrumentationSystem.instrument.extract(..., into: &context, ...) - // 2) start span, passing the freshly extracted baggage explicitly: - await withSpan("\(request.path)", baggage: baggage) { span in + // 2) start span, passing the freshly extracted context explicitly: + await withSpan("\(request.path)", context: context) { span in // ... } } ``` -This method will only restore the baggage once, after the tracer has had a chance to decide if this execution will be traced, and if so, setting its own trace and span identifiers. This way only one task-local access (set) is performed in this version of the code, which is preferable to the set/read/set that was performed previously. +This method will only restore the context once, after the tracer has had a chance to decide if this execution will be traced, and if so, setting its own trace and span identifiers. This way only one task-local access (set) is performed in this version of the code, which is preferable to the set/read/set that was performed previously. #### Manual Span Lifetime Management -While the ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` API is preferable in most situations, it may not be possible to use when the lifetime of a span only terminates in yet another callback API. In such situations, it may be impossible to "wrap" the entire piece of code that would logically represent "the span" using a `withSpan(...) { ... }` call. +While the ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` API is preferable in most situations, it may not be possible to use when the lifetime of a span only terminates in yet another callback API. In such situations, it may be impossible to "wrap" the entire piece of code that would logically represent "the span" using a `withSpan(...) { ... }` call. -In such situations you can resort to using the ``startSpan(_:baggage:ofKind:at:function:file:line:)`` and ``Span/end()`` APIs explicitly. Those APIs can then be used like this: +In such situations you can resort to using the ``startSpan(_:context:ofKind:at:function:file:line:)`` and ``Span/end()`` APIs explicitly. Those APIs can then be used like this: ```swift // Callback heavy APIs may need to store and manage spans manually: @@ -298,9 +298,9 @@ It is worth noting that double-ending a span should be considered a programmer e > > Please also take care to never `end()` a span that was created using `withSpan()` APIs, because `withSpan` will automatically end the span when the closure returns. -#### Storing and restoring baggage across callbacks +#### Storing and restoring context across callbacks -Note also since a `Span` contains an instrumentation `Baggage`, you can also pass the span's baggage to any APIs which may need it, or even restore the baggage e.g. for loggers to pick it up while emitting log statements: +Note also since a `Span` contains a `ServiceContext`, you can also pass the span's context to any APIs which may need it, or even restore the context e.g. for loggers to pick it up while emitting log statements: ```swift final class StatefulHandler { @@ -312,11 +312,11 @@ final class StatefulHandler { // callback, form other task, so we don't have the task-local information here anymore func onSomethingHappening(event: SomeEvent) { - Baggage.withValue(span.baggage) { // restore task-local baggage - // which allows the baggage to be used by loggers and tracers as usual again: + ServiceContext.withValue(span.context) { // restore task-local context + // which allows the context to be used by loggers and tracers as usual again: log.info("Event happened: \(event)") - // since the baggage was restored here, the startSpan will pick it up, + // since the context was restored here, the startSpan will pick it up, // and the "event-id" span will be a child of the "request.path" span we started before. withSpan("event-\(event.id)") { span in // new child span (child of self.span) // ... handle the event ... @@ -326,15 +326,15 @@ final class StatefulHandler { } ``` -It is also possible to pass the baggage explicitly to `withSpan` or `startSpan`: +It is also possible to pass the context explicitly to `withSpan` or `startSpan`: ```swift -withSpan("event-\(event.id)", baggage: span.baggage) { span in +withSpan("event-\(event.id)", context: span.context) { span in // ... } ``` -which is equivalent to surrounding thr withSpan with a binding of the baggage. The passed baggage (with the values updated by the tracer), will then be set for the duration of the `withSpan` operation, just like usual. +which is equivalent to surrounding the `withSpan` call with a binding of the context. The passed context (with the values updated by the tracer), will then be set for the duration of the `withSpan` operation, just like usual. ### Global vs. "Stored" Tracers and Instruments diff --git a/Sources/Tracing/Docs.docc/Guides/TraceYourApplication.md b/Sources/Tracing/Docs.docc/Guides/TraceYourApplication.md index f7cf894..8b4341b 100644 --- a/Sources/Tracing/Docs.docc/Guides/TraceYourApplication.md +++ b/Sources/Tracing/Docs.docc/Guides/TraceYourApplication.md @@ -65,7 +65,7 @@ InstrumentationSystem.bootstrap(otel.tracer()) You'll notice that the API specifically talks about Instrumentation rather than just Tracing. This is because it is also possible to use various instrumentation systems, e.g. which only take care -of propagating certain `Baggage` values across process boundaries, without using tracing itself. +of propagating certain `ServiceContext` values across process boundaries, without using tracing itself. In other words, all tracers are instruments, and the `InstrumentationSystem` works equally for `Instrument`, as well as ``Tracer`` implementations. @@ -100,8 +100,8 @@ import StatsdMetrics // specific Metrics library extension Logger.MetadataProvider { // Include the following OpenTelemetry tracer specific metadata in log statements: - static let otel = Logger.MetadataProvider { baggage in - guard let spanContext = baggage?.spanContext else { return nil } + static let otel = Logger.MetadataProvider { context in + guard let spanContext = context?.spanContext else { return nil } return [ "trace-id": "\(spanContext.traceID)", "span-id": "\(spanContext.spanID)", @@ -258,7 +258,7 @@ This was just a quick introduction to tracing, but hopefully you are now excited ### Efficiently working with Spans -We already saw the basic API to spawn a trace span, the ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` method, but we didn't discuss it in depth yet. In this section we'll discuss how to efficiently work with spans and some common patterns and practices. +We already saw the basic API to spawn a trace span, the ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` method, but we didn't discuss it in depth yet. In this section we'll discuss how to efficiently work with spans and some common patterns and practices. Firstly, spans are created using a `withSpan` call and performing the operation contained within the span in the trailing operation closure body. This is important because it automatically, and correctly, delimits the lifetime of the span: from its creation, until the operation closure returns: @@ -440,15 +440,13 @@ Events usually show up in a in a trace view as points on the timeline (note that Events cannot be "failed" or "successful", that is a property of a ``Span``, and they do not have anything that would be equivalent to a log level. When a trace span is recorded and collected, so will all events related to it. In that sense, events are different from log statements, because one can easily change a logger to include the "debug level" log statements, but technically no such concept exists for events (although you could simulate it with attributes). -### Where (and how) do Baggage and Spans propagate? - ### Integrations #### Swift-log integration Swift-log, the logging package for the server ecosystem, offers native integration with task local values using the `Logger/MetadataProvider`, and its primary application is logging tracing metadata values. -The snippet below shows how one can write a metadata provider and manually extract the baggage and associated metadata value to be included in log statements automatically: +The snippet below shows how one can write a metadata provider and manually extract the context and associated metadata value to be included in log statements automatically: ```swift import Logging @@ -456,15 +454,15 @@ import Tracing // Either manually extract "some specific tracer"'s context or such library would already provide it let metadataProvider = Logger.MetadataProvider { - guard let baggage = Baggage.current else { + guard let context = ServiceContext.current else { return [:] } - guard let context = baggage.someSpecificTracerContext else { + guard let specificContext = context.someSpecificTracerContext else { return [:] } var metadata: Logger.Metadata = [:] - metadata["trace-id"] = "\(someSpecificTracerContext.traceID)" - metadata["span-id"] = "\(someSpecificTracerContext.spanID)" + metadata["trace-id"] = "\(specificContext.traceID)" + metadata["span-id"] = "\(specificContext.spanID)" return metadata } diff --git a/Sources/Tracing/Docs.docc/Tracer.md b/Sources/Tracing/Docs.docc/Tracer.md index 32eef33..8a400f7 100644 --- a/Sources/Tracing/Docs.docc/Tracer.md +++ b/Sources/Tracing/Docs.docc/Tracer.md @@ -4,9 +4,9 @@ ### Creating Spans -- ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` +- ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` ### Manual Span management -- ``startSpan(_:baggage:ofKind:at:function:file:line:)-u1y4`` +- ``startSpan(_:context:ofKind:at:function:file:line:)-u1y4`` - ``Span/end()`` diff --git a/Sources/Tracing/NoOpTracer.swift b/Sources/Tracing/NoOpTracer.swift index f2d3c4b..152295c 100644 --- a/Sources/Tracing/NoOpTracer.swift +++ b/Sources/Tracing/NoOpTracer.swift @@ -14,42 +14,42 @@ import Dispatch @_exported import Instrumentation -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule /// Tracer that ignores all operations, used when no tracing is required. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public struct NoOpTracer: LegacyTracer { public typealias Span = NoOpSpan public init() {} public func startAnySpan(_ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, file fileID: String, line: UInt) -> any Tracing.Span { - NoOpSpan(baggage: baggage()) + NoOpSpan(context: context()) } public func forceFlush() {} - public func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + public func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier { // no-op } - public func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + public func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier { // no-op } public struct NoOpSpan: Tracing.Span { - public let baggage: Baggage + public let context: ServiceContext public var isRecording: Bool { false } @@ -63,8 +63,8 @@ public struct NoOpTracer: LegacyTracer { } } - public init(baggage: Baggage) { - self.baggage = baggage + public init(context: ServiceContext) { + self.context = context } public func setStatus(_ status: SpanStatus) {} @@ -94,14 +94,14 @@ public struct NoOpTracer: LegacyTracer { extension NoOpTracer: Tracer { public func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, file fileID: String, line: UInt ) -> NoOpSpan { - NoOpSpan(baggage: baggage()) + NoOpSpan(context: context()) } } #endif diff --git a/Sources/Tracing/SpanProtocol.swift b/Sources/Tracing/SpanProtocol.swift index 4bc995c..8ec4408 100644 --- a/Sources/Tracing/SpanProtocol.swift +++ b/Sources/Tracing/SpanProtocol.swift @@ -12,16 +12,16 @@ // //===----------------------------------------------------------------------===// -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule /// A `Span` represents an interval from the start of an operation to its end, along with additional metadata included /// with it. /// -/// A `Span` can be created from a `Baggage` or `LoggingContext` which MAY contain existing span identifiers, +/// A `Span` can be created from a `ServiceContext` or `LoggingContext` which MAY contain existing span identifiers, /// in which case this span should be considered as "child" of the previous span. /// /// Spans are created by invoking the `withSpan`` method which delegates to the currently configured bootstrapped -/// tracer.By default the task-local "current" `Baggage` is used to perform this association. +/// tracer.By default the task-local "current" `ServiceContext` is used to perform this association. /// /// ### Reference semantics /// A `Span` always exhibits reference semantics. Even if a span were to be implemented using a struct (or enum), @@ -32,8 +32,8 @@ /// /// - SeeAlso: For more details refer to the [OpenTelemetry Specification: Span](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span) which this type is compatible with. public protocol Span: _SwiftTracingSendableSpan { - /// The read-only `Baggage` of this `Span`, set when starting this `Span`. - var baggage: Baggage { get } + /// The read-only `ServiceContext` of this `Span`, set when starting this `Span`. + var context: ServiceContext { get } /// Returns the name of the operation this span represents. /// @@ -145,7 +145,7 @@ extension Span { /// - Parameter other: The `Span` to link to. /// - Parameter attributes: The ``SpanAttributes`` describing this link. Defaults to no attributes. public func addLink(_ other: Self, attributes: SpanAttributes = [:]) { - self.addLink(SpanLink(baggage: other.baggage, attributes: attributes)) + self.addLink(SpanLink(context: other.context, attributes: attributes)) } } @@ -246,7 +246,7 @@ public protocol SpanAttributeNamespace { public protocol NestedSpanAttributesProtocol { init() - /// :nodoc: INTERNAL API + /// INTERNAL API /// Magic value allowing the dynamicMember lookup inside SpanAttributeNamespaces to work. /// /// There is no need of ever implementing this function explicitly, the default implementation is good enough. @@ -556,7 +556,7 @@ extension SpanAttributes { /// Accesses the ``SpanAttribute`` with the given name for reading and writing. /// /// Please be cautious to not abuse this APIs power to read attributes to "smuggle" values between calls. - /// Only `Baggage` is intended to carry information in a readable fashion between functions / processes / nodes. + /// Only `ServiceContext` is intended to carry information in a readable fashion between functions / processes / nodes. /// The API does allow reading in order to support the subscript-based dynamic member lookup implementation of /// attributes which allows accessing them as `span.attributes.http.statusCode`, forcing us to expose a get operation on the attributes, /// due to the lack of set-only subscripts. @@ -696,8 +696,8 @@ public enum SpanKind { /// The other ``Span``s information is stored in `context` and `attributes` may be used to /// further describe the link. public struct SpanLink { - /// A `Baggage` containing identifying information about the link target ``Span``. - public let baggage: Baggage + /// A `ServiceContext` containing identifying information about the link target ``Span``. + public let context: ServiceContext /// ``SpanAttributes`` further describing the connection between the ``Span``s. public let attributes: SpanAttributes @@ -705,10 +705,10 @@ public struct SpanLink { /// Create a new `SpanLink`. /// /// - Parameters: - /// - baggage: The `Baggage` identifying the targeted ``Span``. + /// - context: The `ServiceContext` identifying the targeted ``Span``. /// - attributes: ``SpanAttributes`` that further describe the link. Defaults to no attributes. - public init(baggage: Baggage, attributes: SpanAttributes) { - self.baggage = baggage + public init(context: ServiceContext, attributes: SpanAttributes) { + self.context = context self.attributes = attributes } } diff --git a/Sources/Tracing/Tracer.swift b/Sources/Tracing/Tracer.swift index c2a2ac1..8b7c2a2 100644 --- a/Sources/Tracing/Tracer.swift +++ b/Sources/Tracing/Tracer.swift @@ -14,17 +14,17 @@ import Dispatch @_exported import Instrumentation -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule /// Start a new ``Span`` using the global bootstrapped tracer reimplementation. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// -/// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start +/// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -35,16 +35,16 @@ import Dispatch /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... /// - instant: the time instant at which the span started -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. /// - line: The file line where the span was started. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func startSpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -55,7 +55,7 @@ public func startSpan( InstrumentationSystem.legacyTracer.startAnySpan( operationName, at: instant(), - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -65,13 +65,13 @@ public func startSpan( /// Start a new ``Span`` using the global bootstrapped tracer reimplementation. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// -/// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start +/// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -81,15 +81,15 @@ public func startSpan( /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. /// - line: The file line where the span was started. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -100,7 +100,7 @@ public func startSpan( InstrumentationSystem.legacyTracer.startAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -111,13 +111,13 @@ public func startSpan( #if swift(>=5.7.0) /// Start a new ``Span`` using the global bootstrapped tracer reimplementation. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// -/// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start +/// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -127,16 +127,16 @@ public func startSpan( /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. /// - line: The file line where the span was started. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -148,7 +148,7 @@ public func startSpan( InstrumentationSystem.tracer.startAnySpan( operationName, at: instant(), - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -162,9 +162,9 @@ public func startSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -174,7 +174,7 @@ public func startSpan( /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... /// - instant: the time instant at which the span started -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -182,11 +182,11 @@ public func startSpan( /// - operation: The operation that this span should be measuring /// - Returns: the value returned by `operation` /// - Throws: the error the `operation` has thrown (if any) -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func withSpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -196,7 +196,7 @@ public func withSpan( try InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -209,9 +209,9 @@ public func withSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -220,7 +220,7 @@ public func withSpan( /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -228,10 +228,10 @@ public func withSpan( /// - operation: The operation that this span should be measuring /// - Returns: the value returned by `operation` /// - Throws: the error the `operation` has thrown (if any) -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -241,7 +241,7 @@ public func withSpan( try InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -256,9 +256,9 @@ public func withSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -268,7 +268,7 @@ public func withSpan( /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... /// - instant: the time instant at which the span started -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -278,7 +278,7 @@ public func withSpan( /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -289,7 +289,7 @@ public func withSpan( try InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: instant(), - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -305,9 +305,9 @@ public func withSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -317,7 +317,7 @@ public func withSpan( /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... /// - instant: the time instant at which the span started -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -325,11 +325,11 @@ public func withSpan( /// - operation: The operation that this span should be measuring /// - Returns: the value returned by `operation` /// - Throws: the error the `operation` has thrown (if any) -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func withSpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -339,7 +339,7 @@ public func withSpan( try await InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -352,9 +352,9 @@ public func withSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -363,7 +363,7 @@ public func withSpan( /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -371,10 +371,10 @@ public func withSpan( /// - operation: The operation that this span should be measuring /// - Returns: the value returned by `operation` /// - Throws: the error the `operation` has thrown (if any) -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -384,7 +384,7 @@ public func withSpan( try await InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -398,9 +398,9 @@ public func withSpan( /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// -/// The current task-local `Baggage` is picked up and provided to the underlying tracer. -/// It is also possible to pass a specific `baggage` explicitly, in which case attempting -/// to pick up the task-local baggage is prevented. This can be useful when we know that +/// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. +/// It is also possible to pass a specific `context` explicitly, in which case attempting +/// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -409,7 +409,7 @@ public func withSpan( /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... -/// - baggage: The `Baggage` providing information on where to start the new ``Span``. +/// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -420,7 +420,7 @@ public func withSpan( /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -431,7 +431,7 @@ public func withSpan( try await InstrumentationSystem.legacyTracer.withAnySpan( operationName, at: instant(), - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, diff --git a/Sources/Tracing/TracerProtocol+Legacy.swift b/Sources/Tracing/TracerProtocol+Legacy.swift index 13361b0..a67cfe5 100644 --- a/Sources/Tracing/TracerProtocol+Legacy.swift +++ b/Sources/Tracing/TracerProtocol+Legacy.swift @@ -14,31 +14,31 @@ import Dispatch @_exported import Instrumentation -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule /// A tracer protocol intended to support Swift 5.6 specifically. /// /// **This protocol will be deprecated as soon as possible**, and the library will continue recommending Swift 5.7+ /// in order to make use of new language features that make expressing the tracing API free of existential types when not necessary. /// -/// When possible, prefer using ``Tracer`` and ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` APIs, +/// When possible, prefer using ``Tracer`` and ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` APIs, /// rather than these `startAnySpan` APIs which unconditionally always return existential Spans even when not necessary /// (under Swift 5.7+ type-system enhancement wrt. protocols with associated types).. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public protocol LegacyTracer: Instrument { /// Start a new span returning an existential ``Span`` reference. /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -48,7 +48,7 @@ public protocol LegacyTracer: Instrument { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -56,7 +56,7 @@ public protocol LegacyTracer: Instrument { /// - line: The file line where the span was started. func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -76,7 +76,7 @@ public protocol LegacyTracer: Instrument { // ==== ------------------------------------------------------------------ // MARK: Legacy implementations for Swift 5.7 -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext extension LegacyTracer { // ==== startSpan --------------------------------------------------------- @@ -84,15 +84,15 @@ extension LegacyTracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -102,7 +102,7 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -111,7 +111,7 @@ extension LegacyTracer { public func startAnySpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -119,7 +119,7 @@ extension LegacyTracer { ) -> any Tracing.Span { self.startAnySpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -133,15 +133,15 @@ extension LegacyTracer { /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -151,14 +151,14 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. /// - line: The file line where the span was started. public func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -166,7 +166,7 @@ extension LegacyTracer { ) -> any Tracing.Span { self.startAnySpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: DefaultTracerClock.now, function: function, @@ -180,9 +180,9 @@ extension LegacyTracer { /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -191,7 +191,7 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -203,7 +203,7 @@ extension LegacyTracer { public func withAnySpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -212,7 +212,7 @@ extension LegacyTracer { ) rethrows -> T { let span = self.startAnySpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -221,7 +221,7 @@ extension LegacyTracer { ) defer { span.end() } do { - return try Baggage.$current.withValue(span.baggage) { + return try ServiceContext.$current.withValue(span.context) { try operation(span) } } catch { @@ -235,9 +235,9 @@ extension LegacyTracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -246,7 +246,7 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -256,7 +256,7 @@ extension LegacyTracer { /// - Throws: the error the `operation` has thrown (if any) public func withAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -266,7 +266,7 @@ extension LegacyTracer { try self.withAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -282,9 +282,9 @@ extension LegacyTracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -293,7 +293,7 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -304,7 +304,7 @@ extension LegacyTracer { public func withAnySpan( _ operationName: String, at instant: @autoclosure () -> Instant, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -314,7 +314,7 @@ extension LegacyTracer { let span = self.startAnySpan( operationName, at: instant(), - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -322,7 +322,7 @@ extension LegacyTracer { ) defer { span.end() } do { - return try await Baggage.$current.withValue(span.baggage) { + return try await ServiceContext.$current.withValue(span.context) { try await operation(span) } } catch { @@ -336,9 +336,9 @@ extension LegacyTracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -347,7 +347,7 @@ extension LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - function: The function name in which the span was started /// - fileID: The `fileID` where the span was started. @@ -357,7 +357,7 @@ extension LegacyTracer { /// - Throws: the error the `operation` has thrown (if any) public func withAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -367,7 +367,7 @@ extension LegacyTracer { let span = self.startAnySpan( operationName, at: DefaultTracerClock.now, - baggage: baggage(), + context: context(), ofKind: kind, function: function, file: fileID, @@ -375,7 +375,7 @@ extension LegacyTracer { ) defer { span.end() } do { - return try await Baggage.$current.withValue(span.baggage) { + return try await ServiceContext.$current.withValue(span.context) { try await operation(span) } } catch { @@ -393,15 +393,15 @@ extension Tracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -411,7 +411,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -419,7 +419,7 @@ extension Tracer { /// - line: The file line where the span was started. public func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> InstrumentationBaggage.Baggage, + context: @autoclosure () -> ServiceContextModule.ServiceContext, ofKind kind: Tracing.SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -428,7 +428,7 @@ extension Tracer { ) -> Tracing.Span { self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -442,15 +442,15 @@ extension Tracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -460,7 +460,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -472,7 +472,7 @@ extension Tracer { public func withAnySpan( _ operationName: String, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -481,7 +481,7 @@ extension Tracer { ) rethrows -> T { try self.withSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -497,15 +497,15 @@ extension Tracer { /// /// - Warning: This method will be deprecated in favor of `Tracer/withSpan` as soon as this project is able to require Swift 5.7. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Legacy API, prefer using ``startSpan(_:baggage:ofKind:at: + /// - Note: Legacy API, prefer using ``startSpan(_:context:ofKind:at: /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -515,7 +515,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -527,7 +527,7 @@ extension Tracer { public func withAnySpan( _ operationName: String, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -536,7 +536,7 @@ extension Tracer { ) async rethrows -> T { try await self.withSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, diff --git a/Sources/Tracing/TracerProtocol.swift b/Sources/Tracing/TracerProtocol.swift index 4e88341..755de75 100644 --- a/Sources/Tracing/TracerProtocol.swift +++ b/Sources/Tracing/TracerProtocol.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @_exported import Instrumentation -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule // ==== ----------------------------------------------------------------------- // MARK: Tracer protocol @@ -23,20 +23,20 @@ /// A tracer capable of creating new trace spans. /// /// A tracer is a special kind of instrument with the added ability to start a ``Span``. -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext public protocol Tracer: LegacyTracer { /// The concrete type of span this tracer will be producing/ associatedtype Span: Tracing.Span - /// Start a new ``Span`` with the given `Baggage`. + /// Start a new ``Span`` with the given `ServiceContext`. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -46,7 +46,7 @@ public protocol Tracer: LegacyTracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -54,7 +54,7 @@ public protocol Tracer: LegacyTracer { /// - line: The file line where the span was started. func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -63,17 +63,17 @@ public protocol Tracer: LegacyTracer { ) -> Self.Span } -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal Baggage +@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) // for TaskLocal ServiceContext extension Tracer { - /// Start a new ``Span`` with the given `Baggage`. + /// Start a new ``Span`` with the given `ServiceContext`. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// - /// - Note: Prefer ``withSpan(_:baggage:ofKind:at:function:file:line:_:)-4o2b`` to start + /// - Note: Prefer ``withSpan(_:context:ofKind:at:function:file:line:_:)-4o2b`` to start /// a span as it automatically takes care of ending the span, and recording errors when thrown. /// Use `startSpan` iff you need to pass the span manually to a different /// location in your source code to end it. @@ -83,7 +83,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -91,7 +91,7 @@ extension Tracer { /// - line: The file line where the span was started. public func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -100,7 +100,7 @@ extension Tracer { ) -> Self.Span { self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -117,9 +117,9 @@ extension Tracer { /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -128,7 +128,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -139,7 +139,7 @@ extension Tracer { /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -149,7 +149,7 @@ extension Tracer { ) rethrows -> T { let span = self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -158,7 +158,7 @@ extension Tracer { ) defer { span.end() } do { - return try Baggage.$current.withValue(span.baggage) { + return try ServiceContext.$current.withValue(span.context) { try operation(span) } } catch { @@ -170,9 +170,9 @@ extension Tracer { /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -181,7 +181,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -192,7 +192,7 @@ extension Tracer { /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -201,7 +201,7 @@ extension Tracer { ) rethrows -> T { let span = self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: DefaultTracerClock.now, function: function, @@ -210,7 +210,7 @@ extension Tracer { ) defer { span.end() } do { - return try Baggage.$current.withValue(span.baggage) { + return try ServiceContext.$current.withValue(span.context) { try operation(span) } } catch { @@ -222,9 +222,9 @@ extension Tracer { /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -233,7 +233,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -244,7 +244,7 @@ extension Tracer { /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, function: String = #function, file fileID: String = #fileID, @@ -253,7 +253,7 @@ extension Tracer { ) async rethrows -> T { let span = self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: DefaultTracerClock.now, function: function, @@ -262,7 +262,7 @@ extension Tracer { ) defer { span.end() } do { - return try await Baggage.$current.withValue(span.baggage) { + return try await ServiceContext.$current.withValue(span.context) { try await operation(span) } } catch { @@ -274,9 +274,9 @@ extension Tracer { /// Start a new ``Span`` and automatically end when the `operation` completes, /// including recording the `error` in case the operation throws. /// - /// The current task-local `Baggage` is picked up and provided to the underlying tracer. - /// It is also possible to pass a specific `baggage` explicitly, in which case attempting - /// to pick up the task-local baggage is prevented. This can be useful when we know that + /// The current task-local `ServiceContext` is picked up and provided to the underlying tracer. + /// It is also possible to pass a specific `context` explicitly, in which case attempting + /// to pick up the task-local context is prevented. This can be useful when we know that /// we're about to start a top-level span, or if a span should be started from a different, /// stored away previously, /// @@ -285,7 +285,7 @@ extension Tracer { /// /// - Parameters: /// - operationName: The name of the operation being traced. This may be a handler function, database call, ... - /// - baggage: The `Baggage` providing information on where to start the new ``Span``. + /// - context: The `ServiceContext` providing information on where to start the new ``Span``. /// - kind: The ``SpanKind`` of the new ``Span``. /// - instant: the time instant at which the span started /// - function: The function name in which the span was started @@ -296,7 +296,7 @@ extension Tracer { /// - Throws: the error the `operation` has thrown (if any) public func withSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage = .current ?? .topLevel, + context: @autoclosure () -> ServiceContext = .current ?? .topLevel, ofKind kind: SpanKind = .internal, at instant: @autoclosure () -> some TracerInstant = DefaultTracerClock.now, function: String = #function, @@ -306,7 +306,7 @@ extension Tracer { ) async rethrows -> T { let span = self.startSpan( operationName, - baggage: baggage(), + context: context(), ofKind: kind, at: instant(), function: function, @@ -315,7 +315,7 @@ extension Tracer { ) defer { span.end() } do { - return try await Baggage.$current.withValue(span.baggage) { + return try await ServiceContext.$current.withValue(span.context) { try await operation(span) } } catch { diff --git a/Sources/Tracing/TracingTime.swift b/Sources/Tracing/TracingTime.swift index 627ca45..3d118e5 100644 --- a/Sources/Tracing/TracingTime.swift +++ b/Sources/Tracing/TracingTime.swift @@ -19,7 +19,7 @@ import Darwin #endif @_exported import Instrumentation -@_exported import InstrumentationBaggage +@_exported import ServiceContextModule public protocol TracerInstant: Comparable, Hashable, Sendable { /// Representation of this instant as the number of nanoseconds since UNIX Epoch (January 1st 1970) diff --git a/Sources/_TracingBenchmarks/SpanAttributesDSLBenchmark.swift b/Sources/_TracingBenchmarks/SpanAttributesDSLBenchmark.swift index 37b6ad7..c0f6b2f 100644 --- a/Sources/_TracingBenchmarks/SpanAttributesDSLBenchmark.swift +++ b/Sources/_TracingBenchmarks/SpanAttributesDSLBenchmark.swift @@ -72,7 +72,7 @@ public let SpanAttributesDSLBenchmarks: [BenchmarkInfo] = [ private var span: (any Tracing.Span)! private func setUp() { - span = InstrumentationSystem.legacyTracer.startAnySpan("something", baggage: .topLevel) + span = InstrumentationSystem.legacyTracer.startAnySpan("something", context: .topLevel) } private func tearDown() { @@ -86,14 +86,14 @@ func bench_empty(times: Int) throws {} func bench_makeSpan(times: Int) throws { for _ in 0 ..< times { - let span = InstrumentationSystem.legacyTracer.startAnySpan("something", baggage: .topLevel) + let span = InstrumentationSystem.legacyTracer.startAnySpan("something", context: .topLevel) _ = span } } func bench_startSpan_end(times: Int) throws { for _ in 0 ..< times { - let span = InstrumentationSystem.legacyTracer.startAnySpan("something", baggage: .topLevel) + let span = InstrumentationSystem.legacyTracer.startAnySpan("something", context: .topLevel) span.end() } } diff --git a/Tests/InstrumentationTests/InstrumentTests.swift b/Tests/InstrumentationTests/InstrumentTests.swift index b0fdb56..5da7067 100644 --- a/Tests/InstrumentationTests/InstrumentTests.swift +++ b/Tests/InstrumentationTests/InstrumentTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import XCTest final class InstrumentTests: XCTestCase { @@ -23,14 +23,14 @@ final class InstrumentTests: XCTestCase { SecondFakeTracer(), ]) - var baggage = Baggage.topLevel - instrument.extract([String: String](), into: &baggage, using: DictionaryExtractor()) + var context = ServiceContext.topLevel + instrument.extract([String: String](), into: &context, using: DictionaryExtractor()) - XCTAssertEqual(baggage[FirstFakeTracer.TraceIDKey.self], FirstFakeTracer.defaultTraceID) - XCTAssertEqual(baggage[SecondFakeTracer.TraceIDKey.self], SecondFakeTracer.defaultTraceID) + XCTAssertEqual(context[FirstFakeTracer.TraceIDKey.self], FirstFakeTracer.defaultTraceID) + XCTAssertEqual(context[SecondFakeTracer.TraceIDKey.self], SecondFakeTracer.defaultTraceID) var subsequentRequestHeaders = ["Accept": "application/json"] - instrument.inject(baggage, into: &subsequentRequestHeaders, using: DictionaryInjector()) + instrument.inject(context, into: &subsequentRequestHeaders, using: DictionaryInjector()) XCTAssertEqual(subsequentRequestHeaders, [ "Accept": "application/json", @@ -53,7 +53,7 @@ private struct DictionaryExtractor: Extractor { } private final class FirstFakeTracer: Instrument { - enum TraceIDKey: BaggageKey { + enum TraceIDKey: ServiceContextKey { typealias Value = String static let name: String? = "FirstFakeTraceID" @@ -62,23 +62,23 @@ private final class FirstFakeTracer: Instrument { static let headerName = "first-fake-trace-id" static let defaultTraceID = UUID().uuidString - func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier { - guard let traceID = baggage[TraceIDKey.self] else { return } + guard let traceID = context[TraceIDKey.self] else { return } injector.inject(traceID, forKey: FirstFakeTracer.headerName, into: &carrier) } - func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier { let traceID = extractor.extract(key: FirstFakeTracer.headerName, from: carrier) ?? FirstFakeTracer.defaultTraceID - baggage[TraceIDKey.self] = traceID + context[TraceIDKey.self] = traceID } } private final class SecondFakeTracer: Instrument { - enum TraceIDKey: BaggageKey { + enum TraceIDKey: ServiceContextKey { typealias Value = String static let name: String? = "SecondFakeTraceID" @@ -88,24 +88,24 @@ private final class SecondFakeTracer: Instrument { static let defaultTraceID = UUID().uuidString func inject( - _ baggage: Baggage, into carrier: inout Carrier, using injector: Inject + _ context: ServiceContext, into carrier: inout Carrier, using injector: Inject ) where Inject: Injector, Carrier == Inject.Carrier { - guard let traceID = baggage[TraceIDKey.self] else { return } + guard let traceID = context[TraceIDKey.self] else { return } injector.inject(traceID, forKey: SecondFakeTracer.headerName, into: &carrier) } func extract( - _ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract + _ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract ) where Extract: Extractor, Carrier == Extract.Carrier { let traceID = extractor.extract(key: SecondFakeTracer.headerName, from: carrier) ?? SecondFakeTracer.defaultTraceID - baggage[TraceIDKey.self] = traceID + context[TraceIDKey.self] = traceID } } diff --git a/Tests/InstrumentationTests/InstrumentationSystemTests.swift b/Tests/InstrumentationTests/InstrumentationSystemTests.swift index 9334242..5955ad2 100644 --- a/Tests/InstrumentationTests/InstrumentationSystemTests.swift +++ b/Tests/InstrumentationTests/InstrumentationSystemTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import XCTest extension InstrumentationSystem { @@ -50,7 +50,7 @@ final class InstrumentationSystemTests: XCTestCase { private final class FakeTracer: Instrument { func inject( - _ baggage: Baggage, + _ context: ServiceContext, into carrier: inout Carrier, using injector: Inject ) @@ -60,7 +60,7 @@ private final class FakeTracer: Instrument { func extract( _ carrier: Carrier, - into baggage: inout Baggage, + into context: inout ServiceContext, using extractor: Extract ) where @@ -70,7 +70,7 @@ private final class FakeTracer: Instrument { private final class FakeInstrument: Instrument { func inject( - _ baggage: Baggage, + _ context: ServiceContext, into carrier: inout Carrier, using injector: Inject ) @@ -80,7 +80,7 @@ private final class FakeInstrument: Instrument { func extract( _ carrier: Carrier, - into baggage: inout Baggage, + into context: inout ServiceContext, using extractor: Extract ) where diff --git a/Tests/TracingTests/ActorTracingTests.swift b/Tests/TracingLegacyTests/ActorTracingTests.swift similarity index 96% rename from Tests/TracingTests/ActorTracingTests.swift rename to Tests/TracingLegacyTests/ActorTracingTests.swift index 5ace8a7..1e88f86 100644 --- a/Tests/TracingTests/ActorTracingTests.swift +++ b/Tests/TracingLegacyTests/ActorTracingTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest diff --git a/Tests/TracingTests/DynamicTracepointTracerTests.swift b/Tests/TracingLegacyTests/DynamicTracepointTracerTests.swift similarity index 87% rename from Tests/TracingTests/DynamicTracepointTracerTests.swift rename to Tests/TracingLegacyTests/DynamicTracepointTracerTests.swift index 4ba7499..122a3d2 100644 --- a/Tests/TracingTests/DynamicTracepointTracerTests.swift +++ b/Tests/TracingLegacyTests/DynamicTracepointTracerTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest @@ -67,10 +67,10 @@ final class DynamicTracepointTracerTests: XCTestCase { #endif for span in tracer.spans { - XCTAssertEqual(span.baggage.traceID, "trace-id-fake-\(fileID)-\(fakeLine)") + XCTAssertEqual(span.context.traceID, "trace-id-fake-\(fileID)-\(fakeLine)") } - XCTAssertEqual(tracer.spans[0].baggage.spanID, "span-id-fake-\(fileID)-\(fakeLine)") - XCTAssertEqual(tracer.spans[1].baggage.spanID, "span-id-fake-\(fileID)-\(fakeNextLine)") + XCTAssertEqual(tracer.spans[0].context.spanID, "span-id-fake-\(fileID)-\(fakeLine)") + XCTAssertEqual(tracer.spans[1].context.spanID, "span-id-fake-\(fileID)-\(fakeNextLine)") } func test_adhoc_enableByFunction() { @@ -92,10 +92,10 @@ final class DynamicTracepointTracerTests: XCTestCase { XCTAssertEqual(tracer.spans.count, 2) for span in tracer.spans { - XCTAssertEqual(span.baggage.traceID, "trace-id-fake-\(fileID)-\(fakeLine)") + XCTAssertEqual(span.context.traceID, "trace-id-fake-\(fileID)-\(fakeLine)") } - XCTAssertEqual(tracer.spans[0].baggage.spanID, "span-id-fake-\(fileID)-\(fakeLine)") - XCTAssertEqual(tracer.spans[1].baggage.spanID, "span-id-fake-\(fileID)-\(fakeNextLine)") + XCTAssertEqual(tracer.spans[0].context.spanID, "span-id-fake-\(fileID)-\(fakeLine)") + XCTAssertEqual(tracer.spans[1].context.spanID, "span-id-fake-\(fileID)-\(fakeNextLine)") } func logic(fakeLine: UInt) { @@ -167,7 +167,7 @@ final class DynamicTracepointTestTracer: LegacyTracer { func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -182,7 +182,7 @@ final class DynamicTracepointTestTracer: LegacyTracer { let span = TracepointSpan( operationName: operationName, startTime: instant(), - baggage: baggage(), + context: context(), kind: kind, file: fileID, line: line, @@ -200,12 +200,12 @@ final class DynamicTracepointTestTracer: LegacyTracer { } // else, perhaps there is already an active span, if so, attach to it - guard let baggage = Baggage.current else { // TODO: we could make this such that we only ever once pick-up 🧳 + guard let context = ServiceContext.current else { return false } - guard baggage.traceID != nil else { - // no span is active, return the baggage though + guard context.traceID != nil else { + // no span is active, return the context though return false } @@ -238,13 +238,13 @@ final class DynamicTracepointTestTracer: LegacyTracer { func forceFlush() {} - func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) where Extract: Extractor, Extract.Carrier == Carrier { + func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Extract.Carrier == Carrier { let traceID = extractor.extract(key: "trace-id", from: carrier) ?? UUID().uuidString - baggage.traceID = traceID + context.traceID = traceID } - func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Inject.Carrier == Carrier { - guard let traceID = baggage.traceID else { + func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Inject.Carrier == Carrier { + guard let traceID = context.traceID else { return } injector.inject(traceID, forKey: "trace-id", into: &carrier) @@ -262,7 +262,7 @@ extension DynamicTracepointTestTracer { private(set) var endTimestampNanosSinceEpoch: UInt64? public var operationName: String - private(set) var baggage: Baggage + private(set) var context: ServiceContext private(set) var isRecording: Bool = false let onEnd: (TracepointSpan) -> Void @@ -271,7 +271,7 @@ extension DynamicTracepointTestTracer { let span = TracepointSpan( operationName: "", startTime: DefaultTracerClock().now, - baggage: .topLevel, + context: .topLevel, kind: .internal, file: fileID, line: line, @@ -283,7 +283,7 @@ extension DynamicTracepointTestTracer { init(operationName: String, startTime: Instant, - baggage: Baggage, + context: ServiceContext, kind: SpanKind, file fileID: String, line: UInt, @@ -291,17 +291,17 @@ extension DynamicTracepointTestTracer { { self.operationName = operationName self.startTimestampNanosSinceEpoch = startTime.nanosecondsSinceEpoch - self.baggage = baggage + self.context = context self.onEnd = onEnd self.kind = kind // inherit or make a new traceID: - if baggage.traceID == nil { - self.baggage.traceID = "trace-id-fake-\(fileID)-\(line)" + if context.traceID == nil { + self.context.traceID = "trace-id-fake-\(fileID)-\(line)" } // always make up a new spanID: - self.baggage.spanID = "span-id-fake-\(fileID)-\(line)" + self.context.spanID = "span-id-fake-\(fileID)-\(line)" } var attributes: Tracing.SpanAttributes = [:] @@ -334,7 +334,7 @@ extension DynamicTracepointTestTracer: Tracer { typealias Span = TracepointSpan func startSpan(_ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: Tracing.SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -349,7 +349,7 @@ extension DynamicTracepointTestTracer: Tracer { let span = TracepointSpan( operationName: operationName, startTime: instant(), - baggage: baggage(), + context: context(), kind: kind, file: fileID, line: line, diff --git a/Tests/TracingTests/SpanTests.swift b/Tests/TracingLegacyTests/SpanTests.swift similarity index 94% rename from Tests/TracingTests/SpanTests.swift rename to Tests/TracingLegacyTests/SpanTests.swift index 598f48b..3754891 100644 --- a/Tests/TracingTests/SpanTests.swift +++ b/Tests/TracingLegacyTests/SpanTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest @@ -81,7 +81,7 @@ final class SpanTests: XCTestCase { } func testSpanAttributeIsExpressibleByArrayLiteral() { - let s = InstrumentationSystem.legacyTracer.startAnySpan("", baggage: .topLevel) + let s = InstrumentationSystem.legacyTracer.startAnySpan("", context: .topLevel) s.attributes["hi"] = [42, 21] s.attributes["hi"] = [42.10, 21.0] s.attributes["hi"] = [true, false] @@ -95,7 +95,7 @@ final class SpanTests: XCTestCase { InstrumentationSystem.bootstrapInternal(NoOpTracer()) } - let s = InstrumentationSystem.legacyTracer.startAnySpan("", baggage: .topLevel) + let s = InstrumentationSystem.legacyTracer.startAnySpan("", context: .topLevel) var attrs = s.attributes attrs["one"] = 42 attrs["two"] = [1, 2, 34] @@ -181,21 +181,21 @@ final class SpanTests: XCTestCase { } func testSpanParentConvenience() { - var parentBaggage = Baggage.topLevel + var parentBaggage = ServiceContext.topLevel parentBaggage[TestBaggageContextKey.self] = "test" let parent = TestSpan( operationName: "client", startTime: DefaultTracerClock.now, - baggage: parentBaggage, + context: parentBaggage, kind: .client, onEnd: { _ in } ) - let childBaggage = Baggage.topLevel + let childBaggage = ServiceContext.topLevel let child = TestSpan( operationName: "server", startTime: DefaultTracerClock.now, - baggage: childBaggage, + context: childBaggage, kind: .server, onEnd: { _ in } ) @@ -205,7 +205,7 @@ final class SpanTests: XCTestCase { child.addLink(parent, attributes: attributes) XCTAssertEqual(child.links.count, 1) - XCTAssertEqual(child.links[0].baggage[TestBaggageContextKey.self], "test") + XCTAssertEqual(child.links[0].context[TestBaggageContextKey.self], "test") XCTAssertEqual(child.links[0].attributes.sampleHttp.statusCode, 418) guard case .some(.int64(let statusCode)) = child.links[0].attributes["http.status_code"]?.toSpanAttribute() else { XCTFail("Expected int value for http.status_code") @@ -215,21 +215,21 @@ final class SpanTests: XCTestCase { } func testSpanAttributeSetterGetter() { - var parentBaggage = Baggage.topLevel + var parentBaggage = ServiceContext.topLevel parentBaggage[TestBaggageContextKey.self] = "test" let parent = TestSpan( operationName: "client", startTime: DefaultTracerClock.now, - baggage: parentBaggage, + context: parentBaggage, kind: .client, onEnd: { _ in } ) - let childBaggage = Baggage.topLevel + let childBaggage = ServiceContext.topLevel let child = TestSpan( operationName: "server", startTime: DefaultTracerClock.now, - baggage: childBaggage, + context: childBaggage, kind: .server, onEnd: { _ in } ) @@ -239,7 +239,7 @@ final class SpanTests: XCTestCase { child.addLink(parent, attributes: attributes) XCTAssertEqual(child.links.count, 1) - XCTAssertEqual(child.links[0].baggage[TestBaggageContextKey.self], "test") + XCTAssertEqual(child.links[0].context[TestBaggageContextKey.self], "test") XCTAssertEqual(child.links[0].attributes.sampleHttp.statusCode, 418) guard case .some(.int32(let statusCode)) = child.links[0].attributes["http.status_code"]?.toSpanAttribute() else { XCTFail("Expected int value for http.status_code") @@ -304,6 +304,6 @@ public struct CustomAttributeValue: Equatable, Sendable, CustomStringConvertible } } -private struct TestBaggageContextKey: BaggageKey { +private struct TestBaggageContextKey: ServiceContextKey { typealias Value = String } diff --git a/Tests/TracingTests/TestTracer.swift b/Tests/TracingLegacyTests/TestTracer.swift similarity index 86% rename from Tests/TracingTests/TestTracer.swift rename to Tests/TracingLegacyTests/TestTracer.swift index 46dcee5..ba2bfce 100644 --- a/Tests/TracingTests/TestTracer.swift +++ b/Tests/TracingLegacyTests/TestTracer.swift @@ -15,7 +15,7 @@ import Dispatch import Foundation import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing /// Only intended to be used in single-threaded testing. @@ -25,7 +25,7 @@ final class TestTracer: LegacyTracer { func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -35,7 +35,7 @@ final class TestTracer: LegacyTracer { let span = TestSpan( operationName: operationName, startTime: instant(), - baggage: baggage(), + context: context(), kind: kind, onEnd: self.onEndSpan ) @@ -45,21 +45,21 @@ final class TestTracer: LegacyTracer { public func forceFlush() {} - func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier { let traceID = extractor.extract(key: "trace-id", from: carrier) ?? UUID().uuidString - baggage.traceID = traceID + context.traceID = traceID } - func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier { - guard let traceID = baggage.traceID else { return } + guard let traceID = context.traceID else { return } injector.inject(traceID, forKey: "trace-id", into: &carrier) } } @@ -68,7 +68,7 @@ final class TestTracer: LegacyTracer { extension TestTracer: Tracer { func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -78,7 +78,7 @@ extension TestTracer: Tracer { let span = TestSpan( operationName: operationName, startTime: instant(), - baggage: baggage(), + context: context(), kind: kind, onEnd: self.onEndSpan ) @@ -89,16 +89,16 @@ extension TestTracer: Tracer { #endif extension TestTracer { - enum TraceIDKey: BaggageKey { + enum TraceIDKey: ServiceContextKey { typealias Value = String } - enum SpanIDKey: BaggageKey { + enum SpanIDKey: ServiceContextKey { typealias Value = String } } -extension Baggage { +extension ServiceContext { var traceID: String? { get { self[TestTracer.TraceIDKey.self] @@ -130,7 +130,7 @@ final class TestSpan: Span { private(set) var recordedErrors: [(Error, SpanAttributes)] = [] var operationName: String - let baggage: Baggage + let context: ServiceContext private(set) var events = [SpanEvent]() { didSet { @@ -153,13 +153,13 @@ final class TestSpan: Span { init( operationName: String, startTime: Instant, - baggage: Baggage, + context: ServiceContext, kind: SpanKind, onEnd: @escaping (TestSpan) -> Void ) { self.operationName = operationName self.startTimestampNanosSinceEpoch = startTime.nanosecondsSinceEpoch - self.baggage = baggage + self.context = context self.onEnd = onEnd self.kind = kind } diff --git a/Tests/TracingTests/TracedLock.swift b/Tests/TracingLegacyTests/TracedLock.swift similarity index 76% rename from Tests/TracingTests/TracedLock.swift rename to Tests/TracingLegacyTests/TracedLock.swift index 7f4af14..6a641e1 100644 --- a/Tests/TracingTests/TracedLock.swift +++ b/Tests/TracingLegacyTests/TracedLock.swift @@ -14,7 +14,7 @@ import Foundation import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing final class TracedLock { @@ -28,21 +28,21 @@ final class TracedLock { self.underlyingLock = NSLock() } - func lock(baggage: Baggage) { + func lock(context: ServiceContext) { // time here self.underlyingLock.lock() - self.activeSpan = InstrumentationSystem.legacyTracer.startAnySpan(self.name, baggage: baggage) + self.activeSpan = InstrumentationSystem.legacyTracer.startAnySpan(self.name, context: context) } - func unlock(baggage: Baggage) { + func unlock(context: ServiceContext) { self.activeSpan?.end() self.activeSpan = nil self.underlyingLock.unlock() } - func withLock(baggage: Baggage, _ closure: () -> Void) { - self.lock(baggage: baggage) - defer { self.unlock(baggage: baggage) } + func withLock(context: ServiceContext, _ closure: () -> Void) { + self.lock(context: context) + defer { self.unlock(context: context) } closure() } } diff --git a/Tests/TracingTests/TracedLockTests.swift b/Tests/TracingLegacyTests/TracedLockTests.swift similarity index 85% rename from Tests/TracingTests/TracedLockTests.swift rename to Tests/TracingLegacyTests/TracedLockTests.swift index 18e8a78..f5831cb 100644 --- a/Tests/TracingTests/TracedLockTests.swift +++ b/Tests/TracingLegacyTests/TracedLockTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest @@ -31,11 +31,11 @@ final class TracedLockTests: XCTestCase { func launchTask(_ name: String) { DispatchQueue.global().async { - var baggage = Baggage.topLevel - baggage[TaskIDKey.self] = name + var context = ServiceContext.topLevel + context[TaskIDKey.self] = name - lock.lock(baggage: baggage) - lock.unlock(baggage: baggage) + lock.lock(context: context) + lock.unlock(context: context) } } launchTask("one") @@ -50,7 +50,7 @@ final class TracedLockTests: XCTestCase { // ==== ------------------------------------------------------------------------ // MARK: test keys -enum TaskIDKey: BaggageKey { +enum TaskIDKey: ServiceContextKey { typealias Value = String static let name: String? = "LockedOperationNameKey" } @@ -62,7 +62,7 @@ enum TaskIDKey: BaggageKey { private final class TracedLockPrintlnTracer: LegacyTracer { func startAnySpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -73,14 +73,14 @@ private final class TracedLockPrintlnTracer: LegacyTracer { operationName: operationName, startTime: instant(), kind: kind, - baggage: baggage() + context: context() ) } public func forceFlush() {} func inject( - _ baggage: Baggage, + _ context: ServiceContext, into carrier: inout Carrier, using injector: Inject ) @@ -90,7 +90,7 @@ private final class TracedLockPrintlnTracer: LegacyTracer { func extract( _ carrier: Carrier, - into baggage: inout Baggage, + into context: inout ServiceContext, using extractor: Extract ) where @@ -106,7 +106,7 @@ private final class TracedLockPrintlnTracer: LegacyTracer { private(set) var endTimeMillis: UInt64? var operationName: String - let baggage: Baggage + let context: ServiceContext private var links = [SpanLink]() @@ -128,14 +128,14 @@ private final class TracedLockPrintlnTracer: LegacyTracer { operationName: String, startTime: Instant, kind: SpanKind, - baggage: Baggage + context: ServiceContext ) { self.operationName = operationName self.startTimeMillis = startTime.millisecondsSinceEpoch - self.baggage = baggage + self.context = context self.kind = kind - print(" span [\(self.operationName): \(self.baggage[TaskIDKey.self] ?? "no-name")] @ \(self.startTimeMillis): start") + print(" span [\(self.operationName): \(self.context[TaskIDKey.self] ?? "no-name")] @ \(self.startTimeMillis): start") } func setStatus(_ status: SpanStatus) { @@ -156,7 +156,7 @@ private final class TracedLockPrintlnTracer: LegacyTracer { func end(at instant: @autoclosure () -> Instant) { let time = instant() self.endTimeMillis = time.millisecondsSinceEpoch - print(" span [\(self.operationName): \(self.baggage[TaskIDKey.self] ?? "no-name")] @ \(time): end") + print(" span [\(self.operationName): \(self.context[TaskIDKey.self] ?? "no-name")] @ \(time): end") } } } @@ -165,7 +165,7 @@ private final class TracedLockPrintlnTracer: LegacyTracer { extension TracedLockPrintlnTracer: Tracer { func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -176,7 +176,7 @@ extension TracedLockPrintlnTracer: Tracer { operationName: operationName, startTime: instant(), kind: kind, - baggage: baggage() + context: context() ) } } diff --git a/Tests/TracingTests/TracerTests+swift57.swift b/Tests/TracingLegacyTests/TracerTests+swift57.swift similarity index 88% rename from Tests/TracingTests/TracerTests+swift57.swift rename to Tests/TracingLegacyTests/TracerTests+swift57.swift index 104caed..17af99c 100644 --- a/Tests/TracingTests/TracerTests+swift57.swift +++ b/Tests/TracingLegacyTests/TracerTests+swift57.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest @@ -26,7 +26,7 @@ final class SampleSwift57Tracer: Tracer { func startSpan( _ operationName: String, - baggage: @autoclosure () -> Baggage, + context: @autoclosure () -> ServiceContext, ofKind kind: SpanKind, at instant: @autoclosure () -> Instant, function: String, @@ -36,7 +36,7 @@ final class SampleSwift57Tracer: Tracer { let span = SampleSwift57Span( operationName: operationName, startTime: instant(), - baggage: baggage(), + context: context(), kind: kind, onEnd: self.onEndSpan ) @@ -46,13 +46,13 @@ final class SampleSwift57Tracer: Tracer { public func forceFlush() {} - func extract(_ carrier: Carrier, into baggage: inout Baggage, using extractor: Extract) + func extract(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract) where Extract: Extractor, Carrier == Extract.Carrier {} - func inject(_ baggage: Baggage, into carrier: inout Carrier, using injector: Inject) + func inject(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject) where Inject: Injector, Carrier == Inject.Carrier @@ -71,7 +71,7 @@ final class SampleSwift57Span: Span { private(set) var recordedErrors: [(Error, SpanAttributes)] = [] var operationName: String - let baggage: Baggage + let context: ServiceContext private(set) var events = [SpanEvent]() { didSet { @@ -94,13 +94,13 @@ final class SampleSwift57Span: Span { init( operationName: String, startTime: Instant, - baggage: Baggage, + context: ServiceContext, kind: SpanKind, onEnd: @escaping (SampleSwift57Span) -> Void ) { self.operationName = operationName self.startTimeNanoseconds = startTime.nanosecondsSinceEpoch - self.baggage = baggage + self.context = context self.onEnd = onEnd self.kind = kind } diff --git a/Tests/TracingTests/TracerTests.swift b/Tests/TracingLegacyTests/TracerTests.swift similarity index 87% rename from Tests/TracingTests/TracerTests.swift rename to Tests/TracingLegacyTests/TracerTests.swift index 963443b..a6cfeff 100644 --- a/Tests/TracingTests/TracerTests.swift +++ b/Tests/TracingLegacyTests/TracerTests.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// @testable import Instrumentation -import InstrumentationBaggage +import ServiceContextModule import Tracing import XCTest @@ -39,22 +39,22 @@ final class TracerTests: XCTestCase { XCTAssertEqual(tracer.spans.count, 2) for span in tracer.spans { - XCTAssertEqual(span.baggage.traceID, "test") + XCTAssertEqual(span.context.traceID, "test") } } func testContextPropagationWithNoOpSpan() { let httpServer = FakeHTTPServer { _, _, client -> FakeHTTPResponse in - var baggage = Baggage.topLevel - baggage.traceID = "test" - client.performRequest(baggage, request: FakeHTTPRequest(path: "/test", headers: [])) + var context = ServiceContext.topLevel + context.traceID = "test" + client.performRequest(context, request: FakeHTTPRequest(path: "/test", headers: [])) return FakeHTTPResponse(status: 418) } httpServer.receive(FakeHTTPRequest(path: "/", headers: [("trace-id", "test")])) - XCTAssertEqual(httpServer.client.baggages.count, 1) - XCTAssertEqual(httpServer.client.baggages.first?.traceID, "test") + XCTAssertEqual(httpServer.client.contexts.count, 1) + XCTAssertEqual(httpServer.client.contexts.first?.traceID, "test") } func testWithSpan_success() { @@ -73,11 +73,11 @@ final class TracerTests: XCTestCase { } #if swift(>=5.7.0) - let value = tracer.withSpan("hello", baggage: .topLevel) { _ in + let value = tracer.withSpan("hello", context: .topLevel) { _ in "yes" } #else - let value = tracer.withAnySpan("hello", baggage: .topLevel) { _ in + let value = tracer.withAnySpan("hello", context: .topLevel) { _ in "yes" } #endif @@ -97,7 +97,7 @@ final class TracerTests: XCTestCase { tracer.onEndSpan = { _ in spanEnded = true } do { - _ = try tracer.withAnySpan("hello", baggage: .topLevel) { _ in + _ = try tracer.withAnySpan("hello", context: .topLevel) { _ in throw ExampleSpanError() } } catch { @@ -128,7 +128,7 @@ final class TracerTests: XCTestCase { } let value = tracer.withAnySpan("hello") { (span: any Tracing.Span) -> String in - XCTAssertEqual(span.baggage.traceID, Baggage.current?.traceID) + XCTAssertEqual(span.context.traceID, ServiceContext.current?.traceID) return operation(span: span) } @@ -188,7 +188,7 @@ final class TracerTests: XCTestCase { try self.testAsync { let value = try await tracer.withAnySpan("hello") { (span: any Tracing.Span) -> String in - XCTAssertEqual(span.baggage.traceID, Baggage.current?.traceID) + XCTAssertEqual(span.context.traceID, ServiceContext.current?.traceID) return try await operation(span: span) } @@ -217,11 +217,11 @@ final class TracerTests: XCTestCase { } self.testAsync { - var fromNonAsyncWorld = Baggage.topLevel + var fromNonAsyncWorld = ServiceContext.topLevel fromNonAsyncWorld.traceID = "1234-5678" - let value = await tracer.withAnySpan("hello", baggage: fromNonAsyncWorld) { (span: any Tracing.Span) -> String in - XCTAssertEqual(span.baggage.traceID, Baggage.current?.traceID) - XCTAssertEqual(span.baggage.traceID, fromNonAsyncWorld.traceID) + let value = await tracer.withAnySpan("hello", context: fromNonAsyncWorld) { (span: any Tracing.Span) -> String in + XCTAssertEqual(span.context.traceID, ServiceContext.current?.traceID) + XCTAssertEqual(span.context.traceID, fromNonAsyncWorld.traceID) return await operation(span: span) } @@ -358,12 +358,12 @@ final class TracerTests: XCTestCase { #if swift(>=5.7.0) tracer.withSpan("") { _ in } tracer.withSpan("", at: clock.now) { _ in } - tracer.withSpan("", baggage: .topLevel) { _ in } + tracer.withSpan("", context: .topLevel) { _ in } #endif tracer.withAnySpan("") { _ in } tracer.withAnySpan("", at: clock.now) { _ in } - tracer.withAnySpan("", baggage: .topLevel) { _ in } + tracer.withAnySpan("", context: .topLevel) { _ in } } #if swift(>=5.7.0) @@ -432,7 +432,7 @@ struct FakeHTTPResponse { } struct FakeHTTPServer { - typealias Handler = (Baggage, FakeHTTPRequest, FakeHTTPClient) -> FakeHTTPResponse + typealias Handler = (ServiceContext, FakeHTTPRequest, FakeHTTPClient) -> FakeHTTPResponse private let catchAllHandler: Handler let client: FakeHTTPClient @@ -443,16 +443,16 @@ struct FakeHTTPServer { } func receive(_ request: FakeHTTPRequest) { - var baggage = Baggage.topLevel - InstrumentationSystem.instrument.extract(request.headers, into: &baggage, using: HTTPHeadersExtractor()) + var context = ServiceContext.topLevel + InstrumentationSystem.instrument.extract(request.headers, into: &context, using: HTTPHeadersExtractor()) #if swift(>=5.7.0) - let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", baggage: baggage) + let span = InstrumentationSystem.tracer.startSpan("GET \(request.path)", context: context) #else - let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", baggage: baggage) + let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", context: context) #endif - let response = self.catchAllHandler(span.baggage, request, self.client) + let response = self.catchAllHandler(span.context, request, self.client) span.attributes["http.status"] = response.status span.end() @@ -462,17 +462,17 @@ struct FakeHTTPServer { // MARK: - Fake HTTP Client final class FakeHTTPClient { - private(set) var baggages = [Baggage]() + private(set) var contexts = [ServiceContext]() - func performRequest(_ baggage: Baggage, request: FakeHTTPRequest) { + func performRequest(_ context: ServiceContext, request: FakeHTTPRequest) { var request = request #if swift(>=5.7.0) - let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", baggage: baggage) + let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", context: context) #else - let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", baggage: baggage) + let span = InstrumentationSystem.legacyTracer.startAnySpan("GET \(request.path)", context: context) #endif - self.baggages.append(span.baggage) - InstrumentationSystem.instrument.inject(baggage, into: &request.headers, using: HTTPHeadersInjector()) + self.contexts.append(span.context) + InstrumentationSystem.instrument.inject(context, into: &request.headers, using: HTTPHeadersInjector()) span.end() } } diff --git a/Tests/TracingTests/TracerTimeTests.swift b/Tests/TracingLegacyTests/TracerTimeTests.swift similarity index 100% rename from Tests/TracingTests/TracerTimeTests.swift rename to Tests/TracingLegacyTests/TracerTimeTests.swift diff --git a/Tests/TracingTests/TracingInstrumentationSystemTests.swift b/Tests/TracingLegacyTests/TracingInstrumentationSystemTests.swift similarity index 100% rename from Tests/TracingTests/TracingInstrumentationSystemTests.swift rename to Tests/TracingLegacyTests/TracingInstrumentationSystemTests.swift diff --git a/Tests/TracingTests/LegacyBaggageTests.swift b/Tests/TracingTests/LegacyBaggageTests.swift new file mode 100644 index 0000000..0c459af --- /dev/null +++ b/Tests/TracingTests/LegacyBaggageTests.swift @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing open source project +// +// Copyright (c) 2020-2023 Apple Inc. and the Swift Distributed Tracing project +// authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@testable import Instrumentation +import ServiceContextModule +import InstrumentationBaggage // legacy module, kept for easier migrations +import Tracing +import XCTest + +final class ActorTracingTests: XCTestCase { + override class func tearDown() { + super.tearDown() + InstrumentationSystem.bootstrapInternal(nil) + } +} + +func test() { + // Testing that the baggage type is just a typealias + let baggage = Baggage.topLevel // import InstrumentationBaggage + let span = startSpan("something", context: baggage) +} \ No newline at end of file diff --git a/scripts/validate_license_headers.sh b/scripts/validate_license_headers.sh index ef99c15..a48401e 100755 --- a/scripts/validate_license_headers.sh +++ b/scripts/validate_license_headers.sh @@ -36,7 +36,7 @@ function replace_acceptable_years() { } printf "=> Checking license headers\n" -tmp=$(mktemp /tmp/.swift-baggage-context-soundness_XXXXXX) +tmp=$(mktemp /tmp/.swift-context-context-soundness_XXXXXX) for language in swift-or-c bash dtrace; do printf " * $language... " From 3677cbc5717ab2da4f52d11d216f9c31ce7513a8 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 31 May 2023 21:10:39 +0200 Subject: [PATCH 2/2] Remove the legacy test, cannot test with deprecation warnings (as errors) --- Package.swift | 7 ---- .../ActorTracingTests.swift | 0 .../DynamicTracepointTracerTests.swift | 0 Tests/TracingTests/LegacyBaggageTests.swift | 32 ------------------- .../SpanTests.swift | 0 .../TestTracer.swift | 0 .../TracedLock.swift | 0 .../TracedLockTests.swift | 0 .../TracerTests+swift57.swift | 0 .../TracerTests.swift | 0 .../TracerTimeTests.swift | 0 .../TracingInstrumentationSystemTests.swift | 0 12 files changed, 39 deletions(-) rename Tests/{TracingLegacyTests => TracingTests}/ActorTracingTests.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/DynamicTracepointTracerTests.swift (100%) delete mode 100644 Tests/TracingTests/LegacyBaggageTests.swift rename Tests/{TracingLegacyTests => TracingTests}/SpanTests.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TestTracer.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracedLock.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracedLockTests.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracerTests+swift57.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracerTests.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracerTimeTests.swift (100%) rename Tests/{TracingLegacyTests => TracingTests}/TracingInstrumentationSystemTests.swift (100%) diff --git a/Package.swift b/Package.swift index 7ded2da..7528f62 100644 --- a/Package.swift +++ b/Package.swift @@ -50,13 +50,6 @@ let package = Package( .target(name: "Tracing"), ] ), - .testTarget( - name: "TracingLegacyTests", - dependencies: [ - .target(name: "Tracing"), - .product(name: "InstrumentationBaggage", package: "swift-service-context"), - ] - ), // ==== -------------------------------------------------------------------------------------------------------- // MARK: Performance / Benchmarks diff --git a/Tests/TracingLegacyTests/ActorTracingTests.swift b/Tests/TracingTests/ActorTracingTests.swift similarity index 100% rename from Tests/TracingLegacyTests/ActorTracingTests.swift rename to Tests/TracingTests/ActorTracingTests.swift diff --git a/Tests/TracingLegacyTests/DynamicTracepointTracerTests.swift b/Tests/TracingTests/DynamicTracepointTracerTests.swift similarity index 100% rename from Tests/TracingLegacyTests/DynamicTracepointTracerTests.swift rename to Tests/TracingTests/DynamicTracepointTracerTests.swift diff --git a/Tests/TracingTests/LegacyBaggageTests.swift b/Tests/TracingTests/LegacyBaggageTests.swift deleted file mode 100644 index 0c459af..0000000 --- a/Tests/TracingTests/LegacyBaggageTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Tracing open source project -// -// Copyright (c) 2020-2023 Apple Inc. and the Swift Distributed Tracing project -// authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@testable import Instrumentation -import ServiceContextModule -import InstrumentationBaggage // legacy module, kept for easier migrations -import Tracing -import XCTest - -final class ActorTracingTests: XCTestCase { - override class func tearDown() { - super.tearDown() - InstrumentationSystem.bootstrapInternal(nil) - } -} - -func test() { - // Testing that the baggage type is just a typealias - let baggage = Baggage.topLevel // import InstrumentationBaggage - let span = startSpan("something", context: baggage) -} \ No newline at end of file diff --git a/Tests/TracingLegacyTests/SpanTests.swift b/Tests/TracingTests/SpanTests.swift similarity index 100% rename from Tests/TracingLegacyTests/SpanTests.swift rename to Tests/TracingTests/SpanTests.swift diff --git a/Tests/TracingLegacyTests/TestTracer.swift b/Tests/TracingTests/TestTracer.swift similarity index 100% rename from Tests/TracingLegacyTests/TestTracer.swift rename to Tests/TracingTests/TestTracer.swift diff --git a/Tests/TracingLegacyTests/TracedLock.swift b/Tests/TracingTests/TracedLock.swift similarity index 100% rename from Tests/TracingLegacyTests/TracedLock.swift rename to Tests/TracingTests/TracedLock.swift diff --git a/Tests/TracingLegacyTests/TracedLockTests.swift b/Tests/TracingTests/TracedLockTests.swift similarity index 100% rename from Tests/TracingLegacyTests/TracedLockTests.swift rename to Tests/TracingTests/TracedLockTests.swift diff --git a/Tests/TracingLegacyTests/TracerTests+swift57.swift b/Tests/TracingTests/TracerTests+swift57.swift similarity index 100% rename from Tests/TracingLegacyTests/TracerTests+swift57.swift rename to Tests/TracingTests/TracerTests+swift57.swift diff --git a/Tests/TracingLegacyTests/TracerTests.swift b/Tests/TracingTests/TracerTests.swift similarity index 100% rename from Tests/TracingLegacyTests/TracerTests.swift rename to Tests/TracingTests/TracerTests.swift diff --git a/Tests/TracingLegacyTests/TracerTimeTests.swift b/Tests/TracingTests/TracerTimeTests.swift similarity index 100% rename from Tests/TracingLegacyTests/TracerTimeTests.swift rename to Tests/TracingTests/TracerTimeTests.swift diff --git a/Tests/TracingLegacyTests/TracingInstrumentationSystemTests.swift b/Tests/TracingTests/TracingInstrumentationSystemTests.swift similarity index 100% rename from Tests/TracingLegacyTests/TracingInstrumentationSystemTests.swift rename to Tests/TracingTests/TracingInstrumentationSystemTests.swift