Tracing Providers
5 minute read
The tracing package supports multiple providers for exporting traces. Choose the provider that best fits your environment and infrastructure.
Available Providers
| Provider | Use Case | Network Required | Best For |
|---|---|---|---|
| Noop | Default, no traces | No | Testing, disabled tracing |
| Stdout | Console output | No | Development, debugging |
| OTLP (gRPC) | OpenTelemetry collector | Yes | Production (preferred) |
| OTLP (HTTP) | OpenTelemetry collector | Yes | Production (alternative) |
Basic Configuration
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithNoop(),
)
defer tracer.Shutdown(context.Background())tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithStdout(),
)
defer tracer.Shutdown(context.Background())tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithServiceVersion("v1.0.0"),
tracing.WithOTLP("localhost:4317"),
)
if err := tracer.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer tracer.Shutdown(context.Background())tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithServiceVersion("v1.0.0"),
tracing.WithOTLPHTTP("http://localhost:4318"),
)
if err := tracer.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer tracer.Shutdown(context.Background())Noop Provider
The noop provider doesn’t export any traces. It’s the default when no provider is configured.
When to Use
- Testing environments where tracing isn’t needed
- Temporarily disabling tracing without code changes
- Safe default for new projects
Configuration
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithNoop(),
)
defer tracer.Shutdown(context.Background())
Or simply omit the provider option (noop is the default):
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
// No provider = Noop
)
Behavior
- Spans are created but not recorded
- No network calls or file I/O
- Minimal performance overhead
- Safe for production if tracing is disabled
Stdout Provider
The stdout provider prints traces to standard output in a human-readable format.
When to Use
- Local development and debugging
- Troubleshooting span creation and attributes
- Testing trace propagation
- Quick validation of tracing logic
Configuration
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithStdout(),
)
defer tracer.Shutdown(context.Background())
Output Format
Traces are printed as pretty-printed JSON to stdout:
{
"Name": "GET /api/users",
"SpanContext": {
"TraceID": "3f3c5e4d...",
"SpanID": "a1b2c3d4...",
"TraceFlags": "01"
},
"Parent": {
"TraceID": "3f3c5e4d...",
"SpanID": "e5f6g7h8..."
},
"SpanKind": "Server",
"StartTime": "2025-01-18T10:15:30.123Z",
"EndTime": "2025-01-18T10:15:30.456Z",
"Attributes": [
{
"Key": "http.method",
"Value": {"Type": "STRING", "Value": "GET"}
}
]
}
Limitations
- Not for production: Output can be noisy and slow
- No persistence: Traces are only printed, not stored
- No visualization: Use an actual backend for trace visualization
OTLP Provider (gRPC)
The OTLP gRPC provider exports traces to an OpenTelemetry collector using the gRPC protocol. You must call Start(ctx) before traces are exported; otherwise no traces will be sent and no error occurs at creation.
When to Use
- Production environments
- OpenTelemetry collector infrastructure
- Jaeger, Zipkin, or other OTLP-compatible backends
- Best performance and reliability
Basic Configuration
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithServiceVersion("v1.0.0"),
tracing.WithOTLP("localhost:4317"),
)
// Start is required for OTLP providers
if err := tracer.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer tracer.Shutdown(context.Background())
Secure Connection (TLS)
By default, OTLP uses TLS:
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithOTLP("collector.example.com:4317"),
// TLS is enabled by default
)
Insecure Connection (Development)
For local development without TLS:
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithOTLP("localhost:4317", tracing.OTLPInsecure()),
)
Configuration Options
import "rivaas.dev/tracing"
// Secure (production)
tracing.WithOTLP("collector.example.com:4317")
// Insecure (development)
tracing.WithOTLP("localhost:4317", tracing.OTLPInsecure())
OTLP Provider (HTTP)
The OTLP HTTP provider exports traces to an OpenTelemetry collector using the HTTP protocol. You must call Start(ctx) before traces are exported; otherwise no traces will be sent and no error occurs at creation.
When to Use
- Alternative to gRPC when firewalls block gRPC
- Simpler infrastructure without gRPC support
- HTTP-only environments
- Debugging with curl/httpie
Configuration
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithServiceVersion("v1.0.0"),
tracing.WithOTLPHTTP("http://localhost:4318"),
)
// Start is required for OTLP providers
if err := tracer.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer tracer.Shutdown(context.Background())
HTTPS Endpoint
Use HTTPS for secure connections:
tracer := tracing.MustNew(
tracing.WithServiceName("my-service"),
tracing.WithOTLPHTTP("https://collector.example.com:4318"),
)
Endpoint Format
The endpoint should include the protocol:
// HTTP (insecure - development only)
tracing.WithOTLPHTTP("http://localhost:4318")
// HTTPS (secure - production)
tracing.WithOTLPHTTP("https://collector.example.com:4318")
Provider Comparison
Performance
| Provider | Latency | Throughput | CPU | Memory |
|---|---|---|---|---|
| Noop | ~10ns | Unlimited | Minimal | Minimal |
| Stdout | ~100µs | Low | Low | Low |
| OTLP (gRPC) | ~1-2ms | High | Low | Medium |
| OTLP (HTTP) | ~2-3ms | Medium | Low | Medium |
Use Case Matrix
// Development
tracer := tracing.MustNew(
tracing.WithServiceName("my-api"),
tracing.WithStdout(), // ← See traces in console
)
// Testing
tracer := tracing.MustNew(
tracing.WithServiceName("my-api"),
tracing.WithNoop(), // ← No tracing overhead
)
// Production (recommended)
tracer := tracing.MustNew(
tracing.WithServiceName("my-api"),
tracing.WithOTLP("collector:4317"), // ← gRPC to collector
)
// Production (HTTP alternative)
tracer := tracing.MustNew(
tracing.WithServiceName("my-api"),
tracing.WithOTLPHTTP("https://collector:4318"), // ← HTTP to collector
)
Switching Providers
Only one provider can be configured at a time. Attempting to configure multiple providers results in a validation error:
// ✗ Error: multiple providers configured
tracer, err := tracing.New(
tracing.WithServiceName("my-service"),
tracing.WithStdout(),
tracing.WithOTLP("localhost:4317"), // Error!
)
// Returns: "validation errors: provider: multiple providers configured"
To switch providers, use environment variables or configuration:
func createTracer(env string) *tracing.Tracer {
opts := []tracing.Option{
tracing.WithServiceName("my-service"),
tracing.WithServiceVersion("v1.0.0"),
}
switch env {
case "production":
opts = append(opts, tracing.WithOTLP("collector:4317"))
case "development":
opts = append(opts, tracing.WithStdout())
default:
opts = append(opts, tracing.WithNoop())
}
return tracing.MustNew(opts...)
}
OpenTelemetry Collector Setup
For OTLP providers, you need an OpenTelemetry collector.
Docker Compose Example
version: '3.8'
services:
otel-collector:
image: otel/opentelemetry-collector:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "13133:13133" # health_check
Collector Configuration
Basic otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
exporters:
logging:
loglevel: debug
# Add your backend (Jaeger, Zipkin, etc.)
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [logging, jaeger]
Provider Selection Guide
Choose Noop When:
- Tracing is disabled via feature flags
- Running in CI/CD without trace backend
- Performance testing without observability overhead
Choose Stdout When:
- Developing locally and need to see traces
- Debugging span creation and attributes
- Quick validation of tracing setup
Choose OTLP (gRPC) When:
- Deploying to production
- Need high throughput and low latency
- Using OpenTelemetry collector
- Standard production setup
Choose OTLP (HTTP) When:
- gRPC is blocked by firewalls
- Simpler infrastructure requirements
- Need HTTP-friendly debugging
- Backend only supports HTTP
Next Steps
- Learn Configuration options for service metadata and sampling
- Set up Middleware for automatic HTTP tracing
- Explore Examples for production-ready configurations
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.