otel

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2025 License: MIT Imports: 10 Imported by: 0

README

OpenTelemetry Integration

Go Reference

Unified OpenTelemetry v2 configuration and instrumentation utilities for the pkg library ecosystem.

Overview

The otel package provides centralized OpenTelemetry configuration that enables observability across all library packages. It supports the three pillars of observability:

  • Traces - Distributed tracing for request flows
  • Metrics - Performance and health measurements
  • Logs - Structured logging via OpenTelemetry standard

Features

  • Unified Configuration: Single config object for all telemetry pillars
  • Selective Enablement: Enable only the telemetry you need
  • No-op by Default: Zero overhead when providers are not configured
  • Method Chaining: Fluent API for configuration
  • Semantic Conventions: Library-specific attributes for consistent instrumentation
  • Graceful Shutdown: Proper resource cleanup

Installation

go get github.com/jasoet/pkg/v2/otel

Quick Start

Basic Configuration
package main

import (
    "context"
    "github.com/jasoet/pkg/v2/otel"
    "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/sdk/metric"
)

func main() {
    // Create tracer and meter providers (your setup)
    tracerProvider := trace.NewTracerProvider(/* ... */)
    meterProvider := metric.NewMeterProvider(/* ... */)

    // Create unified OTel config
    otelConfig := otel.NewConfig("my-service").
        WithTracerProvider(tracerProvider).
        WithMeterProvider(meterProvider).
        WithServiceVersion("1.0.0")

    // Use with library packages
    // server.Start(server.Config{OTelConfig: otelConfig, ...})
    // db.Pool(db.Config{OTelConfig: otelConfig, ...})

    // Cleanup on shutdown
    defer otelConfig.Shutdown(context.Background())
}
Selective Telemetry

Enable only what you need:

// Tracing only
cfg := otel.NewConfig("my-service").
    WithTracerProvider(tracerProvider).
    WithoutLogging()  // Disable default logging

// Metrics only
cfg := otel.NewConfig("my-service").
    WithMeterProvider(meterProvider).
    WithoutLogging()

// All three pillars
cfg := otel.NewConfig("my-service").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider).
    WithLoggerProvider(loggerProvider)
Custom Logger Provider

Use the logging package for better formatting and automatic trace correlation:

import (
    "github.com/jasoet/pkg/v2/logging"
    "github.com/jasoet/pkg/v2/otel"
)

// Production-ready logger with trace correlation
loggerProvider := logging.NewLoggerProvider("my-service", false)

cfg := otel.NewConfig("my-service").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider).
    WithLoggerProvider(loggerProvider)

Configuration API

Config Struct
type Config struct {
    TracerProvider trace.TracerProvider  // nil = no tracing
    MeterProvider  metric.MeterProvider  // nil = no metrics
    LoggerProvider log.LoggerProvider    // nil = no OTel logs
    ServiceName    string
    ServiceVersion string
}
Builder Methods
Method Description
NewConfig(name) Create config with service name and default logger
WithTracerProvider(tp) Enable distributed tracing
WithMeterProvider(mp) Enable metrics collection
WithLoggerProvider(lp) Set custom logger provider
WithServiceVersion(v) Set service version
WithoutLogging() Disable default stdout logging
Helper Methods
// Check what's enabled
cfg.IsTracingEnabled()  // bool
cfg.IsMetricsEnabled()  // bool
cfg.IsLoggingEnabled()  // bool

// Get instrumentation components
tracer := cfg.GetTracer("scope-name")   // Returns no-op if disabled
meter := cfg.GetMeter("scope-name")     // Returns no-op if disabled
logger := cfg.GetLogger("scope-name")   // Returns no-op if disabled

// Cleanup
cfg.Shutdown(context.Background())

Semantic Conventions

Library-specific attributes for consistent instrumentation:

Server Package
otel.AttrServerPort         // "pkg.server.port"
gRPC Package
otel.AttrGRPCMode           // "pkg.grpc.mode"
otel.AttrGRPCPort           // "pkg.grpc.port"
otel.AttrGRPCHTTPPort       // "pkg.grpc.http_port"
otel.AttrGRPCReflection     // "pkg.grpc.reflection_enabled"
otel.AttrGRPCGatewayEnabled // "pkg.grpc.gateway_enabled"

