Skip to main content

Overview

A Resource is an immutable representation of the entity producing telemetry. It captures information about your application, service, or infrastructure component as a set of attributes. For example, a process running in a Kubernetes pod might have these resource attributes:
  • service.name - The name of your service
  • service.version - The version of your service
  • deployment.environment - The environment (production, staging, etc.)
  • k8s.pod.name - The Kubernetes pod name
  • k8s.namespace.name - The Kubernetes namespace

Creating Resources

Using the Builder

The recommended way to create a resource is using the builder pattern:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource = Resource::builder()
    .with_service_name("my_service")
    .with_attribute(KeyValue::new("service.version", "1.0.0"))
    .with_attribute(KeyValue::new("deployment.environment", "production"))
    .build();

Empty Resources

You can also start with an empty resource:
let resource = Resource::builder_empty()
    .with_attributes([
        KeyValue::new("service.name", "my_service"),
        KeyValue::new("custom.attribute", "value"),
    ])
    .build();

Resource Detectors

Resource detectors automatically discover information about your runtime environment. OpenTelemetry provides several built-in detectors:

Default Detectors

When you call Resource::builder(), these detectors are automatically included:
  1. SdkProvidedResourceDetector - Provides required SDK attributes
  2. TelemetryResourceDetector - Adds telemetry SDK information
  3. EnvResourceDetector - Reads from environment variables
use opentelemetry_sdk::Resource;

// This includes default detectors
let resource = Resource::builder().build();

Environment Variables

The EnvResourceDetector reads from standard OpenTelemetry environment variables:
# Set service name
export OTEL_SERVICE_NAME="my_service"

# Set additional attributes
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production,service.version=1.0.0"
use opentelemetry_sdk::Resource;

// Automatically picks up environment variables
let resource = Resource::builder().build();
The OTEL_SERVICE_NAME environment variable takes priority over service.name in OTEL_RESOURCE_ATTRIBUTES.

Custom Detectors

You can implement custom resource detectors:
use opentelemetry_sdk::resource::{ResourceDetector, Resource};
use opentelemetry::KeyValue;

struct CustomDetector;

impl ResourceDetector for CustomDetector {
    fn detect(&self) -> Resource {
        Resource::builder_empty()
            .with_attributes([
                KeyValue::new("custom.attribute", "detected_value"),
                KeyValue::new("host.name", std::env::var("HOSTNAME").unwrap_or_default()),
            ])
            .build()
    }
}

// Use the custom detector
let resource = Resource::builder()
    .with_detector(Box::new(CustomDetector))
    .build();

Additional Detectors

The opentelemetry-resource-detectors crate provides detectors for:
  • Operating System - OS type, version, and architecture
  • Process - Process ID, executable name, command line args
  • Host - Hostname and other host information

Service Name

The service.name attribute is required by the OpenTelemetry specification. The SDK determines it using this priority:
  1. OTEL_SERVICE_NAME environment variable
  2. service.name in OTEL_RESOURCE_ATTRIBUTES
  3. unknown_service:<executable_name> (fallback)
// Priority 1: OTEL_SERVICE_NAME (highest)
export OTEL_SERVICE_NAME="my_service"

// Priority 2: OTEL_RESOURCE_ATTRIBUTES
export OTEL_RESOURCE_ATTRIBUTES="service.name=my_service"

// Priority 3: Automatic fallback
// Results in "unknown_service:my_binary"
Always set a meaningful service name to identify your service in observability backends.

Using Resources

With Tracer Provider

use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my_service")
    .with_attribute(KeyValue::new("service.version", "1.0.0"))
    .build();

let provider = SdkTracerProvider::builder()
    .with_resource(resource)
    .with_simple_exporter(exporter)
    .build();

With Meter Provider

use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my_service")
    .build();

let provider = SdkMeterProvider::builder()
    .with_resource(resource)
    .with_periodic_exporter(exporter)
    .build();

With Logger Provider

use opentelemetry_sdk::logs::SdkLoggerProvider;
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my_service")
    .build();

let provider = SdkLoggerProvider::builder()
    .with_resource(resource)
    .with_simple_exporter(exporter)
    .build();

Merging Resources

Resources can be merged to combine attributes from multiple sources:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource_a = Resource::builder_empty()
    .with_attributes([
        KeyValue::new("service.name", "my_service"),
        KeyValue::new("a", "value_a"),
    ])
    .build();

let resource_b = Resource::builder_empty()
    .with_attributes([
        KeyValue::new("b", "value_b"),
        KeyValue::new("a", "value_a_override"),  // This will override
    ])
    .build();

