The OpenTelemetry Go SDK resource package provides functionality for detecting and creating resources that describe the entity producing telemetry data.
import "go.opentelemetry.io/otel/sdk/resource"A Resource is an immutable set of attributes that describe the entity producing telemetry. Resources typically include information about the service, host, container, process, and runtime environment.
type Resource struct {
// Has unexported fields
}// New creates a Resource from detectors and options
func New(ctx context.Context, opts ...Option) (*Resource, error)
// NewWithAttributes creates a Resource from attributes and associates it with a schema URL
func NewWithAttributes(schemaURL string, attrs ...attribute.KeyValue) *Resource
// NewSchemaless creates a Resource from attributes without a schema URL
func NewSchemaless(attrs ...attribute.KeyValue) *ResourceExample:
package main
import (
"context"
"log"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func main() {
ctx := context.Background()
// Create resource with attributes
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
semconv.DeploymentEnvironment("production"),
),
)
if err != nil {
log.Fatal(err)
}
// Create resource with built-in detectors
res, err = resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
),
resource.WithProcess(),
resource.WithHost(),
resource.WithOS(),
resource.WithContainer(),
)
if err != nil {
log.Fatal(err)
}
}// Attributes returns a copy of attributes from the resource
func (r *Resource) Attributes() []attribute.KeyValue
// SchemaURL returns the schema URL associated with the Resource
func (r *Resource) SchemaURL() string
// Iter returns an iterator of the Resource attributes
func (r *Resource) Iter() attribute.Iterator
// Equal reports whether r and o represent the same resource
func (r *Resource) Equal(o *Resource) bool
// Equivalent returns an attribute.Distinct that uniquely identifies the Resource
func (r *Resource) Equivalent() attribute.Distinct
// MarshalLog returns logging data about the Resource
func (r *Resource) MarshalLog() any
// String returns a human-readable form of the resource
func (r *Resource) String() stringOptions for creating resources.
type Option interface {
// Has unexported methods
}Add attributes to the resource.
func WithAttributes(attributes ...attribute.KeyValue) OptionExample:
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
semconv.ServiceInstanceID("instance-1"),
semconv.DeploymentEnvironment("production"),
),
)Associate a schema URL with the resource.
func WithSchemaURL(schemaURL string) OptionExample:
res, err := resource.New(ctx,
resource.WithSchemaURL(semconv.SchemaURL),
resource.WithAttributes(/*...*/),
)The SDK provides several built-in resource detectors.
Detect process information (PID, executable name, command, owner, runtime).
func WithProcess() OptionDetected Attributes:
process.pidprocess.executable.nameprocess.executable.pathprocess.command_argsprocess.ownerprocess.runtime.nameprocess.runtime.versionprocess.runtime.descriptionExample:
res, err := resource.New(ctx,
resource.WithProcess(),
)Detect host information (hostname, ID, name, type, architecture).
func WithHost() OptionDetected Attributes:
host.namehost.idhost.archhost.typeExample:
res, err := resource.New(ctx,
resource.WithHost(),
)Detect operating system information (type, description).
func WithOS() OptionDetected Attributes:
os.typeos.descriptionos.nameos.versionExample:
res, err := resource.New(ctx,
resource.WithOS(),
)Detect container information (ID, name, image).
func WithContainer() OptionDetected Attributes:
container.idcontainer.namecontainer.image.namecontainer.image.tagExample:
res, err := resource.New(ctx,
resource.WithContainer(),
)Detect resource attributes from environment variables.
func WithFromEnv() OptionReads attributes from OTEL_RESOURCE_ATTRIBUTES environment variable.
Example:
export OTEL_RESOURCE_ATTRIBUTES="key1=value1,key2=value2"res, err := resource.New(ctx,
resource.WithFromEnv(),
)Use custom detectors.
func WithDetectors(d ...Detector) OptionExample:
// Custom detector
type customDetector struct{}
func (d customDetector) Detect(ctx context.Context) (*resource.Resource, error) {
return resource.NewSchemaless(
attribute.String("custom.key", "custom.value"),
), nil
}
res, err := resource.New(ctx,
resource.WithDetectors(customDetector{}),
)For fine-grained control, you can use individual attribute detectors instead of the composite ones.
// WithProcessPID adds the process identifier (PID)
func WithProcessPID() Option
// WithProcessExecutableName adds the process executable name
func WithProcessExecutableName() Option
// WithProcessExecutablePath adds the full path to the process executable
func WithProcessExecutablePath() Option
// WithProcessCommandArgs adds all command arguments (including the executable)
// Warning: May contain sensitive information
func WithProcessCommandArgs() Option
// WithProcessOwner adds the username of the user that owns the process
func WithProcessOwner() Option
// WithProcessRuntimeName adds the name of the runtime (e.g., "go")
func WithProcessRuntimeName() Option
// WithProcessRuntimeVersion adds the version of the runtime
func WithProcessRuntimeVersion() Option
// WithProcessRuntimeDescription adds additional runtime description
func WithProcessRuntimeDescription() OptionExample:
// Add only specific process attributes
res, err := resource.New(ctx,
resource.WithProcessPID(),
resource.WithProcessExecutableName(),
resource.WithProcessRuntimeName(),
resource.WithProcessRuntimeVersion(),
)// WithHostID adds host ID information
func WithHostID() OptionExample:
res, err := resource.New(ctx,
resource.WithHostID(),
resource.WithAttributes(
semconv.HostName("my-host"),
),
)// WithOSType adds the operating system type (e.g., "linux", "darwin", "windows")
func WithOSType() Option
// WithOSDescription adds the operating system description
// Equivalent to the output of `uname -snrvm`
func WithOSDescription() OptionExample:
res, err := resource.New(ctx,
resource.WithOSType(),
resource.WithOSDescription(),
)// WithContainerID adds the container ID
// Note: Will not extract correct container ID in ECS environments
// Use the ECS resource detector instead for ECS
func WithContainerID() OptionExample:
res, err := resource.New(ctx,
resource.WithContainerID(),
)// StringDetector creates a custom detector for a single string attribute
func StringDetector(schemaURL string, k attribute.Key, f func() (string, error)) DetectorExample:
// Create a custom detector for a deployment ID
deploymentDetector := resource.StringDetector(
semconv.SchemaURL,
semconv.DeploymentEnvironmentKey,
func() (string, error) {
// Read from environment or configuration
return os.Getenv("DEPLOYMENT_ENV"), nil
},
)
res, err := resource.New(ctx,
resource.WithDetectors(deploymentDetector),
)Custom detectors implement the Detector interface.
type Detector interface {
// Detect returns a Resource describing the detected entity
Detect(ctx context.Context) (*Resource, error)
}Example:
type cloudProviderDetector struct{}
func (d cloudProviderDetector) Detect(ctx context.Context) (*resource.Resource, error) {
// Detect cloud provider metadata
provider := detectCloudProvider()
if provider == "" {
return resource.Empty(), nil
}
return resource.NewSchemaless(
semconv.CloudProvider(provider),
semconv.CloudAccountID("123456"),
semconv.CloudRegion("us-east-1"),
), nil
}
func detectCloudProvider() string {
// Detection logic
return "aws"
}Get the default resource.
// Default returns the default resource
func Default() *Resource
// Empty returns an empty resource
func Empty() *ResourceExample:
// Get default resource (includes service.name from OTEL_SERVICE_NAME)
res := resource.Default()
// Get empty resource
res := resource.Empty()Merge multiple resources together.
// Merge creates a new Resource by merging a and b
func Merge(a, b *Resource) (*Resource, error)Merge Rules:
Example:
// Create base resource
base, _ := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
),
)
// Create additional resource
additional, _ := resource.New(ctx,
resource.WithAttributes(
semconv.DeploymentEnvironment("production"),
semconv.ServiceInstanceID("instance-1"),
),
)
// Merge resources
merged, err := resource.Merge(base, additional)
if err != nil {
log.Printf("Error merging resources: %v", err)
}The SDK respects the following environment variables:
# Service name
OTEL_SERVICE_NAME=my-service
# Resource attributes as comma-separated key=value pairs
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.version=1.0.0package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
func createResource(ctx context.Context) (*resource.Resource, error) {
// Create resource with all recommended detectors
return resource.New(ctx,
// Static attributes
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
semconv.DeploymentEnvironment("production"),
),
// Built-in detectors
resource.WithProcess(), // Process information
resource.WithHost(), // Host information
resource.WithOS(), // OS information
resource.WithContainer(), // Container information (if running in container)
resource.WithFromEnv(), // From OTEL_RESOURCE_ATTRIBUTES
// Custom detector
resource.WithDetectors(&customDetector{}),
)
}
type customDetector struct{}
func (d *customDetector) Detect(ctx context.Context) (*resource.Resource, error) {
// Detect custom attributes
return resource.NewSchemaless(
attribute.String("custom.region", "us-east-1"),
attribute.String("custom.availability_zone", "us-east-1a"),
), nil
}
func main() {
ctx := context.Background()
// Create resource
res, err := createResource(ctx)
if err != nil {
log.Fatalf("Failed to create resource: %v", err)
}
// Log resource attributes
log.Println("Resource attributes:")
iter := res.Iter()
for iter.Next() {
attr := iter.Attribute()
log.Printf(" %s: %v", attr.Key, attr.Value.AsInterface())
}
// Use resource with tracer provider
// tp := sdktrace.NewTracerProvider(
// sdktrace.WithResource(res),
// )
// otel.SetTracerProvider(tp)
}import (
"context"
"os"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
type k8sDetector struct{}
func (d k8sDetector) Detect(ctx context.Context) (*resource.Resource, error) {
// Detect Kubernetes attributes from environment
var attrs []attribute.KeyValue
if podName := os.Getenv("K8S_POD_NAME"); podName != "" {
attrs = append(attrs, semconv.K8SPodName(podName))
}
if namespace := os.Getenv("K8S_NAMESPACE_NAME"); namespace != "" {
attrs = append(attrs, semconv.K8SNamespaceName(namespace))
}
if nodeName := os.Getenv("K8S_NODE_NAME"); nodeName != "" {
attrs = append(attrs, semconv.K8SNodeName(nodeName))
}
if deployment := os.Getenv("K8S_DEPLOYMENT_NAME"); deployment != "" {
attrs = append(attrs, semconv.K8SDeploymentName(deployment))
}
if len(attrs) == 0 {
return resource.Empty(), nil
}
return resource.NewSchemaless(attrs...), nil
}
func createK8sResource(ctx context.Context) (*resource.Resource, error) {
return resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
),
resource.WithDetectors(k8sDetector{}),
resource.WithHost(),
resource.WithProcess(),
)
}// Good: Always set service name
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
),
)
// Bad: Missing service name
res, err := resource.New(ctx) // Will use default// Good: Use built-in detectors for common attributes
res, err := resource.New(ctx,
resource.WithAttributes(semconv.ServiceName("my-service")),
resource.WithProcess(),
resource.WithHost(),
resource.WithOS(),
)res, err := resource.New(ctx,
resource.WithAttributes(/*...*/),
resource.WithProcess(),
)
if err != nil {
// Log error but continue with partial resource
log.Printf("Failed to detect some resource attributes: %v", err)
}
// res may be partial but still usable# Set via environment for flexibility
export OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod,k8s.cluster.name=productionres, err := resource.New(ctx,
resource.WithFromEnv(),
resource.WithProcess(),
)