mcpField
O
opentelemetry

opentelemetry

github.com/grafanaCloud & DevOps2956.2k installsUpdated 1d ago
Repository

About opentelemetry

opentelemetry is a guide to using OpenTelemetry with Grafana, for developers collecting and exporting application metrics, logs, traces, and profiles. It covers OTLP configuration, auto-instrumentation, sampling, and Alloy collector workflows.

Key features

  1. 01Export telemetry over OTLP
  2. 02Configure Grafana Cloud authentication and environment variables
  3. 03Auto-instrument Java and Python applications
  4. 04Use Alloy for buffering, forwarding, and sampling
  5. 05Inject instrumentation into Kubernetes pods with the Operator

Use cases

  1. 01Send application telemetry to Grafana backends
  2. 02Enable auto-instrumentation for Java or Python apps
  3. 03Configure forwarding and tail sampling in Alloy
  4. 04Inject instrumentation into pods in Kubernetes

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/grafana/skills --skill opentelemetry

After installing, type/opentelemetryin Claude Code to confirm it loaded.

Bundled filesreferences/collector-config.mdreferences/instrumentation.md

Details

From the project README

OpenTelemetry with Grafana

Docs: https://grafana.com/docs/opentelemetry/

Vendor-neutral instrumentation pipeline. Apps speak OTLP → Alloy (or direct) → Grafana Cloud (Mimir / Loki / Tempo / Pyroscope).

Backends

SignalBackend
MetricsGrafana Mimir
LogsGrafana Loki
TracesGrafana Tempo
ProfilesGrafana Pyroscope

Prerequisites

  • Grafana Cloud stack OR self-hosted Mimir / Loki / Tempo
  • Cloud OTLP endpoint: https://otlp-gateway-<region>.grafana.net/otlp
  • Basic-auth credentials: numeric instance ID + API token with MetricsPublisher + LogsPublisher + TracesPublisher
  • An app to instrument

Common Workflows

1. Authenticate to the Grafana Cloud OTLP endpoint
# 1. Build the auth header
INSTANCE_ID=123456
API_KEY="glc_eyJ..."
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-0.grafana.net/otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n "${INSTANCE_ID}:${API_KEY}" | base64)"
export OTEL_RESOURCE_ATTRIBUTES="service.name=myapp,service.namespace=myteam,deployment.environment=prod"

# 2. Smoke-test creds with a curl POST against the OTLP traces endpoint (empty body)
curl -s -o /dev/null -w "%{http_code}\n" \
  -X POST -H "Content-Type: application/x-protobuf" \
  -H "Authorization: Basic $(echo -n "${INSTANCE_ID}:${API_KEY}" | base64)" \
  "$OTEL_EXPORTER_OTLP_ENDPOINT/v1/traces" --data-binary '\n'
# Expect 400 (malformed payload) — NOT 401 (auth) or 404 (wrong endpoint).
2. Auto-instrument a Java app + verify
# 1. Download the Grafana JVM agent (single jar)
curl -sLO https://github.com/grafana/grafana-opentelemetry-java/releases/latest/download/grafana-opentelemetry-java.jar

# 2. Run with the agent + env from step 1
java -javaagent:./grafana-opentelemetry-java.jar -jar myapp.jar

# 3. Generate traffic, then verify in Grafana → Explore → Tempo:
#    TraceQL: { resource.service.name = "myapp" }
#    Expect spans within ~30s. Also verify metrics:
#    PromQL: count by (service_name)({service_name="myapp"})
3. Auto-instrument a Python app
pip install "opentelemetry-distro[otlp]"
opentelemetry-bootstrap -a install

# Same env vars as step 1, then:
opentelemetry-instrument python app.py

# Verify the same way — Explore → Traces filter service.name=myapp.
4. Add Alloy as a buffering / sampling collector
# Application points at local Alloy (gRPC fastest)
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc

# Alloy environment for forwarding to Cloud
export GRAFANA_CLOUD_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-0.grafana.net/otlp
export GRAFANA_CLOUD_INSTANCE_ID=$INSTANCE_ID
export GRAFANA_CLOUD_API_KEY=$API_KEY
alloy run /etc/alloy/config.alloy

# Verify Alloy received and forwarded
curl -s http://localhost:12345/metrics | grep otelcol_exporter_sent_spans

Full Alloy config + tail-sampling block + OTel Collector YAML + K8s Operator install: references/collector-config.md.

SDK-by-language details (Go full code, Node manual setup, .NET ASP.NET Core, all the env-var quirks): references/instrumentation.md.

5. Kubernetes — auto-inject via the Operator
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata: { name: my-instrumentation }
spec:
  exporter: { endpoint: http://otelcol:4317 }
  propagators: [tracecontext, baggage]
  java:
    image: us-docker.pkg.dev/grafanalabs-global/docker-grafana-opentelemetry-java-prod/grafana-opentelemetry-java:2.3.0-beta.1
  nodejs: {}
  python: {}

Then annotate pods:

metadata:
  annotations:
    instrumentation.opentelemetry.io/inject-java: "true"
    # or: inject-nodejs, inject-python, inject-dotnet
# Verify the operator injected the agent
kubectl describe pod <pod> | grep -A2 'opentelemetry-auto-instrumentation'
# Then run the same Grafana Explore checks.

Sampling — when to pick which

# Head sampling (cheap, decided at start; may lose rare errors)
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1   # 10%

Tail sampling (decides after seeing the whole trace — keep errors + sample the rest) requires an Alloy / OTel-Collector tail_sampling processor; full block in references/collector-config.md.

Key environment variables

VariableExample
OTEL_EXPORTER_OTLP_ENDPOINThttps://otlp-gateway-prod-us-east-0.grafana.net/otlp
OTEL_EXPORTER_OTLP_PROTOCOLgrpc or http/protobuf
OTEL_EXPORTER_OTLP_HEADERSAuthorization=Basic <base64>
OTEL_RESOURCE_ATTRIBUTESservice.name=app,service.namespace=team,deployment.environment=prod
OTEL_SERVICE_NAMEshorthand for service.name
OTEL_TRACES_SAMPLER / _ARGparentbased_traceidratio / 0.1

Troubleshooting

  • 401 from OTLP gateway → instance ID is not numeric, or API key missing publisher roles
  • 404 → endpoint URL wrong (must end with /otlp)
  • Spans missing → check OTEL_EXPORTER_OTLP_PROTOCOL matches transport (Cloud OTLP gateway = http/protobuf, Alloy local = grpc)
  • Node.js auto-instrumentation broken after bundling → bundlers like @vercel/ncc defeat the require hooks
  • Python under Gunicorn / uWSGI shows no spans → reinit OTel providers in a post-fork hook

Resources

Comments

Markdown supported