opentelemetry

package
v2.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2025 License: Apache-2.0 Imports: 28 Imported by: 3

README

OpenTelemetry Package

This package provides OpenTelemetry integration for the LerianStudio commons library, including advanced struct obfuscation capabilities for secure telemetry data.

Features

  • OpenTelemetry Integration: Complete setup and configuration for tracing, metrics, and logging
  • Struct Obfuscation: Advanced field obfuscation for sensitive data in telemetry spans
  • Flexible Configuration: Support for custom obfuscation rules and business logic
  • Backward Compatibility: Maintains existing API while adding new security features

Quick Start

Basic Usage (Without Obfuscation)
import (
    "github.com/LerianStudio/lib-commons/v2/commons/opentelemetry"
    "go.opentelemetry.io/otel"
)

// Create a span and add struct data
tracer := otel.Tracer("my-service")
_, span := tracer.Start(ctx, "operation")
defer span.End()

// Add struct data to span (original behavior)
err := opentelemetry.SetSpanAttributesFromStruct(&span, "user_data", userStruct)
With Default Obfuscation
// Create default obfuscator (covers common sensitive fields)
obfuscator := opentelemetry.NewDefaultObfuscator()

// Add obfuscated struct data to span
err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "user_data", userStruct, obfuscator)
With Custom Obfuscation
// Define custom sensitive fields
customFields := []string{"email", "phone", "address"}
customObfuscator := opentelemetry.NewCustomObfuscator(customFields)

// Apply custom obfuscation
err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "user_data", userStruct, customObfuscator)

Struct Obfuscation Examples

Example Data Structure
type UserLoginRequest struct {
    Username    string          `json:"username"`
    Password    string          `json:"password"`
    Email       string          `json:"email"`
    RememberMe  bool            `json:"rememberMe"`
    DeviceInfo  DeviceInfo      `json:"deviceInfo"`
    Credentials AuthCredentials `json:"credentials"`
    Metadata    map[string]any  `json:"metadata"`
}

type DeviceInfo struct {
    UserAgent    string `json:"userAgent"`
    IPAddress    string `json:"ipAddress"`
    DeviceID     string `json:"deviceId"`
    SessionToken string `json:"token"` // Will be obfuscated
}

type AuthCredentials struct {
    APIKey       string `json:"apikey"`        // Will be obfuscated
    RefreshToken string `json:"refresh_token"` // Will be obfuscated
    ClientSecret string `json:"secret"`        // Will be obfuscated
}
Example 1: Default Obfuscation
loginRequest := UserLoginRequest{
    Username:   "john.doe",
    Password:   "super_secret_password_123",
    Email:      "john.doe@example.com",
    RememberMe: true,
    DeviceInfo: DeviceInfo{
        UserAgent:    "Mozilla/5.0...",
        IPAddress:    "192.168.1.100",
        DeviceID:     "device_12345",
        SessionToken: "session_token_abc123xyz",
    },
    Credentials: AuthCredentials{
        APIKey:       "api_key_secret_789",
        RefreshToken: "refresh_token_xyz456",
        ClientSecret: "client_secret_ultra_secure",
    },
    Metadata: map[string]any{
        "theme":       "dark",
        "language":    "en-US",
        "private_key": "private_key_should_be_hidden",
        "public_info": "this is safe to show",
    },
}

// Apply default obfuscation
defaultObfuscator := opentelemetry.NewDefaultObfuscator()
err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "login_request", loginRequest, defaultObfuscator)

// Result: password, token, secret, apikey, private_key fields become "***"
Example 2: Custom Field Selection
// Only obfuscate specific fields
customFields := []string{"username", "email", "deviceId", "ipAddress"}
customObfuscator := opentelemetry.NewCustomObfuscator(customFields)

err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "login_request", loginRequest, customObfuscator)

// Result: Only username, email, deviceId, ipAddress become "***"
Example 3: Custom Business Logic Obfuscator
// Implement custom obfuscation logic
type BusinessLogicObfuscator struct {
    companyPolicy map[string]bool
}

func (b *BusinessLogicObfuscator) ShouldObfuscate(fieldName string) bool {
    return b.companyPolicy[strings.ToLower(fieldName)]
}

func (b *BusinessLogicObfuscator) GetObfuscatedValue() string {
    return "[COMPANY_POLICY_REDACTED]"
}

// Use custom obfuscator
businessObfuscator := &BusinessLogicObfuscator{
    companyPolicy: map[string]bool{
        "email":      true,
        "ipaddress":  true,
        "deviceinfo": true, // Obfuscates entire nested object
    },
}

