swift-otel icon indicating copy to clipboard operation
swift-otel copied to clipboard

Streamline bootstrap for defaults

Open simonjbeaumont opened this issue 1 year ago • 1 comments
trafficstars

Right now if you want to bootstrap the logging, metrics, and tracing systems you require the following code, which we also show in the HTTP server example:^1

// Bootstrap the logging backend with the OTel metadata provider which includes span IDs in logging messages.
LoggingSystem.bootstrap { label in
    var handler = StreamLogHandler.standardError(label: label, metadataProvider: .otel)
    handler.logLevel = .trace
    return handler
}

// Configure OTel resource detection to automatically apply helpful attributes to events.
let environment = OTelEnvironment.detected()
let resourceDetection = OTelResourceDetection(detectors: [
    OTelProcessResourceDetector(),
    OTelEnvironmentResourceDetector(environment: environment),
    .manual(OTelResource(attributes: ["service.name": "example_server"])),
])
let resource = await resourceDetection.resource(environment: environment, logLevel: .trace)

// Bootstrap the metrics backend to export metrics periodically in OTLP/gRPC.
let registry = OTelMetricRegistry()
let metricsExporter = try OTLPGRPCMetricExporter(configuration: .init(environment: environment))
let metricsReader = OTelPeriodicExportingMetricsReader(
    resource: resource,
    producer: registry,
    exporter: metricsExporter,
    configuration: .init(
        environment: environment
    )
)
MetricsSystem.bootstrap(OTLPMetricsFactory(registry: registry))

// Bootstrap the tracing backend to export traces periodically in OTLP/gRPC.
let exporter = try OTLPGRPCSpanExporter(configuration: .init(environment: environment))
let processor = OTelBatchSpanProcessor(exporter: exporter, configuration: .init(environment: environment))
let tracer = OTelTracer(
    idGenerator: OTelRandomIDGenerator(),
    sampler: OTelConstantSampler(isOn: true),
    propagator: OTelW3CPropagator(),
    processor: processor,
    environment: environment,
    resource: resource
)
InstrumentationSystem.bootstrap(tracer)

// Pass `tracer` and `metricsReader` to your service group.

This is quite a bit of boilerplate to get just the default behaviour and it would be nice to provide a more streamlined setup for folks that just want the defaults.

I'm not making any concrete API proposals, but probably a combination of:

  • Default values for initialisers.
  • Taking configuration from environment variables as directed in the OTel spec.
  • Providing higher level factory methods that create, wire up, and return the layered types.
  • Possibly unifying the API shape between tracing and metrics (tracer currently is both the factory and the service, but they are decoupled with metrics).

We may already be doing some of the above but I think we have some way to go.

As a strawman goal, it'd be nice to be able to get the code to something more like this:

// Configure OTel resource detection to automatically apply helpful attributes to events.
await OTelResourceDetection.bootstrap()  // use process and environment detectors automatically by default

// Bootstrap the logging backend with the OTel metadata provider which includes span IDs in logging messages.
LoggingSystem.bootstrap(OTelStandardErrorLoggerFactory(level: .trace))

// Bootstrap the metrics backend to export metrics periodically in OTLP/gRPC.
let metricsFactory, metricsReader = OTelPeriodicExportingMetricsReader.make()
MetricsSystem.bootstrap(metricsFactory)

// Bootstrap the tracing backend to export traces periodically in OTLP/gRPC.
let tracer = OTelTracer() 
InstrumentationSystem.bootstrap(tracer)

// Pass `tracer` and `metricsReader` to your service group.

simonjbeaumont avatar Nov 20 '24 14:11 simonjbeaumont