let merged = resource_a.merge(&resource_b);
// Result: { service.name: "my_service", a: "value_a_override", b: "value_b" }
When merging, attributes from the second resource (parameter) override those from the first resource (caller).

Schema URL

Resources can have an associated schema URL that defines the semantic conventions being used:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource = Resource::builder_empty()
    .with_schema_url(
        vec![KeyValue::new("service.name", "my_service")],
        "https://opentelemetry.io/schemas/1.17.0"
    )
    .build();

Schema URL Merging Rules

When merging resources with schema URLs:
  1. If both have the same schema URL, it’s preserved
  2. If both have different schema URLs, the result has no schema URL
  3. If one has a schema URL and the other doesn’t, the existing schema URL is used
let r1 = Resource::from_schema_url(
    [KeyValue::new("a", "1")],
    "https://opentelemetry.io/schemas/1.17.0"
);

let r2 = Resource::from_schema_url(
    [KeyValue::new("b", "2")],
    "https://opentelemetry.io/schemas/1.17.0"
);

let merged = r1.merge(&r2);
// Schema URL: https://opentelemetry.io/schemas/1.17.0 (same)

let r3 = Resource::from_schema_url(
    [KeyValue::new("c", "3")],
    "https://opentelemetry.io/schemas/1.18.0"
);

let merged = r1.merge(&r3);
// Schema URL: None (different)

Accessing Resource Attributes

use opentelemetry::Key;

let resource = Resource::builder()
    .with_service_name("my_service")
    .build();

// Get a single attribute
if let Some(service_name) = resource.get(&Key::new("service.name")) {
    println!("Service: {}", service_name);
}

// Iterate over all attributes
for (key, value) in resource.iter() {
    println!("{}: {:?}", key, value);
}

// Check if resource is empty
if resource.is_empty() {
    println!("No resource attributes");
}

// Get number of attributes
let count = resource.len();

Standard Resource Attributes

OpenTelemetry defines semantic conventions for common resource attributes:

Service Attributes

  • service.name - Logical name of the service (required)
  • service.version - Version of the service
  • service.namespace - Namespace for service grouping
  • service.instance.id - Unique identifier for the service instance

Deployment Attributes

  • deployment.environment - Environment (production, staging, development)
  • deployment.name - Name of the deployment

Telemetry SDK Attributes

  • telemetry.sdk.name - Name of the SDK (automatically set)
  • telemetry.sdk.language - Language of the SDK (automatically set to “rust”)
  • telemetry.sdk.version - Version of the SDK (automatically set)

Example

use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource = Resource::builder()
    .with_service_name("payment-service")
    .with_attributes([
        KeyValue::new("service.version", "2.1.0"),
        KeyValue::new("service.namespace", "ecommerce"),
        KeyValue::new("deployment.environment", "production"),
        KeyValue::new("cloud.provider", "aws"),
        KeyValue::new("cloud.region", "us-east-1"),
    ])
    .build();

Complete Example

Here’s a complete example showing resource configuration:
use opentelemetry::{global, KeyValue};
use opentelemetry_sdk::{
    Resource,
    trace::SdkTracerProvider,
};

fn main() {
    // Create a resource with multiple sources
    let resource = Resource::builder()
        // Sets service.name
        .with_service_name("my_service")
        // Add custom attributes
        .with_attributes([
            KeyValue::new("service.version", env!("CARGO_PKG_VERSION")),
            KeyValue::new("deployment.environment", "production"),
        ])
        // Add custom detector
        .with_detector(Box::new(CustomDetector))
        .build();

    // Use the resource with a provider
    let provider = SdkTracerProvider::builder()
        .with_resource(resource)
        .with_simple_exporter(exporter)
        .build();

    global::set_tracer_provider(provider.clone());

    // All telemetry will include the resource attributes
    let tracer = global::tracer("my_component");
    tracer.in_span("operation", |cx| {
        // This span will have the resource attributes attached
    });

    provider.shutdown().unwrap();
}

Best Practices

Always set a service name: This is the most important resource attribute for identifying your service in observability backends.
Use environment variables in production: Configure resources via OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES for flexibility.
Include version information: Add service.version to track which version of your service is running.
Leverage detectors: Use resource detectors to automatically capture environmental information instead of manually configuring it.

Next Steps

Context Propagation

Learn how context flows across services

Signals

Understand traces, metrics, and logs

Build docs developers (and LLMs) love