Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@ let package = Package(
.library(name: "Tracing", targets: ["Tracing"]),
.library(name: "InMemoryTracing", targets: ["InMemoryTracing"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-service-context.git", from: "1.1.0")
],
targets: [
// ==== --------------------------------------------------------------------------------------------------------
// MARK: Instrumentation

.target(
name: "Instrumentation",
dependencies: [
.product(name: "ServiceContextModule", package: "swift-service-context")
]
name: "Instrumentation"
),
.testTarget(
name: "InstrumentationTests",
Expand All @@ -34,7 +28,6 @@ let package = Package(
.target(
name: "Tracing",
dependencies: [
.product(name: "ServiceContextModule", package: "swift-service-context"),
.target(name: "Instrumentation"),
.target(name: "_CWASI", condition: .when(platforms: [.wasi])),
]
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ While Swift Distributed Tracing allows building all kinds of _instruments_, whic

---

This project uses the context propagation type defined independently in:

- [swift-service-context](https://github.com/apple/swift-service-context) -- [`ServiceContext`](https://swiftpackageindex.com/apple/swift-service-context/main/documentation/servicecontextmodule/servicecontext) (zero dependencies)
This project defines `InstrumentationContext`, the context propagation type used to carry values across
concurrent Swift code. [swift-service-context](https://github.com/apple/swift-service-context) is a thin
compatibility package that keeps the type's former name, `ServiceContext`, working for existing adopters.

---

Expand Down
6 changes: 3 additions & 3 deletions Sources/InMemoryTracing/InMemorySpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Tracing
public struct InMemorySpan: Span {

/// The service context of the span.
public let context: ServiceContext
public let context: InstrumentationContext
/// The in-memory span context.
public var spanContext: InMemorySpanContext {
context.inMemorySpanContext!
Expand Down Expand Up @@ -66,7 +66,7 @@ public struct InMemorySpan: Span {
/// - onEnd: A closure invoked when the span completes, providing access to the finished span.
public init(
operationName: String,
context: ServiceContext,
context: InstrumentationContext,
spanContext: InMemorySpanContext,
kind: SpanKind,
startInstant: any TracerInstant,
Expand Down Expand Up @@ -210,7 +210,7 @@ public struct FinishedInMemorySpan: Sendable {
public var operationName: String

/// The service context of the finished span.
public var context: ServiceContext
public var context: InstrumentationContext
/// The in-memory span context.
public var spanContext: InMemorySpanContext {
get {
Expand Down
8 changes: 4 additions & 4 deletions Sources/InMemoryTracing/InMemorySpanContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
//
//===----------------------------------------------------------------------===//

import ServiceContextModule
import Instrumentation

/// A type that encapsulates the trace ID, span ID, and parent span ID of an in-memory span.
///
/// Generally used through the `ServiceContext/inMemorySpanContext` task local value.
/// Generally used through the `InstrumentationContext/inMemorySpanContext` task local value.
public struct InMemorySpanContext: Sendable, Hashable {
/// The idenfifier of top-level trace of which this span is a part of.
public var traceID: String
Expand All @@ -39,7 +39,7 @@ public struct InMemorySpanContext: Sendable, Hashable {
}
}

extension ServiceContext {
extension InstrumentationContext {
/// A task-local value that represents the current tracing span as set by the in-memory tracer.
public var inMemorySpanContext: InMemorySpanContext? {
get {
Expand All @@ -51,6 +51,6 @@ extension ServiceContext {
}
}

private struct InMemorySpanContextKey: ServiceContextKey {
private struct InMemorySpanContextKey: InstrumentationContextKey {
typealias Value = InMemorySpanContext
}
14 changes: 7 additions & 7 deletions Sources/InMemoryTracing/InMemoryTracer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension InMemoryTracer {
/// - Returns: An in-memory span.
public func startSpan<Instant>(
_ operationName: String,
context: @autoclosure () -> ServiceContext,
context: @autoclosure () -> InstrumentationContext,
ofKind kind: SpanKind,
at instant: @autoclosure () -> Instant,
function: String,
Expand Down Expand Up @@ -144,7 +144,7 @@ extension InMemoryTracer {
///
/// The service context provides the span, trace, and parent IDs
/// stored in the `inMemorySpanContext`
public func activeSpan(identifiedBy context: ServiceContext) -> InMemorySpan? {
public func activeSpan(identifiedBy context: InstrumentationContext) -> InMemorySpan? {
guard let spanContext = context.inMemorySpanContext else { return nil }
return _state.withValue { $0.activeSpans[spanContext] }
}
Expand Down Expand Up @@ -204,7 +204,7 @@ extension InMemoryTracer {
/// - carrier: The service implementation into which to add the service context.
/// - injector: The type that transfers service context into a carrier.
public func inject<Carrier, Inject: Injector>(
_ context: ServiceContext,
_ context: InstrumentationContext,
into carrier: inout Carrier,
using injector: Inject
) where Carrier == Inject.Carrier {
Expand Down Expand Up @@ -241,7 +241,7 @@ extension InMemoryTracer {
/// A type that represents a recorded call to the In-memory tracer's inject method.
public struct Injection: Sendable {
/// The context from which values were being injected.
public let context: ServiceContext
public let context: InstrumentationContext
/// The injected values; the trace and span identifiers of the propagated span.
public let values: [String: String]
}
Expand All @@ -256,7 +256,7 @@ extension InMemoryTracer {
/// - extractor: The type that transfers service context from a carrier.
public func extract<Carrier, Extract: Extractor>(
_ carrier: Carrier,
into context: inout ServiceContext,
into context: inout InstrumentationContext,
using extractor: Extract
) where Carrier == Extract.Carrier {
defer {
Expand All @@ -278,7 +278,7 @@ extension InMemoryTracer {
/// Lists all recorded calls to this tracer's extract method.
///
/// Lists calls to `Instrument/extract(_:into:using:)`.
/// This may be used to inspect the span identifiers extracted from an incoming carrier object into `ServiceContext`.
/// This may be used to inspect the span identifiers extracted from an incoming carrier object into `InstrumentationContext`.
public var performedContextExtractions: [Extraction] {
_state.withValue { $0.extractions }
}
Expand All @@ -288,7 +288,7 @@ extension InMemoryTracer {
/// The carrier object from which the context values were extracted from, such as an HTTP request.
public let carrier: any Sendable
/// The constructed service context, containing the extracted in-memory span context.
public let context: ServiceContext
public let context: InstrumentationContext
}
}

Expand Down
25 changes: 25 additions & 0 deletions Sources/Instrumentation/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

Base set of types which can be used to instrument libraries, excluding tracing support. Instrument implementations can be used to extract or inject contextual metadata from carrier objects (such as http requests, messages, or similar), and can be used for context propagation across process boundaries, or enrichment of contextual data, such as injecting/extracting "authorized user" or similar metadata.

## Overview

``InstrumentationContext`` is a minimal context propagation container, intended to "carry" context items for
purposes of cross-cutting tools to be built on top of it.

It is modeled after the concepts explained in [W3C Baggage](https://w3c.github.io/baggage/) and the
in the spirit of [Tracing Plane](https://cs.brown.edu/~jcmace/papers/mace18universal.pdf)'s "Baggage Context" type,
although by itself it doesn't define a specific serialization format.

This module is the implementation home for ``InstrumentationContext``. Most code should not depend on it
directly: depend on `Tracing` if you need spans, or on the `ServiceContextModule` product of the
`swift-service-context` package if you only need context propagation without tracing. Both re-export the
exact same `InstrumentationContext` type defined here. This package also keeps a deprecated `ServiceContext`
alias, the type's former name.

> Note: Automatic propagation through task-locals by using `InstrumentationContext.current` is supported in
> Swift version 5.5 or later.

## Topics

### Instruments
Expand All @@ -12,3 +30,10 @@ Base set of types which can be used to instrument libraries, excluding tracing s
- ``Instrument``
- ``Extractor``
- ``Injector``

### Context Propagation

- ``InstrumentationContext``
- ``InstrumentationContextKey``
- ``AnyInstrumentationContextKey``
- ``TODOLocation``
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# ``Instrumentation/AnyInstrumentationContextKey``

## Topics

### Inspecting a generic context key

- ``name``
- ``keyType``
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ``Instrumentation/InstrumentationContext``

## Topics

### Accessing the current context

- ``current``
- ``withValue(_:operation:)-4vtzm``
- ``withValue(_:isolation:operation:)``
- ``TODO(_:function:file:line:)``
- ``withValue(_:operation:)-1a4rm``

### Creating a new top-level context

- ``topLevel``

### Inspecting a context

- ``count``
- ``isEmpty``

### Accessing values from a context

- ``subscript(_:)``

### Iterating a context

- ``forEach(_:)``
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# ``Instrumentation/InstrumentationContextKey``

## Topics

### Inspecting a context key

- ``nameOverride``
- ``Value``
16 changes: 9 additions & 7 deletions Sources/Instrumentation/Instrument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
//
//===----------------------------------------------------------------------===//

import ServiceContextModule

/// A type that allows extracting values from an associated carrier.
///
/// The assocaited type, `Carrier`, is a service request such as an HTTP request,
Expand Down Expand Up @@ -67,17 +65,21 @@ public protocol Instrument: Sendable {
///
/// - Parameters:
/// - carrier: The `Carrier` that was used to propagate values across boundaries.
/// - context: The `ServiceContext` into which these values should be injected.
/// - context: The `InstrumentationContext` into which these values should be injected.
/// - extractor: The ``Extractor`` that extracts values from the given `Carrier`.
func extract<Carrier, Extract>(_ carrier: Carrier, into context: inout ServiceContext, using extractor: Extract)
func extract<Carrier, Extract>(
_ carrier: Carrier,
into context: inout InstrumentationContext,
using extractor: Extract
)
where Extract: Extractor, Extract.Carrier == Carrier

/// Extract values from a service context and inject them into the given carrier using the provided injector.
///
/// - Parameters:
/// - context: The `ServiceContext` from which relevant information is extracted.
/// - context: The `InstrumentationContext` from which relevant information is extracted.
/// - carrier: The `Carrier` into which this information is injected.
/// - injector: The ``Injector`` to use to inject extracted `ServiceContext` into the given `Carrier`.
func inject<Carrier, Inject>(_ context: ServiceContext, into carrier: inout Carrier, using injector: Inject)
/// - injector: The ``Injector`` to use to inject extracted `InstrumentationContext` into the given `Carrier`.
func inject<Carrier, Inject>(_ context: InstrumentationContext, into carrier: inout Carrier, using injector: Inject)
where Inject: Injector, Inject.Carrier == Carrier
}
Loading
Loading