skip to content

In an OpenTelemetry SDK, what is a Resource, why does telemetry show up under a name like 'unknown_service' when it is not configured, and how do OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES and resource detectors interact?

level: middleimportance: must knowfreq 55%

answer

  1. resource = who emitted, attributes = what happened
  2. service.name required; fallback unknown_service
  3. OTEL_SERVICE_NAME beats service.name in the list
  4. detectors: host, process, container, orchestrator, cloud
  5. sent once per batch, never per-request data

basics

~20 s

A Resource is the immutable set of attributes identifying the entity producing telemetry — service name, version, instance, host, container. It is set once at SDK startup, attached to every signal. With no service name configured the SDK falls back to a placeholder like unknown_service.

solid answer

~50 s

The Resource answers 'who emitted this', as opposed to span attributes which answer 'what happened'. It is built once when the SDK starts and attached to every span, metric and log the process produces, so backends group and route telemetry by it. `service.name` is the one attribute treated as required; when nothing supplies it the SDK substitutes a placeholder such as `unknown_service` (often suffixed with the runtime), which is why unconfigured deployments all collapse into one meaningless bucket. Three sources merge: **detectors** (host, OS, process, container, orchestrator, cloud) contribute automatically; **OTEL_RESOURCE_ATTRIBUTES** carries a comma-separated `key=value` list; **OTEL_SERVICE_NAME** sets the service name and takes precedence over a `service.name` inside the attributes list. Programmatic configuration is normally applied on top. Because the resource is sent once per export batch rather than per span, it is cheap — the right place for identity, and the wrong place for anything that varies per request.

go deeper

for a junior

Say the resource identifies the service producing telemetry, that service.name is essential, and that missing it yields an unknown_service placeholder.

for a middle

Cover the three sources — detectors, environment variables, programmatic — the dedicated service-name variable's precedence, and once-per-batch attachment.

for a senior

Add detector startup hazards, instance-id cardinality in metrics, downward-API injection, and pinning environment/version from the deploy pipeline.

for a principal

Treat identity as a deploy-time contract: one source of truth for name/version/environment across the fleet, plus a policy on where enrichment happens, process or collector.

## Identity versus event Every piece of telemetry has two kinds of context. **Span attributes** describe the individual operation: which route, which status, how many rows. **Resource attributes** describe the producing entity and are identical for the whole process lifetime: which service, which version, which instance, which host and container. Separating them is not cosmetic. The resource is emitted once per export batch — in OTLP, spans are nested under a resource block — so a hundred spans in a batch carry the identity once instead of a hundred times. It is also the natural key for grouping, routing and multi-tenant separation on the backend. ## The attributes that matter - **service.name** — required in practice; the primary grouping key everywhere. - **service.version** — makes 'did the new build do this?' answerable. - **service.namespace** — disambiguates identically named services across teams. - **service.instance.id** — distinguishes replicas; needed whenever per-instance behaviour matters. - **deployment environment** — the prod/staging separator. Note that its conventional key was renamed during stabilisation, so pin the convention version you target. - Infrastructure keys from detectors: host, OS, process, container, orchestrator pod/node, cloud provider and region. ## Where the values come from **Detectors** run at startup and read the ambient environment: process metadata, hostname, container id from the cgroup, orchestrator downward-API values, cloud instance metadata endpoints. They give you infrastructure identity for free. Two cautions: a detector that calls a metadata endpoint can add startup latency (and hang if that endpoint is unreachable in an unexpected environment), and detector sets differ across SDKs and distributions, so do not assume a key exists because it appeared in another language. **Environment variables** are the portable path. `OTEL_RESOURCE_ATTRIBUTES` takes a comma-separated `key=value` list, with values percent-encoded when they contain reserved characters. `OTEL_SERVICE_NAME` exists because service name is set so often, and it wins over any `service.name` supplied in the attributes list. **Programmatic** configuration composes on top, and is where you add values only the application knows — a build id, a feature-set label. When sources disagree, the general shape is: defaults and detectors first, then environment, then explicit programmatic customisation, with the dedicated service-name variable outranking the generic list. Exact precedence has varied slightly between SDK versions, so if two sources set the same key, set it in one place rather than reasoning about the winner. ## Why 'unknown_service' happens The specification says implementations must supply a fallback service name rather than emitting nothing, so a misconfigured deployment produces telemetry attributed to a placeholder. It is a diagnostic signal, not a bug: seeing it means no environment variable, no programmatic setting and no detector supplied a name — usually a container spec that forgot the variable, or an agent installed without configuration. Multiple unrelated services then merge into one blob, and dashboards filtered by service name silently show nothing. ## Practical rules - **Nothing per-request goes in the resource.** It is immutable and process-wide; a user or request id belongs in span attributes. - **Set the identity trio at deploy time** — service name, version, environment — from the same source of truth that stamps the build, so they cannot drift. - **Beware instance-id cardinality** in metrics. Per-instance series in an autoscaling fleet with short-lived replicas produce large numbers of series; that is a metrics-backend decision, and some pipelines deliberately drop instance id before storage. - **In orchestrated environments prefer downward-API injection** into the environment variable over relying on every SDK having the same detector. - Resource attributes are attached in the process, so if a collector later needs to enrich or override them, that is a pipeline concern, not an SDK one.

  • A service sets service.name inside OTEL_RESOURCE_ATTRIBUTES and also sets OTEL_SERVICE_NAME to a different value. Which wins?
    The dedicated OTEL_SERVICE_NAME variable takes precedence over a service.name entry in the attributes list — it exists as a convenience for the most-set attribute. Relying on that precedence is still poor practice: set the name in exactly one place so a reader does not have to know the rule.
  • Why not put the user id in the resource so every span carries it without extra work?
    The resource is immutable for the process lifetime and describes the emitting entity, so a per-request value there is simply wrong — it would be attached to every span from every request, including other users'. Per-request values belong in span attributes or, if they must cross services, in propagated baggage that instrumentation copies onto spans deliberately.

saying these in an interview costs you the question

  • Confusing resource attributes with span attributes, or trying to set per-request values on the resource
  • Assuming a specific detector exists in every language SDK and distribution
  • Deploying without a service name and then hunting for 'missing' telemetry that is filed under unknown_service
  • Setting the same key from several sources and relying on precedence rules that shift between versions

context