OpenTelemetry Observability AI Skill Guide
Overview
OpenTelemetry (OTel) is a vendor-neutral standard for traces, metrics, and logs. Applications use language SDKs/APIs; the OpenTelemetry Collector receives, processes, and exports telemetry to backends (Jaeger, Prometheus, cloud APM). Agents should prioritize correct context propagation and low-cardinality attributes over dumping every field into spans.
App SDK (traces/metrics/logs)
|
| OTLP
v
OTel Collector (receivers -> processors -> exporters)
|
+--> Tempo/Jaeger (traces)
+--> Prometheus/Mimir (metrics)
+--> Loki/ELK (logs)
When to use
- Adding instrumentation to HTTP/gRPC clients and servers
- Designing Collector pipelines and sampling strategies
- Debugging missing spans, broken trace IDs, or cardinality explosions
- Standardizing attributes (
service.name,deployment.environment)
Operational directives
- Always set
service.name(and ideallyservice.version) via resource attributes. - Propagate W3C Trace Context (
traceparent) across process boundaries. - Prefer semantic conventions for HTTP, DB, messaging attributes.
- Sample thoughtfully in production - head sampling or tail sampling in Collector.
- Avoid high-cardinality labels (raw user IDs, unbounded URLs) on metrics.
Concrete examples
Collector pipeline sketch
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch: {}
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
otlp/tempo:
endpoint: tempo:4317
tls: { insecure: true }
prometheus:
endpoint: 0.0.0.0:8889
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
Node.js manual span sketch
const { trace } = require("@opentelemetry/api");
const tracer = trace.getTracer("api");
async function createOrder(req) {
return tracer.startActiveSpan("createOrder", async (span) => {
span.setAttribute("order.items", req.items.length);
try {
const id = await saveOrder(req);
span.setAttribute("order.id", id);
return id;
} catch (err) {
span.recordException(err);
span.setStatus({ code: 2, message: String(err) });
throw err;
} finally {
span.end();
}
});
}
Propagation check
# Expect traceparent on outbound calls; verify backend shows one trace ID
curl -sI https://api.example.com/health | tr -d '\r' | grep -i trace
Common failure modes
| Symptom | Cause | Fix |
|---|---|---|
| Orphan spans | Missing propagation | Inject/extract TraceContext |
| Huge bills / load | Over-instrumentation | Sample; drop noisy spans |
| Useless metrics | High cardinality | Bound label sets |
| No data in backend | Exporter endpoint/TLS | Collector logs; otel-debug |
Best practices
- Auto-instrument frameworks first; add manual spans at business boundaries.
- Keep Collector config in Git; use processors for redaction (PII scrubbing).
- Correlate logs with
trace_id/span_idfields. - Define SLOs from RED/USE metrics derived from OTel, not ad-hoc counters only.
Limitations
- Spec and semantic conventions evolve - pin SDK versions.
- Logs signal maturity varies by language; confirm SDK support.
- OTel does not replace on-call process or runbooks.
Related skills
kubernetes- sidecars/DaemonSets for Collector deploymentelasticsearch- log backend often paired with tracesdocker- local Collector + app compose stacksnginx-hardening- edge headers and tracing passthrough