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:

go
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:

bash
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:

go
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

go
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:

shell
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

go
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

go
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

go
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

go
import "go.opentelemetry.io/contrib/detectors/aws/lambda"

lambdaDetector := lambda.NewResourceDetector()
res, err := lambdaDetector.Detect(context.Background())

Google Cloud resource detectors

Install GCP detector:

shell
go get go.opentelemetry.io/contrib/detectors/gcp

The GCP detector automatically identifies the environment (GCE, GKE, Cloud Run, Cloud Functions):

go
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:

yaml
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:

go
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:

go
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:

go
func printResource(res *resource.Resource) {
    iter := res.Iter()
    for iter.Next() {
        attr := iter.Attribute()
        fmt.Printf("  %s = %v\n", attr.Key, attr.Value.AsInterface())
    }
}

What's next?