Skip to main content
One initialisation block sends logs, metrics, and traces from your Python application to Bronto. Logs are bridged from the standard logging module, metrics flow through a meter provider, and traces through a tracer provider โ€” all three sharing one resource identity and one OTLP/HTTP exporter configuration. The application exports to an OpenTelemetry Collector, which forwards to Bronto. If you donโ€™t run a Collector, export directly to Bronto instead โ€” the code is identical, only two environment variables change.

Prerequisites

Install the SDK

Configure the environment

The SDK reads its endpoint and identity from environment variables, so nothing in the code below is environment-specific.
The SDK appends the signal path (/v1/logs, /v1/metrics, /v1/traces) to OTEL_EXPORTER_OTLP_ENDPOINT automatically. Two resource attributes determine how Bronto organises your data:
http://localhost:4318 is the standard OTLP/HTTP address for a Collector on the same host. Use the address reachable from your application if the Collector runs in another container, pod, or host. No authentication is needed between the application and the Collector โ€” the Collector holds the Bronto API key.

Initialise the SDK

Add this module to your project and call configure_otel() once, before your first log statement or span.
otel.py
The handler is attached to the root logger, so every logger in the process inherits it and existing log statements are unchanged. Pass a name to logging.getLogger("my_app") to instrument only part of your application.

Instrument your application

app.py
Any log emitted inside an active span automatically carries that spanโ€™s trace_id and span_id, so you can jump from a log line to its trace in Bronto with no manual context propagation.
Framework spans without code changes. Install opentelemetry-instrumentation-<framework> (for example opentelemetry-instrumentation-flask, -django, -fastapi, -sqlalchemy, -requests) and run your app with opentelemetry-instrument python app.py. The CLI configures the providers itself from the same OTEL_* variables above, so use it instead of otel.py โ€” and add opentelemetry-instrumentation-logging so standard-library logs are still bridged. See Python zero-code instrumentation.
Using structlog or loguru? See Python: structlog / loguru to route them through the same pipeline.

Verify

Run your application, then check each signal in Bronto, filtering by the service.name you set:
  • Logs โ€” the Search page, in the dataset named after your service
  • Metrics โ€” the Metric Explorer
  • Traces โ€” the Explore Traces page
If nothing arrives:
  • Confirm the Collector is running and reachable at OTEL_EXPORTER_OTLP_ENDPOINT, and that its pipelines include an otlp receiver and the Bronto exporters.
  • The batch processors export on a background thread. Short-lived scripts can exit before the first flush โ€” for those, swap in SimpleLogRecordProcessor and SimpleSpanProcessor, or call logger_provider.shutdown() and tracer_provider.shutdown() before exit.
For signal-specific reference material, see Send Metrics to Bronto and Send Traces to Bronto.

Direct export to Bronto

Without a Collector, the application exports straight to Bronto over OTLP/HTTP. The code in otel.py is unchanged โ€” point the endpoint at your Bronto region and add your API key:
See API Keys for how to create a key with ingestion permissions.
Prefer the Collector for multi-service environments โ€” it batches, filters, and enriches telemetry, and keeps the API key out of every application. Direct export suits single services and simple architectures.

GenAI semantic conventions

If your application calls an LLM, OpenTelemetryโ€™s GenAI semantic conventions define gen_ai.* span attributes for model, token usage, and prompt and response content. Python has the richest GenAI auto-instrumentation of any OTel SDK. Install the instrumentation for your provider and run under the zero-code wrapper:
Each model call then produces a span such as chat gpt-4o-mini in place of a plain HTTP client span:

Capture prompts and responses

Content capture is off by default. Two environment variables enable it:
span_only writes content to gen_ai.input.messages and gen_ai.output.messages on the span, which Bronto surfaces as searchable trace fields. The legacy true setting emits it as separate log records correlated by trace_id, where it is not queryable on the span itself โ€” prefer span_only where your instrumentation supports it. These conventions are still experimental, so verify the attributes emitted by the version you deploy.

Manual spans

Where no instrumentation package exists for your provider, set the same attributes yourself:
See the GenAI span conventions for the full attribute list. For the recommended attribute set and Bronto search queries, see LLM Observability. For Anthropic and LangChain instrumentation, see OpenLLMetry and LangChain.