What are OpenTelemetry resource attributes and how do you use them for service identification?
Quick Answer
Resource attributes are key-value metadata that describe the entity producing telemetry, such as the service name, version, deployment environment, and host. They are attached to all spans, metrics, and logs emitted by a service and are essential for filtering, grouping, and correlating telemetry across a distributed system.
Detailed Answer
Think of resource attributes like the return address on an envelope. No matter what is inside the envelope (a trace, a metric, a log), the return address tells you exactly who sent it, from where, and in what context. Without a return address, you receive thousands of envelopes with no way to sort them by sender. In a microservices architecture with dozens of services like payments-api, user-auth-service, and order-processing-service, resource attributes are what make telemetry data actionable rather than an undifferentiated flood of signals.
Resource attributes are defined by the OpenTelemetry Semantic Conventions, which provide a standardized vocabulary for describing telemetry sources. The most critical attribute is service.name, which uniquely identifies the logical service. Without it, backends like Jaeger or Grafana Tempo cannot group spans into service-level views. Other essential attributes include service.version (the deployed version, critical for correlating deployments with performance changes), service.namespace (grouping related services like the payments domain), deployment.environment (production, staging, development), and service.instance.id (distinguishing between replicas of the same service). Cloud-specific attributes like cloud.provider, cloud.region, and cloud.availability_zone are standardized under the semantic conventions for infrastructure correlation.
In the OpenTelemetry SDK, resource attributes are configured when creating the TracerProvider, MeterProvider, or LoggerProvider. They are set once at startup and apply to all telemetry emitted by that provider. The SDK automatically detects some resource attributes through resource detectors. For example, the AWS resource detector populates cloud.provider, cloud.region, and cloud.account.id. The Kubernetes resource detector fills in k8s.pod.name, k8s.namespace.name, and k8s.node.name. The process resource detector captures process.pid and process.runtime.name. You can combine multiple detectors with manually specified attributes, where manual attributes take precedence over detected ones. Environment variables like OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME provide a configuration mechanism that works without code changes, making it easy to set attributes in Kubernetes manifests or Docker Compose files.
In production, resource attributes are the foundation of effective observability. When the checkout-service experiences a latency spike, you filter traces by service.name=checkout-service and deployment.environment=production. When you deploy a new version, you compare metrics between service.version=2.3.1 and service.version=2.4.0 to detect regressions. When a specific availability zone has network issues, you filter by cloud.availability_zone=us-east-1a to isolate the blast radius. The OpenTelemetry Collector can also enrich telemetry with additional resource attributes using the resource processor, adding attributes like k8s.cluster.name that the SDK might not know about. This is particularly useful in multi-cluster deployments where the same service runs in multiple Kubernetes clusters.
A common gotcha is forgetting to set service.name, which causes backends to group all telemetry under a generic name like unknown_service. Another pitfall is inconsistent naming across services. If the payments-api team uses deployment.environment=prod while the user-auth-service team uses deployment.environment=production, dashboards and queries that filter on environment will miss half the data. Establishing a naming convention and enforcing it through the Collector's resource processor or through shared SDK configuration libraries prevents this fragmentation. High-cardinality resource attributes like service.instance.id are useful for debugging but should not be used as primary grouping dimensions in metrics backends, as they create a new time series per instance.