// Values
otel.GRPCModeSeparate       // "separate"
otel.GRPCModeH2C            // "h2c"
REST Client Package
otel.AttrRESTClientName     // "pkg.rest.client.name"
otel.AttrRESTRetryCount     // "pkg.rest.retry.max_count"
otel.AttrRESTRetryAttempt   // "pkg.rest.retry.attempt"
otel.AttrRESTTimeout        // "pkg.rest.timeout_ms"
Database Package
otel.AttrDBConnectionPool   // "pkg.db.pool.name"
otel.AttrDBType            // "pkg.db.type"
otel.AttrDBMaxIdleConns    // "pkg.db.pool.max_idle"
otel.AttrDBMaxOpenConns    // "pkg.db.pool.max_open"

// Values
otel.DBTypePostgreSQL      // "postgresql"
otel.DBTypeMySQL           // "mysql"
otel.DBTypeMSSQL           // "mssql"
Concurrent Package
otel.AttrConcurrentTaskCount   // "pkg.concurrent.task.count"
otel.AttrConcurrentTaskSuccess // "pkg.concurrent.task.success"
otel.AttrConcurrentTaskFailed  // "pkg.concurrent.task.failed"
otel.AttrConcurrentMaxWorkers  // "pkg.concurrent.max_workers"

Integration Examples

HTTP Server
import (
    "github.com/jasoet/pkg/v2/otel"
    "github.com/jasoet/pkg/v2/server"
)

otelConfig := otel.NewConfig("my-api").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider)

server.Start(server.Config{
    Port:       8080,
    OTelConfig: otelConfig,
})
gRPC Server
import (
    "github.com/jasoet/pkg/v2/otel"
    "github.com/jasoet/pkg/v2/grpc"
)

otelConfig := otel.NewConfig("my-grpc-service").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider)

grpcServer := grpc.NewServer(
    grpc.NewConfig("my-service", 9090).
        WithOTelConfig(otelConfig),
)
Database
import (
    "github.com/jasoet/pkg/v2/otel"
    "github.com/jasoet/pkg/v2/db"
)

otelConfig := otel.NewConfig("my-db-service").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider)

pool, _ := db.ConnectionConfig{
    DbType:     db.Postgresql,
    Host:       "localhost",
    OTelConfig: otelConfig,
}.Pool()

// All queries are automatically traced
pool.Find(&users)
REST Client
import (
    "github.com/jasoet/pkg/v2/otel"
    "github.com/jasoet/pkg/v2/rest"
)

otelConfig := otel.NewConfig("my-client").
    WithTracerProvider(tracerProvider).
    WithMeterProvider(meterProvider)

client := rest.NewClient(rest.ClientConfig{
    BaseURL:    "https://api.example.com",
    OTelConfig: otelConfig,
})

// Requests are automatically traced
client.Get("/users", &result)

Complete Example

See the fullstack OTel example for a complete application demonstrating all three telemetry pillars across multiple packages.

Testing

The package includes comprehensive tests with 97.1% coverage:

# Run tests
go test ./otel -v

# With coverage
go test ./otel -cover
Test Utilities

Use no-op providers for testing:

import (
    "github.com/jasoet/pkg/v2/otel"
    noopm "go.opentelemetry.io/otel/metric/noop"
    noopt "go.opentelemetry.io/otel/trace/noop"
)

func TestMyCode(t *testing.T) {
    cfg := otel.NewConfig("test-service").
        WithTracerProvider(noopt.NewTracerProvider()).
        WithMeterProvider(noopm.NewMeterProvider()).
        WithoutLogging()

    // Test your code with cfg
}

Best Practices

1. Create Once, Share Everywhere
// ✅ Good: Single config shared across packages
otelConfig := otel.NewConfig("my-service").
    WithTracerProvider(tp).
    WithMeterProvider(mp)

serverCfg := server.Config{OTelConfig: otelConfig}
dbCfg := db.Config{OTelConfig: otelConfig}
2. Always Shutdown
// ✅ Good: Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := otelConfig.Shutdown(ctx); err != nil {
    log.Printf("OTel shutdown error: %v", err)
}
3. Use Semantic Conventions
// ✅ Good: Use defined attributes
import "go.opentelemetry.io/otel/attribute"

span.SetAttributes(
    otel.AttrServerPort.Int(8080),
    otel.AttrGRPCMode.String(otel.GRPCModeH2C),
)
4. Check Before Using
// ✅ Good: Check enablement
if cfg.IsTracingEnabled() {
    tracer := cfg.GetTracer("my-scope")
    // Use tracer
}
5. Production Logger Provider
// ✅ Good: Use logging package for production
import "github.com/jasoet/pkg/v2/logging"

cfg := otel.NewConfig("my-service").
    WithLoggerProvider(logging.NewLoggerProvider("my-service", false))

Architecture

Design Principles
  1. Zero Dependencies: Only depends on OTel SDK (no custom exporters)
  2. No-op Safety: Nil providers result in no-op implementations
  3. Lazy Initialization: Providers created only when needed
  4. Immutable Config: Thread-safe after creation