err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "login_request", loginRequest, businessObfuscator)
Example 4: Standalone Obfuscation Utility
// Use obfuscation without OpenTelemetry spans
obfuscator := opentelemetry.NewDefaultObfuscator()
obfuscatedData, err := opentelemetry.ObfuscateStruct(loginRequest, obfuscator)
if err != nil {
    log.Printf("Obfuscation failed: %v", err)
}

// obfuscatedData now contains the struct with sensitive fields replaced

Default Sensitive Fields

The NewDefaultObfuscator() uses a shared list of sensitive field names from the commons/security package, ensuring consistent obfuscation behavior across HTTP logging, OpenTelemetry spans, and other components.

The following common sensitive field names are automatically obfuscated (case-insensitive):

Authentication & Security
  • password
  • token
  • secret
  • key
  • authorization
  • auth
  • credential
  • credentials
  • apikey
  • api_key
  • access_token
  • refresh_token
  • private_key
  • privatekey

Note: This list is shared with HTTP logging middleware and other components via security.DefaultSensitiveFields to ensure consistent behavior across the entire commons library.

API Reference

Core Functions
SetSpanAttributesFromStruct(span *trace.Span, key string, valueStruct any) error

Original function for backward compatibility. Adds struct data to span without obfuscation.

SetSpanAttributesFromStructWithObfuscation(span *trace.Span, key string, valueStruct any, obfuscator FieldObfuscator) error

Enhanced function that applies obfuscation before adding struct data to span. If obfuscator is nil, behaves like the original function.

ObfuscateStruct(valueStruct any, obfuscator FieldObfuscator) (any, error)

Standalone utility function that obfuscates a struct and returns the result. Can be used independently of OpenTelemetry spans.

Obfuscator Constructors
NewDefaultObfuscator() *DefaultObfuscator

Creates an obfuscator with predefined common sensitive field names.

NewCustomObfuscator(sensitiveFields []string) *DefaultObfuscator

Creates an obfuscator with custom sensitive field names. Field matching is case-insensitive.

Interface
FieldObfuscator
type FieldObfuscator interface {
    // ShouldObfuscate returns true if the given field name should be obfuscated
    ShouldObfuscate(fieldName string) bool
    // GetObfuscatedValue returns the value to use for obfuscated fields
    GetObfuscatedValue() string
}
Constants
ObfuscatedValue = "***"

Default value used to replace sensitive fields. Can be referenced for consistency.

Advanced Features

Recursive Obfuscation

The obfuscation system works recursively on:

  • Nested structs: Processes all nested object fields
  • Arrays and slices: Processes each element in collections
  • Maps: Processes all key-value pairs
Case-Insensitive Matching

Field name matching is case-insensitive for flexibility:

// All these variations will be obfuscated if "password" is in the sensitive list
"password", "Password", "PASSWORD", "PaSsWoRd"
Performance Considerations
  • Efficient processing: Uses pre-allocated maps for field lookups
  • Memory conscious: Minimal allocations during recursive processing
  • JSON conversion: Leverages Go's efficient JSON marshaling/unmarshaling

Best Practices

Security
  • Always use obfuscation for production telemetry data
  • Review sensitive field lists regularly to ensure comprehensive coverage
  • Implement custom obfuscators for business-specific sensitive data
  • Test obfuscation rules to verify sensitive data is properly hidden
Performance
  • Reuse obfuscator instances instead of creating new ones for each call
  • Use appropriate obfuscation level - don't over-obfuscate if not needed
  • Consider caching obfuscated results for frequently used structs
Maintainability
  • Use NewDefaultObfuscator() for most common use cases
  • Document custom obfuscation rules in your business logic
  • Centralize obfuscation policies for consistency across services
  • Test obfuscation behavior in your unit tests
Migration
  • Backward compatibility: Existing code using SetSpanAttributesFromStruct() continues to work
  • Gradual adoption: Add obfuscation incrementally to existing telemetry code
  • Monitoring: Verify obfuscated telemetry data meets security requirements

Error Handling

The obfuscation functions return errors in these cases:

  • Invalid JSON: When the input struct cannot be marshaled to JSON
  • Malformed data: When JSON unmarshaling fails during processing
err := opentelemetry.SetSpanAttributesFromStructWithObfuscation(
    &span, "data", invalidStruct, obfuscator)
if err != nil {
    log.Printf("Obfuscation failed: %v", err)
    // Handle error appropriately
}

Testing

The package includes comprehensive tests covering:

  • Default obfuscator behavior
  • Custom obfuscator functionality
  • Recursive obfuscation of nested structures
  • Error handling for invalid data
  • Integration with OpenTelemetry spans
  • Custom obfuscator interface implementations

