-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdoc.go
More file actions
213 lines (212 loc) · 7.37 KB
/
doc.go
File metadata and controls
213 lines (212 loc) · 7.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2025 The Rivaas Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package tracing provides comprehensive OpenTelemetry-based distributed tracing
// for Go applications. It supports multiple exporters (Stdout, OTLP, Noop)
// and integrates seamlessly with HTTP frameworks.
//
// The tracing package follows the functional options pattern:
// - Options apply to an internal config struct
// - New() validates the config and builds the Tracer from it
// - New() returns (*Tracer, error); MustNew() panics on error
//
// # Basic Usage
//
// import (
// "context"
// "log"
// "rivaas.dev/tracing"
// )
//
// tracer, err := tracing.New(
// tracing.WithServiceName("my-service"),
// tracing.WithServiceVersion("v1.0.0"),
// tracing.WithStdout(),
// )
// if err != nil {
// log.Fatal(err)
// }
// defer tracer.Shutdown(context.Background())
//
// # Providers
//
// These providers are supported with convenient options:
//
// - WithNoop() (default): No traces exported (safe default)
// - WithStdout(): Prints traces to stdout (for development/testing)
// - WithOTLP(endpoint): Sends traces to OTLP collector via gRPC (for production)
// - WithOTLPHTTP(endpoint): Sends traces to OTLP collector via HTTP
//
// # OTLP and Start
//
// With OTLP providers (WithOTLP or WithOTLPHTTP), you must call Start(ctx) before exporting traces:
//
// tracer := tracing.MustNew(
// tracing.WithServiceName("my-service"),
// tracing.WithOTLP("localhost:4317"),
// )
// if err := tracer.Start(ctx); err != nil {
// log.Fatal(err)
// }
// defer tracer.Shutdown(context.Background())
//
// If you forget to call Start(), New() does not return an error and no traces are exported;
// a one-time log warning is emitted when the first span is created. Use RequiresStart() to
// detect when a tracer requires Start (returns true for OTLP providers), and IsStarted() to
// assert that Start(ctx) has been called (e.g. in tests or wiring code).
//
// # HTTP Middleware
//
// Use the middleware for automatic request tracing:
//
// tracer := tracing.MustNew(
// tracing.WithServiceName("my-api"),
// tracing.WithOTLP("localhost:4317"),
// )
// defer tracer.Shutdown(context.Background())
//
// // Use Middleware for HTTP handlers (returns error; use MustMiddleware to panic)
// handler, err := tracing.Middleware(tracer,
// tracing.WithExcludePaths("/health", "/metrics"),
// tracing.WithHeaders("X-Request-ID"),
// )
// if err != nil {
// log.Fatal(err)
// }
// http.ListenAndServe(":8080", handler(mux))
//
// Middleware options (e.g., WithExcludePaths) must not be nil. Passing a nil option
// returns a validation error from Middleware() or causes MustMiddleware() to panic.
//
// # Custom Spans
//
// Create and manage spans using the provided methods:
//
// ctx, span := tracer.StartSpan(ctx, "database-query")
// defer tracer.FinishSpan(span)
//
// Use FinishSpan(span) for success (no HTTP status), or FinishSpanWithHTTPStatus(span, statusCode)
// for request-level spans. Use FinishSpanWithError(span, err) when the span fails.
//
// tracer.SetSpanAttribute(span, "user.id", "123")
// tracer.AddSpanEvent(span, "cache_hit",
// attribute.String("key", "user:123"),
// )
//
// When you only have context (e.g. from middleware), use SetSpanAttributeFromContext
// and AddSpanEventFromContext; when you have the *Tracer and span, use
// tracer.SetSpanAttribute and tracer.AddSpanEvent. The two styles are equivalent;
// the context helpers are for use when the tracer is not in scope.
//
// # Error handling
//
// When a span fails, call FinishSpanWithError(span, err) to record the error and end the span.
// To record an error but continue (e.g. retry), use RecordError(span, err); then call
// FinishSpan or FinishSpanWithError when the span ends.
//
// if err := step(); err != nil {
// tracer.RecordError(span, err)
// }
// defer tracer.FinishSpan(span)
//
// # Context propagation (goroutines)
//
// Use CopyTraceContext(ctx) when starting goroutines or background work so new spans
// link to the same trace:
//
// traceCtx := tracing.CopyTraceContext(r.Context())
// go func() {
// _, span := tracer.StartSpan(traceCtx, "async-job")
// defer tracer.FinishSpan(span)
// doAsyncWork(ctx)
// }()
//
// # WithSpan
//
// Run a function under a span; the span is finished with success or error based on
// the returned error:
//
// err := tracer.WithSpan(ctx, "process-order", func(ctx context.Context) error {
// return processOrder(ctx, id)
// })
//
// # App integration
//
// When using the app package, enable tracing with
// app.WithObservability(app.WithTracing(...)). Request spans are created with
// the same behavior as the standalone middleware (W3C propagation, sampling,
// standard attributes). From handlers, use c.StartSpan, c.FinishSpan,
// c.FinishSpanWithHTTPStatus, c.FinishSpanWithError, c.RecordError, c.CopyTraceContext,
// and c.WithSpan with the same semantics as the tracing package; use c.Tracer()
// only for advanced use (e.g. passing the tracer to another library).
//
// # Context Propagation
//
// Automatically propagate trace context across service boundaries:
//
// ctx = tracer.ExtractTraceContext(ctx, req.Header)
// tracer.InjectTraceContext(ctx, resp.Header)
//
// # Sampling
//
// Control which requests are traced using sampling:
//
// tracer := tracing.MustNew(
// tracing.WithServiceName("my-service"),
// tracing.WithSampleRate(0.1), // Sample 10% of requests
// )
//
// # Thread Safety
//
// All methods are thread-safe. The Tracer struct is immutable after creation,
// with read-only maps and slices ensuring safe concurrent access without locks.
// Span operations use OpenTelemetry's thread-safe primitives.
//
// # Global State
//
// By default, this package does NOT set the global OpenTelemetry tracer provider.
// Use WithGlobalTracerProvider() option if you want to register the tracer provider
// as the global default via otel.SetTracerProvider().
//
// This allows multiple tracing configurations to coexist in the same process,
// and makes it easier to integrate Rivaas into larger binaries that already
// manage their own global tracer provider.
//
// # Path Filtering (Middleware Option)
//
// Exclude specific paths from tracing via middleware options:
//
// handler, err := tracing.Middleware(tracer,
// tracing.WithExcludePaths("/health", "/metrics", "/ready"),
// tracing.WithExcludePrefixes("/debug/"),
// tracing.WithExcludePatterns("^/internal/.*"),
// )
// if err != nil {
// log.Fatal(err)
// }
// // ... use handler(mux)
//
// # Custom Tracer Provider
//
// For advanced use cases, provide your own OpenTelemetry tracer provider:
//
// tracer := tracing.MustNew(
// tracing.WithServiceName("my-service"),
// tracing.WithTracerProvider(customProvider),
// )
//
// # Standalone Usage
//
// This package works independently without the full Rivaas framework. Use it
// with any Go HTTP handler (net/http, Gin, Echo, etc.).
package tracing