Package Structure
otel/
├── config.go       # Config struct and builder methods
├── attributes.go   # Semantic conventions
├── doc.go         # Package documentation
└── config_test.go # Comprehensive tests (97.1% coverage)

Troubleshooting

No Telemetry Data

Problem: Not seeing traces/metrics/logs

Solutions:

// 1. Check if enabled
fmt.Println("Tracing:", cfg.IsTracingEnabled())
fmt.Println("Metrics:", cfg.IsMetricsEnabled())
fmt.Println("Logging:", cfg.IsLoggingEnabled())

// 2. Verify providers are set
if cfg.TracerProvider == nil {
    // Tracing will be no-op
}

// 3. Ensure shutdown is called
defer cfg.Shutdown(context.Background())
Default Logger Too Verbose

Problem: Stdout logger creating too much output

Solution:

// Disable default logger
cfg := otel.NewConfig("my-service").WithoutLogging()

// Or use custom logger
cfg := otel.NewConfig("my-service").
    WithLoggerProvider(myLoggerProvider)
Provider Already Registered

Problem: Global provider conflicts

Solution: This package doesn't use global providers - it returns scoped instruments from GetTracer(), GetMeter(), and GetLogger().

Version Compatibility

  • OpenTelemetry: v1.38.0+
  • Go: 1.25+
  • pkg library: v2.0.0+

Migration from v1

v2 uses OpenTelemetry v2 API:

// v1 (OTel v1)
import "go.opentelemetry.io/otel"
tracer := otel.Tracer("my-scope")

// v2 (OTel v2)
import "github.com/jasoet/pkg/v2/otel"
cfg := otel.NewConfig("my-service").WithTracerProvider(tp)
tracer := cfg.GetTracer("my-scope")

See VERSIONING_GUIDE.md for complete migration guide.

  • logging - Structured logging with OTel integration
  • server - HTTP server with automatic tracing
  • grpc - gRPC server with automatic instrumentation
  • db - Database with query tracing
  • rest - REST client with distributed tracing

License

MIT License - see LICENSE for details.

Documentation

Overview

Package otel provides OpenTelemetry instrumentation utilities for github.com/jasoet/pkg/v2.

This package offers:

  • Centralized configuration for traces, metrics, and logs
  • Library-specific semantic conventions
  • No-op implementations when telemetry is disabled

Configuration

Create an otel.Config with the desired providers:

cfg := &otel.Config{
    TracerProvider: tracerProvider,  // optional
    MeterProvider:  meterProvider,   // optional
    LoggerProvider: loggerProvider,  // optional
    ServiceName:    "my-service",
    ServiceVersion: "1.0.0",
}

Then pass this config to package configurations (server.Config, grpc options, etc.).

Telemetry Pillars

Enable any combination of:

  • Traces (distributed tracing)
  • Metrics (measurements and aggregations)
  • Logs (structured log export via OpenTelemetry standard)

Each pillar is independently controlled by setting its provider. Nil providers result in no-op implementations with zero overhead.

Semantic Conventions

This package defines library-specific attributes (AttrServerPort, AttrGRPCMode, etc.) that complement standard OpenTelemetry semantic conventions.

Index

Constants

View Source
const (
	// Package identification
	AttrPackageName    = attribute.Key("pkg.name")
	AttrPackageVersion = attribute.Key("pkg.version")

	// Server package attributes
	AttrServerPort = attribute.Key("pkg.server.port")

	// gRPC package attributes
	AttrGRPCMode           = attribute.Key("pkg.grpc.mode")
	AttrGRPCPort           = attribute.Key("pkg.grpc.port")
	AttrGRPCHTTPPort       = attribute.Key("pkg.grpc.http_port")
	AttrGRPCReflection     = attribute.Key("pkg.grpc.reflection_enabled")
	AttrGRPCGatewayEnabled = attribute.Key("pkg.grpc.gateway_enabled")

	// REST client package attributes
	AttrRESTClientName   = attribute.Key("pkg.rest.client.name")
	AttrRESTRetryCount   = attribute.Key("pkg.rest.retry.max_count")
	AttrRESTRetryAttempt = attribute.Key("pkg.rest.retry.attempt")
	AttrRESTTimeout      = attribute.Key("pkg.rest.timeout_ms")

	// Database package attributes
	AttrDBConnectionPool = attribute.Key("pkg.db.pool.name")
	AttrDBType           = attribute.Key("pkg.db.type")
	AttrDBMaxIdleConns   = attribute.Key("pkg.db.pool.max_idle")
	AttrDBMaxOpenConns   = attribute.Key("pkg.db.pool.max_open")

	// Concurrent package attributes
	AttrConcurrentTaskCount   = attribute.Key("pkg.concurrent.task.count")
	AttrConcurrentTaskSuccess = attribute.Key("pkg.concurrent.task.success")
	AttrConcurrentTaskFailed  = attribute.Key("pkg.concurrent.task.failed")
	AttrConcurrentMaxWorkers  = attribute.Key("pkg.concurrent.max_workers")
)

