OpenTelemetry Instrumentation Guide
Expert guidance for implementing high-quality, cost-efficient OpenTelemetry telemetry.
Rules & Quick Reference
| Use Case / Rule |
Description |
| telemetry |
Entrypoint — signal types, correlation, and navigation |
| resolve-values |
Resolving configuration values from the codebase |
| verify-dependencies |
Verifying instrumentation packages and versions exist before adding them |
| resources |
Resource attributes — service identity and environment |
| k8s |
Kubernetes deployment — downward API, pod spec |
| spans |
Spans — naming, kind, status, and hygiene |
| logs |
Logs — structured logging, severity, trace correlation |
| metrics |
Metrics — instrument types, naming, units, cardinality |
| sensitive-data |
Sensitive data — PII prevention, sanitization, redaction |
| capture-database-query-parameters |
Prepared-statement parameter capture per language (Java, .NET, Python, Node.js, Go) |
| validation |
Telemetry validation — post-deployment verification checklist |
| nodejs |
Node.js instrumentation setup |
| go |
Go instrumentation setup |
| python |
Python instrumentation setup |
| java |
Java instrumentation setup |
| scala |
Scala instrumentation setup |
| dotnet |
.NET instrumentation setup |
| ruby |
Ruby instrumentation setup |
| php |
PHP instrumentation setup |
| browser |
Browser instrumentation setup |
| nextjs |
Next.js full-stack instrumentation (App Router) |
Official documentation
Getting started
Follow these steps when instrumenting an application from scratch:
- Pick your SDK rule — choose the language-specific rule from the table above (e.g., nodejs, python).
- Set up resource attributes — define service identity and environment per resources.
- Add spans, metrics, and logs — instrument your code following spans, metrics, and logs.
- Guard sensitive data — scrub PII before export per sensitive-data.
- Validate — confirm telemetry reaches the backend using the checklist in validation.
The snippet below shows a complete span with attributes and status for Node.js — see nodejs for full setup including SDK initialisation, exporter configuration, and auto-instrumentation:
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('my-service', '1.0.0');
tracer.startActiveSpan('operation-name', async (span) => {
try {
span.setAttribute('user.id', userId);
span.setAttribute('order.id', orderId);
const result = await processOrder(orderId);
span.setAttribute('order.status', result.status);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (err) {
// Record the exception as a structured log record, not span.recordException — see rules/spans.md
span.setStatus({ code: SpanStatusCode.ERROR, message: `${err.name}: ${err.message}` });
const spanContext = span.spanContext();
logger.error('operation-name.failed', {
'trace_id': spanContext.traceId,
'span_id': spanContext.spanId,
'exception.type': err.name,
'exception.message': err.message,
'exception.stacktrace': err.stack,
});
throw err;
} finally {
span.end();
}
});
Key principles
Signal density over volume
Every telemetry item should serve one of three purposes:
- Detect - Help identify that something is wrong
- Localize - Help pinpoint where the problem is
- Explain - Help understand why it happened
If it doesn't serve one of these purposes, don't emit it.
Sample in the pipeline, not the SDK
Use the AlwaysOn sampler (the default) in every SDK.
Do not configure SDK-side samplers — they make irreversible decisions before the outcome of a request is known.
Defer all sampling to the Collector, where policies can be changed centrally without redeploying applications.
SDK (AlwaysOn) → Collector (sampling) → Backend (retention)
↓ ↓ ↓
All spans Head or tail Storage policies
exported sampling applied
1---2name: otel-instrumentation3description: Configures trace spans, defines custom metrics, sets up log exporters, and optimizes sampling strategies for OpenTelemetry instrumentation. Use when instrumenting applications with traces, metrics, or logs. Triggers on requests for observability, telemetry, tracing, metrics collection, logging integration, or OTel setup.4license: Apache-2.05---6
7# OpenTelemetry Instrumentation Guide
8
9Expert guidance for implementing high-quality, cost-efficient OpenTelemetry telemetry.
10
11## Rules & Quick Reference
12
13| Use Case / Rule | Description |
14|-----------------|-------------|
15| [telemetry](./rules/telemetry.md) | **Entrypoint** — signal types, correlation, and navigation |
16| [resolve-values](./rules/resolve-values.md) | Resolving configuration values from the codebase |
17| [verify-dependencies](./rules/verify-dependencies.md) | Verifying instrumentation packages and versions exist before adding them |
18| [resources](./rules/resources.md) | Resource attributes — service identity and environment |
19| [k8s](./rules/platforms/k8s.md) | Kubernetes deployment — downward API, pod spec |
20| [spans](./rules/spans.md) | Spans — naming, kind, status, and hygiene |
21| [logs](./rules/logs.md) | Logs — structured logging, severity, trace correlation |
22| [metrics](./rules/metrics.md) | Metrics — instrument types, naming, units, cardinality |
23| [sensitive-data](./rules/sensitive-data.md) | Sensitive data — PII prevention, sanitization, redaction |
24| [capture-database-query-parameters](./rules/capture-database-query-parameters.md) | Prepared-statement parameter capture per language (Java, .NET, Python, Node.js, Go) |
25| [validation](./rules/validation.md) | Telemetry validation — post-deployment verification checklist |
26| [nodejs](./rules/sdks/nodejs.md) | Node.js instrumentation setup |
27| [go](./rules/sdks/go.md) | Go instrumentation setup |
28| [python](./rules/sdks/python.md) | Python instrumentation setup |
29| [java](./rules/sdks/java.md) | Java instrumentation setup |
30| [scala](./rules/sdks/scala.md) | Scala instrumentation setup |
31| [dotnet](./rules/sdks/dotnet.md) | .NET instrumentation setup |
32| [ruby](./rules/sdks/ruby.md) | Ruby instrumentation setup |
33| [php](./rules/sdks/php.md) | PHP instrumentation setup |
34| [browser](./rules/sdks/browser.md) | Browser instrumentation setup |
35| [nextjs](./rules/sdks/nextjs.md) | Next.js full-stack instrumentation (App Router) |
36
37## Official documentation
38
39- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
40- [Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)
41- [Dash0 Integration Hub](https://www.dash0.com/hub/integrations)
42
43## Getting started
44
45Follow these steps when instrumenting an application from scratch:
46
471. **Pick your SDK rule** — choose the language-specific rule from the table above (e.g., [nodejs](./rules/sdks/nodejs.md), [python](./rules/sdks/python.md)).
482. **Set up resource attributes** — define service identity and environment per [resources](./rules/resources.md).
493. **Add spans, metrics, and logs** — instrument your code following [spans](./rules/spans.md), [metrics](./rules/metrics.md), and [logs](./rules/logs.md).
504. **Guard sensitive data** — scrub PII before export per [sensitive-data](./rules/sensitive-data.md).
515. **Validate** — confirm telemetry reaches the backend using the checklist in [validation](./rules/validation.md).
52
53The snippet below shows a complete span with attributes and status for Node.js — see [nodejs](./rules/sdks/nodejs.md) for full setup including SDK initialisation, exporter configuration, and auto-instrumentation:
54
55```js
56import { trace, SpanStatusCode } from '@opentelemetry/api';
57const tracer = trace.getTracer('my-service', '1.0.0');
58
59tracer.startActiveSpan('operation-name', async (span) => {
60 try {
61 span.setAttribute('user.id', userId);
62 span.setAttribute('order.id', orderId);
63
64 const result = await processOrder(orderId);
65
66 span.setAttribute('order.status', result.status);
67 span.setStatus({ code: SpanStatusCode.OK });
68 return result;
69 } catch (err) {
70 // Record the exception as a structured log record, not span.recordException — see rules/spans.md
71 span.setStatus({ code: SpanStatusCode.ERROR, message: `${err.name}: ${err.message}` });
72 const spanContext = span.spanContext();
73 logger.error('operation-name.failed', {
74 'trace_id': spanContext.traceId,
75 'span_id': spanContext.spanId,
76 'exception.type': err.name,
77 'exception.message': err.message,
78 'exception.stacktrace': err.stack,
79 });
80 throw err;
81 } finally {
82 span.end();
83 }
84});
85```
86
87## Key principles
88
89### Signal density over volume
90
91Every telemetry item should serve one of three purposes:
92- **Detect** - Help identify that something is wrong
93- **Localize** - Help pinpoint where the problem is
94- **Explain** - Help understand why it happened
95
96If it doesn't serve one of these purposes, don't emit it.
97
98### Sample in the pipeline, not the SDK
99
100Use the `AlwaysOn` sampler (the default) in every SDK.
101Do not configure SDK-side samplers — they make irreversible decisions before the outcome of a request is known.
102Defer all sampling to the [Collector](../otel-collector/rules/sampling.md), where policies can be changed centrally without redeploying applications.
103
104<!-- eval:skip -->
105```
106SDK (AlwaysOn) → Collector (sampling) → Backend (retention)
107 ↓ ↓ ↓
108 All spans Head or tail Storage policies
109 exported sampling applied
110```