Repository: vapor/vapor
File: Sources/Vapor/Middleware/TracingMiddleware.swift (Swift)

Code before:
import Tracing

/// Creates a trace and metadata for every request
///
/// See https://opentelemetry.io/docs/specs/semconv/http/http-spans/
public final class TracingMiddleware: AsyncMiddleware {
    private let setCustomAttributes: @Sendable (inout SpanAttributes, Request) -> Void
    
    /// Create a TracingMiddleware
    /// - Parameter setCustomAttributes: Closure that allows setting custom span attributes for a particular request. A custom span attribute could be extracted from a request
    /// header, for example. This closure is called during span creation on every request, so should be lightweight.
    public init(
        setCustomAttributes: @escaping @Sendable (inout SpanAttributes, Request) -> Void = { _, _ in }
    ) {
        self.setCustomAttributes = setCustomAttributes
    }
    
    public func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
        var parentContext = request.serviceContext
        InstrumentationSystem.instrument.extract(request.headers, into: &parentContext, using: HTTPHeadersExtractor())
        
        return try await withSpan(
            // Name: https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name
            request.route?.description ?? "vapor_route_undefined",
            context: parentContext,
            ofKind: .server
        ) { span in
            // Set the request.serviceContext for the duration of this middleware & then reset it to parent
            // Using this pattern in `withSpan` allows spans to nest across sequential future chains
            request.serviceContext = span.context
            defer {
                request.serviceContext = parentContext
            }
            
            // Attributes: https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-semantic-conventions
            span.updateAttributes { attributes in
                // Required
                attributes["http.request.method"] = request.method.rawValue
                attributes["url.path"] = request.url.path

Review comment (suggestion):
This will get rid of the API breakage complaint (which is for once technically correct).

```suggestion
    public init() {
        self.setCustomAttributes = { _, _ in }
    }
    
    public init(
        setCustomAttributes: @escaping @Sendable (inout SpanAttributes, Request) -> Void
    ) {
```

Code after:
import Tracing

/// Creates a trace and metadata for every request
///
/// See https://opentelemetry.io/docs/specs/semconv/http/http-spans/
public final class TracingMiddleware: AsyncMiddleware {
    private let setCustomAttributes: @Sendable (inout SpanAttributes, Request) -> Void
    
    /// Create a TracingMiddleware
    public init() {
        self.setCustomAttributes = { _, _ in }
    }
    
    /// Create a TracingMiddleware
    /// - Parameter setCustomAttributes: Closure that allows setting custom span attributes for a particular request. A custom span attribute could be extracted from a request
    /// header, for example. This closure is called during span creation on every request, so should be lightweight.
    public init(
        setCustomAttributes: @escaping @Sendable (inout SpanAttributes, Request) -> Void
    ) {
        self.setCustomAttributes = setCustomAttributes
    }
    
    public func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response {
        var parentContext = request.serviceContext
        InstrumentationSystem.instrument.extract(request.headers, into: &parentContext, using: HTTPHeadersExtractor())
        
        return try await withSpan(
            // Name: https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name
            request.route?.description ?? "vapor_route_undefined",
            context: parentContext,
            ofKind: .server
        ) { span in
            // Set the request.serviceContext for the duration of this middleware & then reset it to parent
            // Using this pattern in `withSpan` allows spans to nest across sequential future chains
            request.serviceContext = span.context
            defer {
                request.serviceContext = parentContext
            }