Semantic conventions specific to github.com/jasoet/pkg/v2 library. These complement standard OpenTelemetry semantic conventions.

View Source
const (
	// gRPC modes
	GRPCModeSeparate = "separate"
	GRPCModeH2C      = "h2c"

	// Database types
	DBTypePostgreSQL = "postgresql"
	DBTypeMySQL      = "mysql"
	DBTypeMSSQL      = "mssql"
)

Common attribute values

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// TracerProvider for distributed tracing
	// If nil, tracing will be disabled (no-op tracer)
	TracerProvider trace.TracerProvider

	// MeterProvider for metrics collection
	// If nil, metrics will be disabled (no-op meter)
	MeterProvider metric.MeterProvider

	// LoggerProvider for structured logging via OTel
	// Defaults to stdout exporter when using NewConfig()
	// Set to nil explicitly to disable logging
	LoggerProvider log.LoggerProvider

	// ServiceName identifies the service in telemetry data
	ServiceName string

	// ServiceVersion identifies the service version
	ServiceVersion string
}

Config holds OpenTelemetry configuration for instrumentation. TracerProvider and MeterProvider are optional - nil values result in no-op implementations. LoggerProvider defaults to stdout exporter when using NewConfig().

func NewConfig

func NewConfig(serviceName string) *Config

NewConfig creates a new OpenTelemetry configuration with default LoggerProvider. The default LoggerProvider uses stdout exporter for easy debugging. Use With* methods to add TracerProvider and MeterProvider.

For production use with better formatting and automatic log-span correlation, use logging.NewLoggerProvider() instead:

import "github.com/jasoet/pkg/v2/logging"
cfg := &otel.Config{
    ServiceName:    "my-service",
    LoggerProvider: logging.NewLoggerProvider("my-service", false),
}
cfg.WithTracerProvider(tp).WithMeterProvider(mp)

Example with default stdout logger:

cfg := otel.NewConfig("my-service").
    WithTracerProvider(tp).
    WithMeterProvider(mp)

func (*Config) GetLogger

func (c *Config) GetLogger(scopeName string, opts ...log.LoggerOption) log.Logger

GetLogger returns a logger for the given instrumentation scope. Returns a no-op logger if logging is not configured.

func (*Config) GetMeter

func (c *Config) GetMeter(scopeName string, opts ...metric.MeterOption) metric.Meter

GetMeter returns a meter for the given instrumentation scope. Returns a no-op meter if metrics are not configured.

func (*Config) GetTracer

func (c *Config) GetTracer(scopeName string, opts ...trace.TracerOption) trace.Tracer

GetTracer returns a tracer for the given instrumentation scope. Returns a no-op tracer if tracing is not configured.

func (*Config) IsLoggingEnabled

func (c *Config) IsLoggingEnabled() bool

IsLoggingEnabled returns true if OTel logging is configured

func (*Config) IsMetricsEnabled

func (c *Config) IsMetricsEnabled() bool

IsMetricsEnabled returns true if metrics collection is configured

func (*Config) IsTracingEnabled

func (c *Config) IsTracingEnabled() bool

IsTracingEnabled returns true if tracing is configured

func (*Config) Shutdown

func (c *Config) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down all configured providers Call this when your application exits to flush any pending telemetry

func (*Config) WithLoggerProvider

func (c *Config) WithLoggerProvider(lp log.LoggerProvider) *Config

WithLoggerProvider sets a custom LoggerProvider, replacing the default stdout logger

func (*Config) WithMeterProvider

func (c *Config) WithMeterProvider(mp metric.MeterProvider) *Config

WithMeterProvider sets the MeterProvider for metrics collection

func (*Config) WithServiceVersion

func (c *Config) WithServiceVersion(version string) *Config

WithServiceVersion sets the service version for telemetry data

func (*Config) WithTracerProvider

func (c *Config) WithTracerProvider(tp trace.TracerProvider) *Config

WithTracerProvider sets the TracerProvider for distributed tracing

func (*Config) WithoutLogging

func (c *Config) WithoutLogging() *Config

WithoutLogging disables the default logging by setting LoggerProvider to nil

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL