Skip to main content

Overview

A Resource represents the entity producing telemetry data. Resources are immutable and contain attributes that describe your application, service, host, and environment.

Resource attributes

Common resource attributes include:
  • service.name - Your service name
  • service.version - Application version
  • service.instance.id - Unique instance identifier
  • host.name - Hostname
  • process.pid - Process ID
  • deployment.environment - Environment (dev, staging, prod)

Resource builders

OpenTelemetry Rust provides two ways to create resources:

Default builder

Includes built-in detectors:
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my-service")
    .build();
The default builder includes:
  • SdkProvidedResourceDetector - SDK name and version
  • TelemetryResourceDetector - Telemetry SDK information
  • EnvResourceDetector - Environment variables

Empty builder

Start with no attributes:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource = Resource::builder_empty()
    .with_attributes(vec![
        KeyValue::new("service.name", "my-service"),
        KeyValue::new("service.version", "1.0.0"),
    ])
    .build();

Built-in resource detectors

SdkProvidedResourceDetector

Provides OpenTelemetry SDK information:
use opentelemetry_sdk::resource::SdkProvidedResourceDetector;

let detector = SdkProvidedResourceDetector;
let resource = detector.detect();

// Attributes detected:
// - telemetry.sdk.name = "opentelemetry"
// - telemetry.sdk.language = "rust"
// - telemetry.sdk.version = "0.31.0"

TelemetryResourceDetector

Provides telemetry SDK-specific information:
use opentelemetry_sdk::resource::TelemetryResourceDetector;

let detector = TelemetryResourceDetector;
let resource = detector.detect();

EnvResourceDetector

Detects resources from environment variables:
use opentelemetry_sdk::resource::EnvResourceDetector;

let detector = EnvResourceDetector::new();
let resource = detector.detect();
Environment variables:
  • OTEL_RESOURCE_ATTRIBUTES - Key-value pairs: key1=value1,key2=value2
  • OTEL_SERVICE_NAME - Service name (overrides service.name in attributes)
Example:
export OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES="service.version=1.0.0,deployment.environment=production"

Service name priority

Service name is resolved in this order (highest to lowest priority):
  1. OTEL_SERVICE_NAME environment variable
  2. service.name in OTEL_RESOURCE_ATTRIBUTES
  3. with_service_name() builder method
  4. service.name in custom attributes
  5. Default: "unknown_service"
use opentelemetry_sdk::Resource;

// Priority 3: Builder method
let resource = Resource::builder()
    .with_service_name("my-service")
    .build();

// Priority 1: Environment variable (overrides builder)
// export OTEL_SERVICE_NAME=production-service

Custom resource detectors

Implement the ResourceDetector trait:
use opentelemetry_sdk::resource::ResourceDetector;
use opentelemetry::KeyValue;

struct CustomDetector;

impl ResourceDetector for CustomDetector {
    fn detect(&self) -> Vec<KeyValue> {
        vec![
            KeyValue::new("custom.attribute", "value"),
            KeyValue::new("build.id", env!("BUILD_ID")),
        ]
    }
}
Use custom detector:
let resource = Resource::builder_empty()
    .with_detector(Box::new(CustomDetector))
    .build();

Merging resources

Merge multiple resources together:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let base_resource = Resource::builder()
    .with_service_name("my-service")
    .build();

let custom_resource = Resource::builder_empty()
    .with_attributes(vec![
        KeyValue::new("deployment.environment", "production"),
        KeyValue::new("service.version", "2.0.0"),
    ])
    .build();

// Merge resources (custom_resource takes precedence)
let merged = base_resource.merge(&custom_resource);

Schema URLs

Resources can include a schema URL that identifies the semantic convention version:
use opentelemetry_sdk::Resource;
use opentelemetry::KeyValue;

let resource = Resource::builder_empty()
    .with_schema_url("https://opentelemetry.io/schemas/1.21.0")
    .with_attributes(vec![
        KeyValue::new("service.name", "my-service"),
    ])
    .build();
When merging resources with different schema URLs, the operation may fail if the schemas are incompatible.

Using resources with providers

TracerProvider

use opentelemetry_sdk::{
    trace::TracerProvider,
    Resource,
};

let resource = Resource::builder()
    .with_service_name("my-trace-service")
    .build();

let provider = TracerProvider::builder()
    .with_resource(resource)
    .build();

MeterProvider

use opentelemetry_sdk::{
    metrics::MeterProvider,
    Resource,
};

let resource = Resource::builder()
    .with_service_name("my-metrics-service")
    .build();

let provider = MeterProvider::builder()
    .with_resource(resource)
    .build();

LoggerProvider

use opentelemetry_sdk::{
    logs::LoggerProvider,
    Resource,
};

let resource = Resource::builder()
    .with_service_name("my-logging-service")
    .build();

let provider = LoggerProvider::builder()
    .with_resource(resource)
    .build();

Complete example

use opentelemetry::KeyValue;
use opentelemetry_sdk::{
    trace::{TracerProvider, BatchSpanProcessor},
    Resource,
};
use opentelemetry_otlp::SpanExporter;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create custom resource
    let resource = Resource::builder()
        .with_service_name(env!("CARGO_PKG_NAME"))
        .with_attributes(vec![
            KeyValue::new("service.version", env!("CARGO_PKG_VERSION")),
            KeyValue::new("deployment.environment", "production"),
            KeyValue::new("service.instance.id", uuid::Uuid::new_v4().to_string()),
        ])
        .build();
    
    // Use resource in provider
    let exporter = SpanExporter::builder().with_http().build()?;
    let processor = BatchSpanProcessor::builder(exporter, opentelemetry_sdk::runtime::Tokio).build();
    
    let provider = TracerProvider::builder()
        .with_resource(resource)
        .with_span_processor(processor)
        .build();
    
    Ok(())
}

OS and Process detectors

For more advanced resource detection (OS, process, host info), use the separate opentelemetry-resource-detectors crate:
[dependencies]
opentelemetry-resource-detectors = "0.1"
use opentelemetry_resource_detectors::{OsResourceDetector, ProcessResourceDetector};
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_detector(Box::new(OsResourceDetector))
    .with_detector(Box::new(ProcessResourceDetector))
    .build();
This provides:
  • host.name
  • host.arch
  • os.type
  • os.description
  • process.pid
  • process.executable.name
  • process.command_line
  • process.runtime.name
  • process.runtime.version

Best practices

Always set service.name - This is the most important resource attribute for identifying your service in observability backends.
Use semantic conventions - Follow OpenTelemetry semantic conventions for standard attribute names.
Environment-based configuration - Use OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES environment variables for deployment-specific configuration.

Resources concept

Learn about resource fundamentals

Semantic conventions

Standard attribute names

Build docs developers (and LLMs) love