Set OTEL_SERVICE_NAME=payments-worker, restart, and your spans can still say treasury-agent. The variable is fine. A setup like the one in the quickstart never reads it.
Resource or span?
A resource describes whoever emits the telemetry (OpenTelemetry resources). It’s set once and attached to every span from that SDK.
| Resource attribute | Span attribute | |
|---|---|---|
| Describes | The process, service, host | One operation |
| Set | Once, at SDK setup | Per span |
| Examples | service.name, deployment.environment.name, telemetry.sdk.language |
blockchain.tx.hash, gen_ai.agent.id |
| Changes between spans? | No | Yes |
Why is the variable ignored?
Because with sdk-trace-node 2.x, a resource you pass in replaces the default one, and environment variables are read by a detector you have to add. Here are three setups, each run with and without OTEL_SERVICE_NAME=payments-worker OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=staging:
| Setup | service.name |
Env vars used? | telemetry.sdk.* |
|---|---|---|---|
No resource option |
unknown_service:/…/bin/node |
No | Yes |
resourceFromAttributes({...}) |
treasury-agent |
No | No |
Default + yours + envDetector |
payments-worker |
Yes | Yes |
The third one does what most people expect:
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';import { defaultResource, detectResources, envDetector, resourceFromAttributes } from '@opentelemetry/resources';
const resource = defaultResource() .merge(resourceFromAttributes({ 'service.name': 'treasury-agent' })) .merge(detectResources({ detectors: [envDetector] })); // env vars win
new NodeTracerProvider({ resource, spanProcessors: [new SimpleSpanProcessor(exporter)] }).register();The later merge wins, so the environment can override what the code sets. NodeSDK from @opentelemetry/sdk-node reads both variables on its own, but there a serviceName passed in code wins over OTEL_SERVICE_NAME.
Is the agent a resource?
Sometimes. If a service runs exactly one agent, service.name can be the agent’s name, and that’s what the hashspan demo does. But one service often runs many agents or many runs of one. A resource can’t tell them apart, because it’s the same for every span.