Observability with Logfire and OpenTelemetry Collector
As applications grow in complexity and scale, managing observability data becomes increasingly challenging. The OpenTelemetry Collector provides a powerful solution for collecting, processing, and exporting telemetry data from multiple sources. When combined with Pydantic Logfire, you get a robust observability stack that scales with your infrastructure.
In this guide, we'll explore how to integrate Logfire with OpenTelemetry Collector to build a centralized observability pipeline that gives you complete visibility into your applications.
Introduction to Logfire and OpenTelemetry Collector
What is Pydantic Logfire?
Pydantic Logfire is a developer-centric observability platform built on OpenTelemetry standards. It provides:
- Automatic instrumentation for Python, JavaScript, Rust, and other languages
- Distributed tracing across your entire stack, AI to API
- Real-time monitoring with powerful SQL query capabilities
- Beautiful, intuitive UI for exploring traces, logs, and metrics
- Built-in security with automatic scrubbing of sensitive data
What is OpenTelemetry Collector?
The OpenTelemetry Collector is a vendor-agnostic telemetry data pipeline that can:
- Receive telemetry data via multiple protocols (OTLP, Jaeger, Zipkin, Prometheus)
- Process data through filtering, sampling, batching, and enrichment
- Export to one or multiple backends simultaneously
- Scale horizontally to handle high-volume telemetry
Why Use Them Together?
While Logfire applications can send data directly to the Logfire backend, using OpenTelemetry Collector as an intermediary provides several benefits:
- Centralized Collection: Route telemetry from multiple services through a single collection point
- Reduced Application Overhead: Offload telemetry processing from your application
- Flexible Routing: Send data to multiple backends (Logfire, Prometheus, etc.) simultaneously
- Data Transformation: Filter, sample, or enrich telemetry before it reaches Logfire
- Network Resilience: Buffer telemetry during network issues or backend downtime
- Multi-tenant Support: Route data from different teams or environments appropriately
Setting Up OpenTelemetry Collector
Running the Collector with Docker
The easiest way to get started with OpenTelemetry Collector is using Docker:
docker pull otel/opentelemetry-collector:latest
docker run -d \
--name otel-collector \
-p 4317:4317 \
-p 4318:4318 \
-v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector:latest \
--config=/etc/otel-collector-config.yaml
Basic Configuration
The OpenTelemetry Collector is configured using YAML. A minimal configuration consists of three main sections: receivers, processors, and exporters.
Create a file named otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 10s
send_batch_size: 1024
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
This basic configuration:
- Receives OTLP data on ports 4317 (gRPC) and 4318 (HTTP)
- Batches telemetry for efficient transmission
- Exports to console (useful for testing)
Integrating Logfire with OpenTelemetry Collector
Now let's configure the collector to send data to Logfire.
Get Your Logfire Credentials
First, obtain your Logfire write token:
- Sign in to Logfire
- Navigate to your project settings
- Create and copy your write token from the project's settings under "Write Tokens"
Configure the OTLP Exporter
Update your otel-collector-config.yaml to include the Logfire exporter:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 10s
send_batch_size: 1024
# Add resource attributes
resource:
attributes:
- key: deployment.environment
value: production
action: upsert
exporters:
otlphttp/logfire:
# Configure the US / EU endpoint for Logfire.
# - US: https://logfire-us.pydantic.dev
# - EU: https://logfire-eu.pydantic.dev
endpoint: https://logfire-us.pydantic.dev
headers:
Authorization: "Bearer YOUR_LOGFIRE_TOKEN"
compression: gzip
# Keep debug exporter for debugging
debug:
verbosity: basic
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlphttp/logfire, debug]
metrics:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlphttp/logfire, debug]
logs:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlphttp/logfire, debug]
Replace YOUR_LOGFIRE_TOKEN with your actual Logfire write token.
Configure Your Application
Now update your application to send telemetry to the collector instead of directly to Logfire:
FastAPI Example
# /// script
# dependencies = [
# "logfire[fastapi]",
# "fastapi",
# "uvicorn",
# ]
# ///
import logfire
from fastapi import FastAPI
logfire.configure(send_to_logfire=False)
app = FastAPI()
# Instrument FastAPI with Logfire
logfire.instrument_fastapi(app)
@app.get("/")
async def root():
logfire.info("Root endpoint accessed")
return {"message": "Hello World"}
if __name__ == "__main__":
import uvicorn
logfire.info("Starting FastAPI application")
uvicorn.run(app, host="0.0.0.0", port=8000)
Run the application:
# Using uv (automatically installs dependencies from script header)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 uv run app.py
# Or install manually and run with python
pip install 'logfire[fastapi]' uvicorn
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 python app.py
Test the endpoints:
# Test root endpoint
curl http://localhost:8000/
Configuring Logfire for Observability
Advanced Collector Configuration
Let's explore more advanced configurations that enhance your observability setup.
Sampling
Reduce data volume by sampling traces:
processors:
probabilistic_sampler:
sampling_percentage: 10 # Sample 10% of traces
service:
pipelines:
traces:
receivers: [otlp]
processors: [probabilistic_sampler, batch, resource]
exporters: [otlphttp/logfire]
Filtering
Filter out unwanted telemetry:
processors:
filter:
traces:
span:
- 'attributes["http.target"] == "/health"'
- 'attributes["http.target"] == "/metrics"'
service:
pipelines:
traces:
receivers: [otlp]
processors: [filter, batch, resource]
exporters: [otlphttp/logfire]
Enrichment
Add environment context to all telemetry:
processors:
resource/add_environment:
attributes:
- key: environment
value: production
action: insert
- key: region
value: us-east-1
action: insert
- key: cluster
value: primary
action: insert
service:
pipelines:
traces:
receivers: [otlp]
processors: [resource/add_environment, batch]
exporters: [otlphttp/logfire, debug]
Multi-Backend Export
Send data to multiple backends simultaneously:
exporters:
otlphttp/logfire:
endpoint: https://logfire-us.pydantic.dev
headers:
authorization: "Bearer YOUR_LOGFIRE_TOKEN"
prometheus:
endpoint: "localhost:9090"
jaeger:
endpoint: "jaeger:14250"
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlphttp/logfire, jaeger]
metrics:
receivers: [otlp]
processors: [batch, resource]
exporters: [otlphttp/logfire, prometheus]
At Logfire, we use this multi-backend export feature for our demo project to send traces and metrics simultaneously to both our US and EU production environments. This ensures data redundancy and allows users in different regions to access the demo data with low latency. You can see our production configuration in the Logfire demo repository.
Monitoring the Collector
The collector exposes metrics about its own operation. Configure Prometheus to scrape these metrics:
service:
telemetry:
metrics:
level: detailed
address: 0.0.0.0:8888
Then configure Prometheus:
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['localhost:8888']
Key metrics to monitor:
otelcol_receiver_accepted_spans- Spans receivedotelcol_receiver_refused_spans- Spans rejectedotelcol_exporter_sent_spans- Spans successfully exportedotelcol_exporter_send_failed_spans- Export failuresotelcol_processor_batch_batch_send_size- Batch sizes
Best Practices
1. Start Simple, Scale Gradually
Begin with a basic configuration and add complexity as needed. Don't over-engineer your initial setup.
2. Use Batching
Always enable the batch processor to reduce network overhead and improve throughput.
3. Monitor Your Collector
The collector is a critical component. Monitor its health, resource usage, and throughput.
4. Handle Failures Gracefully
Configure retry logic and queuing to handle temporary failures without data loss.
5. Secure Your Endpoints
Use TLS and authentication to protect your collector endpoints, especially in production.
6. Version Your Configuration
Keep your collector configuration in version control and treat it like application code.
7. Test Configuration Changes
Validate configuration changes in a staging environment before deploying to production.
8. Use Resource Attributes
Add consistent resource attributes to identify the source of telemetry across your infrastructure.
Troubleshooting
Collector Not Receiving Data
First, verify the collector is running and healthy by sending a test request:
# Test the collector's HTTP endpoint
curl -X POST http://localhost:4318/v1/traces \
-H "Content-Type: application/json" \
-d '{"resourceSpans":[]}'
Healthy response: {"partialSuccess":{}} - The collector is running and accepting data.
If this fails, check that:
- The collector container is running:
docker ps - The ports are properly exposed:
4317(gRPC) and4318(HTTP) - The configuration file is valid and loaded correctly
Once the collector is confirmed healthy, verify your application is configured to send to the correct endpoint (http://localhost:4317 for gRPC or http://localhost:4318 for HTTP).
Data Not Reaching Logfire
Enable debug logging in the collector:
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
exporters: [debug, otlp/logfire]
High Memory Usage
Configure memory limits:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
Authentication Errors
Verify your Logfire write token is correct and hasn't expired by testing it directly:
# For US production stack
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://logfire-us.pydantic.dev/v1/traces
# For EU production stack
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://logfire-eu.pydantic.dev/v1/traces
If you see unknown token%, your write token is invalid.
Conclusion
Integrating Pydantic Logfire with OpenTelemetry Collector gives you a flexible, scalable observability pipeline. You gain centralized control over your telemetry data, can route to multiple backends, and have the processing power to filter, sample, and enrich data before it reaches Logfire.
Whether you're running a small application or a large distributed system, this combination provides the foundation for comprehensive observability that grows with your needs.
Ready to get started? Sign up for Pydantic Logfire and check out the OpenTelemetry Collector documentation for more details.