Run tests with:

go test ./commons/opentelemetry -v

Examples

For complete working examples, see:

  • obfuscation_test.go - Comprehensive test cases
  • examples/opentelemetry_obfuscation_example.go - Runnable example application

Contributing

When adding new features:

  1. Follow the interface pattern for extensibility
  2. Add comprehensive tests for new functionality
  3. Update documentation with examples
  4. Maintain backward compatibility with existing APIs
  5. Follow Go best practices and the project's coding standards

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractGRPCContext

func ExtractGRPCContext(ctx context.Context) context.Context

ExtractGRPCContext extracts OpenTelemetry trace context from incoming gRPC metadata and injects it into the context. It handles case normalization for W3C trace headers.

func ExtractHTTPContext

func ExtractHTTPContext(c *fiber.Ctx) context.Context

ExtractHTTPContext extracts OpenTelemetry trace context from incoming HTTP headers and injects it into the context. It works with Fiber's HTTP context.

func ExtractQueueTraceContext

func ExtractQueueTraceContext(ctx context.Context, headers map[string]string) context.Context

ExtractQueueTraceContext extracts OpenTelemetry trace context from RabbitMQ headers and returns a new context with the extracted trace information. This enables distributed tracing continuity across queue message boundaries.

func ExtractTraceContextFromQueueHeaders

func ExtractTraceContextFromQueueHeaders(baseCtx context.Context, amqpHeaders map[string]any) context.Context

ExtractTraceContextFromQueueHeaders extracts OpenTelemetry trace context from RabbitMQ amqp.Table headers and returns a new context with the extracted trace information. Handles type conversion automatically.

func GetTraceIDFromContext

func GetTraceIDFromContext(ctx context.Context) string

GetTraceIDFromContext extracts the trace ID from the current span context Returns empty string if no active span or trace ID is found

func GetTraceStateFromContext

func GetTraceStateFromContext(ctx context.Context) string

GetTraceStateFromContext extracts the trace state from the current span context Returns empty string if no active span or trace state is found

func HandleSpanBusinessErrorEvent added in v2.3.0

func HandleSpanBusinessErrorEvent(span *trace.Span, eventName string, err error)

HandleSpanBusinessErrorEvent adds a business error event to the span.

func HandleSpanError

func HandleSpanError(span *trace.Span, message string, err error)

HandleSpanError sets the status of the span to error and records the error.

func HandleSpanEvent added in v2.3.0

func HandleSpanEvent(span *trace.Span, eventName string, attributes ...attribute.KeyValue)

HandleSpanEvent adds an event to the span.

func InjectGRPCContext

func InjectGRPCContext(ctx context.Context) context.Context

InjectGRPCContext injects OpenTelemetry trace context into outgoing gRPC metadata. It normalizes W3C trace headers to lowercase for gRPC compatibility.

func InjectHTTPContext

func InjectHTTPContext(headers *http.Header, ctx context.Context)

InjectHTTPContext modifies HTTP headers for trace propagation in outgoing client requests

func InjectQueueTraceContext

func InjectQueueTraceContext(ctx context.Context) map[string]string

InjectQueueTraceContext injects OpenTelemetry trace context into RabbitMQ headers for distributed tracing across queue messages. Returns a map of headers to be added to the RabbitMQ message headers.

func InjectTraceHeadersIntoQueue

func InjectTraceHeadersIntoQueue(ctx context.Context, headers *map[string]any)

InjectTraceHeadersIntoQueue adds OpenTelemetry trace headers to existing RabbitMQ headers following W3C trace context standards. Modifies the headers map in place.

func ObfuscateStruct

func ObfuscateStruct(valueStruct any, obfuscator FieldObfuscator) (any, error)

ObfuscateStruct applies obfuscation to a struct and returns the obfuscated data. This is a utility function that can be used independently of OpenTelemetry spans.

func PrepareQueueHeaders

func PrepareQueueHeaders(ctx context.Context, baseHeaders map[string]any) map[string]any

PrepareQueueHeaders prepares RabbitMQ headers with trace context injection following W3C trace context standards. Returns a map suitable for amqp.Table.

func SetSpanAttributeForParam added in v2.3.0

func SetSpanAttributeForParam(c *fiber.Ctx, param, value, entityName string)

SetSpanAttributeForParam sets a span attribute for a Fiber request parameter with consistent naming entityName is a snake_case string used to identify id name, for example the "organization" entity name will result in "app.request.organization_id" otherwise the path parameter "id" in a Fiber request for example "/v1/organizations/:id" will be parsed as "app.request.id"

