# \[Pitch\] Swift Service Context

**URL:** <https://forums.swift.org/t/pitch-swift-service-context/66687>\
**Category:** Pitches\
**Tags:** pitch, sswg, tracing\
**Created:** [August 10, 2023, 12:35am UTC](https://forums.swift.org/t/pitch-swift-service-context/66687 "2023-08-10T00:35:42Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [August 10, 2023, 12:35am UTC](https://forums.swift.org/t/pitch-swift-service-context/66687/1 "2023-08-10T00:35:42Z")

</div>

Hello everyone,  
as a companion to the just posted [[Pitch] Swift Distributed Tracing](https://forums.swift.org/t/pitch-swift-distributed-tracing/66679) we'd like to also pitch the endorsement of the swift-service-context package by the SSWG.

* * *

- Proposal: SSWG-NNNN
- Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [Moritz Lang](https://github.com/slashmo)
- Review Manager: TBD
- Status: **Implemented**
- Implementation:
  - [swift-service-context](https://github.com/apple/swift-service-context)

## Package Description

ServiceContext is a minimal (zero-dependency) context propagation container, intended to "carry" items for purposes of cross-cutting tools to be built on top of it.

It is modeled after the concepts explained in W3C Baggage and in the spirit of Tracing Plane's "Baggage Context" type, although by itself, it does not define a specific serialization format.

See [GitHub - apple/swift-distributed-tracing: Instrumentation library for Swift server applications](https://github.com/apple/swift-distributed-tracing) for actual instrument types and implementations, which can be used to deploy various cross-cutting instruments, all reusing the same service context type. More information can be found in the SSWG meeting notes.

| | |
| --- | --- |
| **Package Name** | `swift-service-context` |
| **Module Name** | `ServiceContextModule` |
| **Proposed Maturity Level** | [Incubating](https://github.com/swift-server/sswg/blob/main/process/incubation.md#process-diagram) |
| **License** | [Apache v2](https://github.com/apple/swift-distributed-tracing/blob/main/LICENSE.txt) |
| **Dependencies** | - |

## Introduction

Service context is a type-safe dictionary that is keyed using types and intended to be used for contextual information propagation across asynchronous code.

It declares a well-known Task Local key for the service context, and patterns how to add keys.

It is a core building block of [swift-distributed-tracing](https://github.com/apple/swift-distributed-tracing) which uses it to propagate trace information across swift concurrency tasks, though it can be used independently as well.

## Overview

Service context's API surface is relatively small and is all based around the service context type which is a type-safe dictionary.

One can create a service context using the `.topLevel` factory method which intends to clarify that this should not be used UNLESS intending to start a fresh "top level" context that does not inherit any values from the current context:

```swift
var context = ServiceContext.topLevel

```

In most situations, developers should prefer to "pick up" the context from the current context:

```swift
var context: ServiceContext? = ServiceContext.current

```

which inspects the task-local values for the presence of a service context. It is by design that it is possible to determine if no context was set, or if an empty context is set. When intending to "carry existing, or create a new" context, the following pattern is used:

```swift
var context = ServiceContext.current ?? ServiceContext.topLevel

```

Once obtained, one can set values in by using defined keys, like this:

```swift
private enum FirstTestKey: ServiceContextKey {
  typealias Value = Int
}

var context = ServiceContext.topLevel
context[FirstTestKey.self] = 42

```

Keys declared as types conforming to `ServiceContextKey` allow for future extension where we might want to configure specific keys using additional behavior, like for example defining the `static let nameOverride` of a key. It is also important that a Key type may be private to whomever declares it, allowing only such library to set these values.

Service context is used primarily as a task-local value read and modified like this:

```swift
func exampleFunction() async -> Int {
  guard let context = ServiceContext.current {
    return 0
  }
  guard let value = context[FirstTestKey.self] {
    return 0
  }
  print("test = \(value)") // test = 42
  return value
}

// ----------------------------------------

var context = ServiceContext.topLevel
context[FirstTestKey.self] = 42

let c = ServiceContext.withValue(context) {
    await exampleFunction()
}
assert(c == 42)

```

Swift's [task local values](https://developer.apple.com/documentation/swift/tasklocal) are used to automatically propagate the context value to any child tasks that may be created from this code, such that context is automatically propagated through to them, e.g. like this:

```swift
func testMeMore() {
  guard let context = ServiceContext.current {
    return 0
  }
  guard let value = context[FirstTestKey.self] {
    return 0
  }
  return value
}

func test() async -> Int {
  async let v = testMeMore() // read context from child task
  return await v
}

var context = ServiceContext.topLevel
context[FirstTestKey.self] = 42
ServiceContext.withValue(context) {
    assert(test() == 42)
}

```

For keys which are expected to be public API, used ty developers, it is recommended to follow this pattern to declare an accessor on the context, rather than exposing the Key type directly:

```swift
extension ServiceContext { 
  public var testValue: String? {
    set { 
      self[TestKey.self] = newValue
    }
    get {
      self[TestKey.self]
    }
  }
}

```

## Maturity Justification

We are proposing this package at the "Incubation" level of maturity. We believe this package to be an important building block of the server ecosystem, in the same way [swift-log](https://github.com/apple/swift-log) and [swift-metrics](https://github.com/apple/swift-metrics) are adopted across many server and client libraries.

The project has matured for over 3 years, has multiple active maintainers and fulfills adoption requirements in production.

## Alternatives considered

Not building a tracing API was considered but we see this as a fundamental building block for the server ecosystem, in the same style as swift-log and swift-metrics.

---

<div class="post-metadata">

**Author:** ![adam-fowler](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/adam-fowler/32/9266_2.png) [@adam-fowler](https://forums.swift.org/u/adam-fowler)\
**Post date:** [August 10, 2023, 4:47pm UTC](https://forums.swift.org/t/pitch-swift-service-context/66687/2 "2023-08-10T16:47:26Z")

</div>

+1  
Can't really have tracing without some way to propagate its baggage. This is a key part of the tracing story although a separate part which can be used in other contexts. It has had a long development cycle and a lot of thought has gone into its design.
