|
1 | | -## Span profiles support for OpenTelemetry in Python |
| 1 | +# Span Profiles Support for OpenTelemetry in Python |
2 | 2 |
|
3 | | -This package enables applications that already rely on [OpenTelemetry](https://opentelemetry.io/docs/instrumentation/python/getting-started/) for distributed tracing and Pyroscope for continuous profiling to link the tracing and profiling data together. |
| 3 | +This package links OpenTelemetry tracing data with Pyroscope continuous profiling data, enabling you to correlate traces with performance profiles. |
4 | 4 |
|
5 | | -See https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/ for more information. |
| 5 | +Reference: https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/ |
6 | 6 |
|
7 | | -### Prerequisites |
8 | | -- Your Python application is instrumented with [Pyroscope's profiler](https://grafana.com/docs/pyroscope/latest/configure-client/language-sdks/python/) |
9 | | -- Your Python application is instrumented with [OpenTelemetry](https://opentelemetry.io/docs/instrumentation/python/getting-started/) |
10 | | - |
11 | | -### Integration |
| 7 | +## Prerequisites |
12 | 8 |
|
13 | | -Add the following package to your project: |
| 9 | +- Your Python application is instrumented with [Pyroscope profiler](https://grafana.com/docs/pyroscope/latest/configure-client/language-sdks/python/) |
| 10 | +- Your Python application is instrumented with [OpenTelemetry](https://opentelemetry.io/docs/instrumentation/python/getting-started/) |
14 | 11 |
|
| 12 | +## Installation |
15 | 13 | ```shell |
16 | 14 | pip install pyroscope-otel |
17 | 15 | ``` |
18 | 16 |
|
19 | | -Register the `PyroscopeSpanProcessor` in your OpenTelemetry integration: |
| 17 | +## Pyroscope Configuration (Required) |
20 | 18 |
|
| 19 | +Pyroscope must be configured before creating any spans. This is mandatory for all setups: |
21 | 20 | ```python |
| 21 | +from pyroscope import configure as pyroscope_configure |
| 22 | + |
| 23 | +# Local setup (default) |
| 24 | +pyroscope_configure( |
| 25 | + app_name="my-app", |
| 26 | + server_address="http://localhost:4040", |
| 27 | + sample_rate=100, |
| 28 | +) |
| 29 | + |
| 30 | +# Grafana Cloud setup (uncomment and update with your credentials) |
| 31 | +# pyroscope_configure( |
| 32 | +# app_name="my-app", |
| 33 | +# server_address="https://pyroscope-blocks-prod-us-central-1.grafana-cloud.com/prom/push", |
| 34 | +# auth_token="<your-grafana-cloud-token>", |
| 35 | +# basic_auth_username="<your-username>", # Optional: username for basic auth (Grafana Cloud) |
| 36 | +# basic_auth_password="<your-password>", # Optional: password for basic auth (Grafana Cloud) |
| 37 | +# sample_rate=100, |
| 38 | +# ) |
| 39 | +``` |
| 40 | + |
| 41 | +## How It Works & Span Attributes |
| 42 | + |
| 43 | +The `PyroscopeSpanProcessor` automatically attaches the profile identifier (`pyroscope.profile.id`) as an attribute to the **root span** of each trace. This creates a direct link between traces and their corresponding performance profiles in Grafana Tempo, allowing you to navigate from any trace to the exact performance profile data for that transaction. |
22 | 44 |
|
| 45 | +## Manual Instrumentation |
| 46 | + |
| 47 | +Configure OpenTelemetry explicitly (after Pyroscope is already configured): |
| 48 | +```python |
23 | 49 | from opentelemetry import trace |
24 | 50 | from opentelemetry.sdk.trace import TracerProvider |
25 | | - |
26 | 51 | from pyroscope.otel import PyroscopeSpanProcessor |
27 | 52 |
|
| 53 | +# Configure OpenTelemetry |
28 | 54 | provider = TracerProvider() |
29 | 55 | provider.add_span_processor(PyroscopeSpanProcessor()) |
30 | 56 |
|
| 57 | +# TODO: Add your trace exporter configuration here |
| 58 | +# (e.g., Grafana Tempo OTLP exporter, etc.) |
| 59 | +# from opentelemetry.sdk.trace.export import BatchSpanProcessor |
| 60 | +# provider.add_span_processor(BatchSpanProcessor(your_exporter)) |
| 61 | + |
31 | 62 | trace.set_tracer_provider(provider) |
32 | 63 |
|
| 64 | +# Use tracing in your application |
| 65 | +tracer = trace.get_tracer(__name__) |
| 66 | +with tracer.start_as_current_span("my_operation"): |
| 67 | + # Your code here |
| 68 | + pass |
| 69 | +``` |
| 70 | + |
| 71 | +## Automatic Instrumentation |
| 72 | + |
| 73 | +When using auto-instrumentation (e.g., `opentelemetry-distro`), you must still register `PyroscopeSpanProcessor` manually (after Pyroscope is already configured): |
| 74 | +```python |
| 75 | +from opentelemetry import trace |
| 76 | +from pyroscope.otel import PyroscopeSpanProcessor |
| 77 | + |
| 78 | +# After auto-instrumentation is initialized |
| 79 | +provider = trace.get_tracer_provider() |
| 80 | +provider.add_span_processor(PyroscopeSpanProcessor()) |
33 | 81 | ``` |
| 82 | + |
| 83 | +> **Note:** Auto-instrumentation only handles OpenTelemetry setup. Pyroscope configuration is still required. |
| 84 | +
|
| 85 | +## Grafana Cloud OpenTelemetry Exporter (Optional) |
| 86 | +```python |
| 87 | +# OpenTelemetry exporter for Grafana Cloud / Grafana Tempo |
| 88 | +# from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter |
| 89 | +# |
| 90 | +# otlp_exporter = OTLPSpanExporter( |
| 91 | +# endpoint="<your-tempo-instance>.grafana.net:443", |
| 92 | +# headers=(("Authorization", "Bearer <your-grafana-cloud-token>"),), |
| 93 | +# ) |
| 94 | +# provider.add_span_processor(BatchSpanProcessor(otlp_exporter)) |
| 95 | +``` |
| 96 | + |
| 97 | +## Integration Checklist |
| 98 | + |
| 99 | +- ✅ Pyroscope configured with `pyroscope_configure()` |
| 100 | +- ✅ OpenTelemetry `TracerProvider` created |
| 101 | +- ✅ `PyroscopeSpanProcessor` registered with `add_span_processor()` |
| 102 | +- ✅ Trace exporter configured (Grafana Tempo, etc.) |
| 103 | +- ✅ Application instrumented with OpenTelemetry |
| 104 | +- ✅ Verify `pyroscope.profile.id` appears in span attributes in Grafana Tempo |
| 105 | + |
| 106 | +## Troubleshooting |
| 107 | + |
| 108 | +| Issue | Solution | |
| 109 | +|-------|----------| |
| 110 | +| `pyroscope.profile.id` not in spans | Ensure `PyroscopeSpanProcessor` was registered with `add_span_processor()` | |
| 111 | +| Profiles not appearing in Pyroscope | Verify `pyroscope_configure()` is called before creating spans | |
| 112 | +| Traces not exporting | Check trace exporter configuration and credentials | |
| 113 | +| Auto-instrumentation not working | Manually add `PyroscopeSpanProcessor()` after initializing the provider | |
| 114 | + |
| 115 | +## References |
| 116 | + |
| 117 | +- [Pyroscope Documentation](https://grafana.com/docs/pyroscope/latest/) |
| 118 | +- [OpenTelemetry Python](https://opentelemetry.io/docs/instrumentation/python/) |
| 119 | +- [Trace-Profile Integration](https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/) |
0 commit comments