diff --git a/docs/advanced/tracing.md b/docs/advanced/tracing.md new file mode 100644 index 000000000..6c70edbe8 --- /dev/null +++ b/docs/advanced/tracing.md @@ -0,0 +1,83 @@ +# Tracing + +Tracing is a powerful tool for monitoring and debugging distributed systems. Vapor's tracing API allows developers to easily track request lifecycles, propagate metadata, and integrate with popular backends like OpenTelemetry. + +Vapor's tracing API is built on top of [swift-distributed-tracing](https://github.com/apple/swift-distributed-tracing), which means it is compatible with all of swift-distributed-tracing's [backend implementations](https://github.com/apple/swift-distributed-tracing/blob/main/README.md#tracing-backends). + +If you are unfamiliar with tracing and spans in Swift, review the [OpenTelemetry Trace documentation](https://opentelemetry.io/docs/concepts/signals/traces/) and [swift-distributed-tracing documentation](https://swiftpackageindex.com/apple/swift-distributed-tracing/main/documentation/tracing). + +## TracingMiddleware + +To automatically create a fully annotated span for each request, add the `TracingMiddleware` to your application. + +```swift +app.middleware.use(TracingMiddleware()) +``` + +To get accurate span measurements and ensure that tracing identifiers are passed along correctly to other services, add this middleware before other middlewares. + +## Adding Spans + +When adding spans to route handlers, it's ideal for them to be associated with the top-level request span. This is referred to as "span propagation" and can be handled in two different ways: automatic or manual. + +### Automatic Propagation + +Vapor has support to automatically propagate spans between middleware and route callbacks. To do so, set the `Application.traceAutoPropagation` property to true during configuration. + +```swift +app.traceAutoPropagation = true +``` + +!!! note + Enabling auto-propagation may degrade performance on high-throughput APIs with minimal tracing needs, since request span metadata must be restored for every route handler regardless of whether spans are created. + +Then spans may be created in the route closure using the ordinary distributed tracing syntax. + +```swift +app.get("fetchAndProcess") { req in + let result = try await fetch() + return try await withSpan("getNameParameter") { _ in + try await process(result) + } +} +``` + +### Manual Propagation + +To avoid the performance implications of automatic propagation, you may manually restore span metadata where necessary. `TracingMiddleware` automatically sets a `Request.serviceContext` property which may be used directly in `withSpan`'s `context` parameter. + +```swift +app.get("fetchAndProcess") { req in + let result = try await fetch() + return try await withSpan("getNameParameter", context: req.serviceContext) { _ in + try await process(result) + } +} +``` + +To restore the span metadata without creating a span, use `ServiceContext.withValue`. This is valuable if you know that downstream async libraries emit their own tracing spans, and those should be nested underneath the parent request span. + +```swift +app.get("fetchAndProcess") { req in + try await ServiceContext.withValue(req.serviceContext) { + try await fetch() + return try await process(result) + } +} +``` + +## NIO Considerations + +Because `swift-distributed-tracing` uses [`TaskLocal properties`](https://developer.apple.com/documentation/swift/tasklocal) to propagate, you must manually re-restore the context whenever you cross `NIO EventLoopFuture` boundaries to ensure spans are linked correctly. **This is necessary regardless of whether automatic propagation is enabled**. + +```swift +app.get("fetchAndProcessNIO") { req in + withSpan("fetch", context: req.serviceContext) { span in + fetchSomething().map { result in + withSpan("process", context: span.context) { _ in + process(result) + } + } + } +} +``` diff --git a/mkdocs.yml b/mkdocs.yml index 206616338..3f31575ec 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -578,6 +578,7 @@ nav: - Services: "advanced/services.md" - Request: "advanced/request.md" - APNS: "advanced/apns.md" + - Tracing: "advanced/tracing.md" - Security: - Authentication: "security/authentication.md" - Crypto: "security/crypto.md"