func SetSpanAttributesFromStruct added in v2.1.0

func SetSpanAttributesFromStruct(span *trace.Span, key string, valueStruct any) error

SetSpanAttributesFromStruct converts a struct to a JSON string and sets it as an attribute on the span.

func SetSpanAttributesFromStructWithCustomObfuscation deprecated

func SetSpanAttributesFromStructWithCustomObfuscation(span *trace.Span, key string, valueStruct any, obfuscator FieldObfuscator) error

Deprecated: Use SetSpanAttributesFromStruct instead.

SetSpanAttributesFromStructWithCustomObfuscation converts a struct to a JSON string, obfuscates sensitive fields using the custom obfuscator provided, and sets it as an attribute on the span.

func SetSpanAttributesFromStructWithObfuscation deprecated

func SetSpanAttributesFromStructWithObfuscation(span *trace.Span, key string, valueStruct any) error

Deprecated: Use SetSpanAttributesFromStruct instead.

SetSpanAttributesFromStructWithObfuscation converts a struct to a JSON string, obfuscates sensitive fields using the default obfuscator, and sets it as an attribute on the span.

Types

type AttrBagSpanProcessor added in v2.3.0

type AttrBagSpanProcessor struct{}

AttrBagSpanProcessor copies request-scoped attributes from context into every span at start.

func (AttrBagSpanProcessor) ForceFlush added in v2.3.0

func (AttrBagSpanProcessor) ForceFlush(ctx context.Context) error

func (AttrBagSpanProcessor) OnEnd added in v2.3.0

func (AttrBagSpanProcessor) OnStart added in v2.3.0

func (AttrBagSpanProcessor) Shutdown added in v2.3.0

func (AttrBagSpanProcessor) Shutdown(ctx context.Context) error

type DefaultObfuscator

type DefaultObfuscator struct {
	// contains filtered or unexported fields
}

DefaultObfuscator provides a simple implementation that obfuscates common sensitive field names using a predefined list.

func NewCustomObfuscator

func NewCustomObfuscator(sensitiveFields []string) *DefaultObfuscator

NewCustomObfuscator creates a new DefaultObfuscator with custom sensitive field names.

func NewDefaultObfuscator

func NewDefaultObfuscator() *DefaultObfuscator

NewDefaultObfuscator creates a new DefaultObfuscator with common sensitive field names. Uses the shared sensitive fields list from the security package to ensure consistency across HTTP logging, OpenTelemetry spans, and other components.

func (*DefaultObfuscator) GetObfuscatedValue

func (o *DefaultObfuscator) GetObfuscatedValue() string

GetObfuscatedValue returns the obfuscated value.

func (*DefaultObfuscator) ShouldObfuscate

func (o *DefaultObfuscator) ShouldObfuscate(fieldName string) bool

ShouldObfuscate returns true if the field name is in the sensitive fields list.

type FieldObfuscator

type FieldObfuscator interface {
	// ShouldObfuscate returns true if the given field name should be obfuscated
	ShouldObfuscate(fieldName string) bool
	// GetObfuscatedValue returns the value to use for obfuscated fields
	GetObfuscatedValue() string
}

FieldObfuscator defines the interface for obfuscating sensitive fields in structs. Implementations can provide custom logic for determining which fields to obfuscate and how to obfuscate them.

type Telemetry

type Telemetry struct {
	TelemetryConfig
	TracerProvider *sdktrace.TracerProvider
	MetricProvider *sdkmetric.MeterProvider
	LoggerProvider *sdklog.LoggerProvider
	MetricsFactory *metrics.MetricsFactory
	// contains filtered or unexported fields
}

func InitializeTelemetry added in v2.3.0

func InitializeTelemetry(cfg *TelemetryConfig) *Telemetry

InitializeTelemetry initializes the telemetry providers and sets them globally. (Logger is being passed as a parameter because it not exists in the global context at this point to be injected)

func (*Telemetry) EndTracingSpans

func (tl *Telemetry) EndTracingSpans(ctx context.Context)

func (*Telemetry) ShutdownTelemetry

func (tl *Telemetry) ShutdownTelemetry()

ShutdownTelemetry shuts down the telemetry providers and exporters.

type TelemetryConfig added in v2.3.0

type TelemetryConfig struct {
	LibraryName               string
	ServiceName               string
	ServiceVersion            string
	DeploymentEnv             string
	CollectorExporterEndpoint string
	EnableTelemetry           bool
	Logger                    log.Logger
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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