Resource Detectors in OpenTelemetry Go
Resource detectors automatically detect and collect information about the environment in which your application is running. This information is attached to all telemetry data (traces, metrics, and logs) to provide context for monitoring and debugging.
Overview
Resources describe the entity producing telemetry. Common resource attributes include:
- service.name - Logical name of the service
- service.version - Version of the service
- deployment.environment - Deployment environment (production, staging, etc.)
- host.name - Hostname of the machine
- cloud.provider - Cloud provider (aws, gcp, azure)
Setting resources with uptrace-go
By default, uptrace-go uses host and environment resource detectors. You can add custom attributes and additional detectors:
import (
"github.com/uptrace/uptrace-go/uptrace"
"go.opentelemetry.io/contrib/detectors/aws/ec2"
)
uptrace.ConfigureOpentelemetry(
// copy your project DSN here or use UPTRACE_DSN env var
// uptrace.WithDSN("<FIXME>"),
uptrace.WithServiceName("myservice"),
uptrace.WithServiceVersion("1.0.0"),
uptrace.WithDeploymentEnvironment("production"),
// Add cloud-specific detectors
uptrace.WithResourceDetectors(ec2.NewResourceDetector()),
)
Custom resources via environment variables
The simplest way to add custom resource attributes is via the OTEL_RESOURCE_ATTRIBUTES environment variable:
export OTEL_SERVICE_NAME="myservice"
export OTEL_RESOURCE_ATTRIBUTES="service.version=1.2.0,deployment.environment=production,team=platform"
These attributes are automatically detected when using resource.WithFromEnv().
Custom resources in code
You can create resources programmatically with full control over attributes:
package main
import (
"context"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func createResource(ctx context.Context) (*resource.Resource, error) {
return resource.New(ctx,
// Include default detectors
resource.WithHost(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithFromEnv(),
// Add custom attributes
resource.WithAttributes(
semconv.ServiceName("myservice"),
semconv.ServiceVersion("1.2.0"),
semconv.ServiceInstanceID("instance-1"),
semconv.DeploymentEnvironment("production"),
),
)
}
Using resources with TracerProvider
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func main() {
ctx := context.Background()
// Create resource
res, err := resource.New(ctx,
resource.WithHost(),
resource.WithProcess(),
resource.WithAttributes(
semconv.ServiceName("myservice"),
semconv.ServiceVersion("1.0.0"),
),
)
if err != nil {
log.Fatal(err)
}
// Create exporter
exporter, err := otlptracegrpc.New(ctx)
if err != nil {
log.Fatal(err)
}
// Create TracerProvider with resource
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
defer tp.Shutdown(ctx)
otel.SetTracerProvider(tp)
}
AWS resource detectors
Install AWS detectors:
go get go.opentelemetry.io/contrib/detectors/aws/ec2
go get go.opentelemetry.io/contrib/detectors/aws/ecs
go get go.opentelemetry.io/contrib/detectors/aws/eks
go get go.opentelemetry.io/contrib/detectors/aws/lambda
EC2
Detects: cloud.provider, cloud.platform, cloud.region, cloud.account.id, cloud.availability_zone, host.id, host.type, host.name, host.image.id
import "go.opentelemetry.io/contrib/detectors/aws/ec2"
ec2Detector := ec2.NewResourceDetector()
res, err := ec2Detector.Detect(context.Background())
ECS
Detects: aws.ecs.container.arn, aws.ecs.cluster.arn, aws.ecs.launchtype, aws.ecs.task.arn, aws.ecs.task.family, aws.ecs.task.revision
import "go.opentelemetry.io/contrib/detectors/aws/ecs"
ecsDetector := ecs.NewResourceDetector()
res, err := ecsDetector.Detect(context.Background())
EKS
Detects: cloud.provider, cloud.platform, k8s.cluster.name
import "go.opentelemetry.io/contrib/detectors/aws/eks"
eksDetector := eks.NewResourceDetector()
res, err := eksDetector.Detect(context.Background())
Lambda
Detects: cloud.provider, cloud.region, faas.name, faas.version
import "go.opentelemetry.io/contrib/detectors/aws/lambda"
lambdaDetector := lambda.NewResourceDetector()
res, err := lambdaDetector.Detect(context.Background())
Google Cloud resource detectors
Install GCP detector:
go get go.opentelemetry.io/contrib/detectors/gcp
The GCP detector automatically identifies the environment (GCE, GKE, Cloud Run, Cloud Functions):
import (
"context"
"go.opentelemetry.io/contrib/detectors/gcp"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func createGCPResource(ctx context.Context) (*resource.Resource, error) {
return resource.New(ctx,
resource.WithDetectors(gcp.NewDetector()),
resource.WithTelemetrySDK(),
resource.WithFromEnv(),
resource.WithAttributes(
semconv.ServiceName("myservice"),
semconv.ServiceVersion("1.0.0"),
),
)
}
Kubernetes on GKE
For GKE, set Kubernetes attributes via environment variables in your Pod spec:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CONTAINER_NAME
value: my-container
- name: OTEL_RESOURCE_ATTRIBUTES
value: k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(NAMESPACE_NAME),k8s.container.name=$(CONTAINER_NAME)
Then use resource.WithFromEnv() to read these attributes.
Combining multiple detectors
You can combine multiple detectors for comprehensive resource detection:
package main
import (
"context"
"go.opentelemetry.io/contrib/detectors/aws/ec2"
"go.opentelemetry.io/contrib/detectors/aws/ecs"
"go.opentelemetry.io/contrib/detectors/aws/eks"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func createAWSResource(ctx context.Context) (*resource.Resource, error) {
return resource.New(ctx,
// Cloud detectors (will use what's available)
resource.WithDetectors(
ec2.NewResourceDetector(),
ecs.NewResourceDetector(),
eks.NewResourceDetector(),
),
// Built-in detectors
resource.WithHost(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithFromEnv(),
// Custom attributes
resource.WithAttributes(
semconv.ServiceName("myservice"),
semconv.ServiceVersion("1.0.0"),
),
)
}
Custom resource detector
Create custom detectors for application-specific metadata:
package main
import (
"context"
"os"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
)
// CustomDetector detects application-specific resources
type CustomDetector struct{}
func (d *CustomDetector) Detect(ctx context.Context) (*resource.Resource, error) {
attrs := []attribute.KeyValue{
attribute.String("app.team", "platform"),
attribute.String("app.tier", "backend"),
}
// Detect from environment
if datacenter := os.Getenv("DATACENTER"); datacenter != "" {
attrs = append(attrs, attribute.String("deployment.datacenter", datacenter))
}
if gitSHA := os.Getenv("GIT_SHA"); gitSHA != "" {
attrs = append(attrs, attribute.String("vcs.revision", gitSHA))
}
return resource.NewWithAttributes(
resource.Default().SchemaURL(),
attrs...,
), nil
}
// Usage
func createResourceWithCustomDetector(ctx context.Context) (*resource.Resource, error) {
return resource.New(ctx,
resource.WithDetectors(&CustomDetector{}),
resource.WithHost(),
resource.WithProcess(),
)
}
Production considerations
Performance
Resource detection runs during application initialization:
- Cloud metadata services (EC2, GCE) require HTTP calls that add startup latency
- Use only the detectors you need for your environment
- Consider caching or pre-computing resource attributes
Error handling
Resource detectors are designed to be resilient:
- Failed detectors log warnings but don't prevent application startup
- Missing cloud metadata falls back gracefully
- Network timeouts are handled with reasonable defaults
Debugging resources
Print detected resource attributes for debugging:
func printResource(res *resource.Resource) {
iter := res.Iter()
for iter.Next() {
attr := iter.Attribute()
fmt.Printf(" %s = %v\n", attr.Key, attr.Value.AsInterface())
}
}