A community based topic aggregation platform built on atproto
1// Package observability provides OpenTelemetry tracing configuration and middleware for Coves.
2package observability
3
4import (
5 "errors"
6 "fmt"
7 "log/slog"
8 "os"
9 "strconv"
10)
11
12// Config validation errors
13var (
14 // ErrInvalidSampleRatio is returned when SampleRatio is outside the valid range [0.0, 1.0]
15 ErrInvalidSampleRatio = errors.New("SampleRatio must be between 0.0 and 1.0")
16 // ErrMissingEndpoint is returned when Endpoint is empty while tracing is enabled
17 ErrMissingEndpoint = errors.New("Endpoint is required when observability is enabled")
18)
19
20// Config holds the configuration for OpenTelemetry tracing.
21type Config struct {
22 // Enabled determines whether OpenTelemetry tracing is active.
23 Enabled bool
24
25 // ServiceName is the name of this service in traces.
26 ServiceName string
27
28 // Endpoint is the OTLP collector endpoint (e.g., "http://localhost:4317").
29 Endpoint string
30
31 // Headers contains optional headers for the OTLP exporter (key=value,key2=value2 format).
32 Headers string
33
34 // SampleRatio is the trace sampling ratio between 0.0 and 1.0.
35 // 1.0 means all traces are sampled, 0.5 means 50% are sampled.
36 SampleRatio float64
37
38 // Insecure determines whether to use an insecure gRPC connection to the collector.
39 Insecure bool
40}
41
42// DefaultConfig returns a Config with sensible default values.
43// By default, tracing is disabled to avoid unexpected overhead.
44func DefaultConfig() Config {
45 return Config{
46 Enabled: false,
47 ServiceName: "coves-appview",
48 Endpoint: "http://localhost:4317",
49 Headers: "",
50 SampleRatio: 1.0,
51 Insecure: false,
52 }
53}
54
55// ConfigFromEnv creates a Config from environment variables.
56// Uses defaults for any missing environment variables.
57//
58// Environment variables:
59// - OTEL_ENABLED: "true"/"1" to enable, "false"/"0" to disable (default: false)
60// - OTEL_SERVICE_NAME: service name for traces (default: "coves-appview")
61// - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (default: "http://localhost:4317")
62// - OTEL_EXPORTER_OTLP_HEADERS: headers for OTLP exporter in key=value,key2=value2 format (default: "")
63// - OTEL_TRACES_SAMPLER_ARG: sampling ratio 0.0-1.0 (default: 1.0)
64// - OTEL_EXPORTER_OTLP_INSECURE: "true"/"1" for insecure connection (default: false)
65func ConfigFromEnv() Config {
66 cfg := DefaultConfig()
67
68 if v := os.Getenv("OTEL_ENABLED"); v != "" {
69 cfg.Enabled = v == "true" || v == "1"
70 }
71
72 if v := os.Getenv("OTEL_SERVICE_NAME"); v != "" {
73 cfg.ServiceName = v
74 }
75
76 if v := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"); v != "" {
77 cfg.Endpoint = v
78 }
79
80 if v := os.Getenv("OTEL_EXPORTER_OTLP_HEADERS"); v != "" {
81 cfg.Headers = v
82 }
83
84 if v := os.Getenv("OTEL_TRACES_SAMPLER_ARG"); v != "" {
85 if ratio, err := strconv.ParseFloat(v, 64); err == nil && ratio >= 0.0 && ratio <= 1.0 {
86 cfg.SampleRatio = ratio
87 } else {
88 slog.Warn("[OTEL] invalid OTEL_TRACES_SAMPLER_ARG value, using default",
89 "value", v,
90 "default", cfg.SampleRatio,
91 "error", err,
92 )
93 }
94 }
95
96 if v := os.Getenv("OTEL_EXPORTER_OTLP_INSECURE"); v != "" {
97 cfg.Insecure = v == "true" || v == "1"
98 }
99
100 return cfg
101}
102
103// Validate checks the configuration for invalid values.
104// Returns nil if the configuration is valid, or an error describing the problem.
105// When Enabled is false, only the SampleRatio is validated (for safety).
106// When Enabled is true, all required fields must be set.
107func (c Config) Validate() error {
108 // Always validate SampleRatio regardless of enabled state
109 if c.SampleRatio < 0.0 || c.SampleRatio > 1.0 {
110 return fmt.Errorf("%w: got %f", ErrInvalidSampleRatio, c.SampleRatio)
111 }
112
113 // When enabled, validate required fields
114 if c.Enabled {
115 if c.Endpoint == "" {
116 return ErrMissingEndpoint
117 }
118 }
119
120 return nil
121}