gitlab-labkit 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/gitlab-labkit.gemspec +5 -1
- data/lib/gitlab-labkit.rb +2 -0
- data/lib/labkit/logging/field_validator.rb +10 -11
- data/lib/labkit/middleware/sidekiq/tracing/client.rb +1 -1
- data/lib/labkit/middleware/sidekiq/tracing/server.rb +1 -1
- data/lib/labkit/tracing/README.md +716 -0
- data/lib/labkit/tracing/abstract_instrumenter.rb +1 -1
- data/lib/labkit/tracing/adapters/base_span.rb +35 -0
- data/lib/labkit/tracing/adapters/base_tracer.rb +39 -0
- data/lib/labkit/tracing/adapters/opentelemetry_span.rb +73 -0
- data/lib/labkit/tracing/adapters/opentelemetry_tracer.rb +102 -0
- data/lib/labkit/tracing/adapters/opentracing_span.rb +70 -0
- data/lib/labkit/tracing/adapters/opentracing_tracer.rb +50 -0
- data/lib/labkit/tracing/auto_initialize.rb +46 -0
- data/lib/labkit/tracing/factory.rb +26 -38
- data/lib/labkit/tracing/grpc/client_interceptor.rb +1 -1
- data/lib/labkit/tracing/grpc/server_interceptor.rb +2 -2
- data/lib/labkit/tracing/jaeger_factory.rb +12 -9
- data/lib/labkit/tracing/open_telemetry_factory.rb +218 -0
- data/lib/labkit/tracing/open_tracing_factory.rb +48 -0
- data/lib/labkit/tracing/rack_middleware.rb +1 -1
- data/lib/labkit/tracing/railtie.rb +15 -0
- data/lib/labkit/tracing/tracing_utils.rb +37 -34
- data/lib/labkit/tracing.rb +108 -5
- data/lib/labkit/user_experience_sli/null.rb +2 -0
- metadata +74 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
# Labkit::Tracing
|
|
2
|
+
|
|
3
|
+
The `Labkit::Tracing` module provides distributed tracing functionality for Ruby applications using the OpenTelemetry standard. It enables you to trace requests across multiple services and components, helping you understand application performance and debug issues in distributed systems.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Distributed tracing allows you to track requests as they flow through your application and external services. The tracing module integrates with OpenTelemetry (OTLP) backends, and provides automatic instrumentation for:
|
|
8
|
+
|
|
9
|
+
- HTTP requests (Rack/Rails)
|
|
10
|
+
- Redis operations
|
|
11
|
+
- External HTTP requests
|
|
12
|
+
- Rails components (ActiveRecord, ActionView, ActiveSupport)
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
Tracing is controlled via an environment variable:
|
|
17
|
+
|
|
18
|
+
### Required Configuration
|
|
19
|
+
|
|
20
|
+
**`GITLAB_TRACING`** - Connection string for the tracing backend
|
|
21
|
+
|
|
22
|
+
Format: `otlp://<host:port>?<options>`
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
```bash
|
|
26
|
+
# OpenTelemetry with HTTP endpoint and service name
|
|
27
|
+
export GITLAB_TRACING="otlp://localhost:4318?service_name=my-api"
|
|
28
|
+
|
|
29
|
+
# Console exporter for development/testing (outputs to stdout)
|
|
30
|
+
export GITLAB_TRACING="otlp://console?service_name=my-api"
|
|
31
|
+
|
|
32
|
+
# Without service name (defaults to "labkit-service")
|
|
33
|
+
export GITLAB_TRACING="otlp://localhost:4318"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Automatic Initialization**
|
|
37
|
+
|
|
38
|
+
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer when the gem is loaded. No manual initialization is required in most cases.
|
|
39
|
+
|
|
40
|
+
Auto-initialization provides:
|
|
41
|
+
- Automatic tracer creation with connection string settings
|
|
42
|
+
- Service name from `service_name` query parameter (defaults to `"labkit-service"`)
|
|
43
|
+
- All available OpenTelemetry instrumentation enabled by default (`c.use_all()`)
|
|
44
|
+
- LabKit-specific instrumentation enabled automatically:
|
|
45
|
+
- Rails components (ActiveRecord, ActionView, ActiveSupport) - if Rails is available
|
|
46
|
+
- Redis instrumentation - if Redis gem is loaded
|
|
47
|
+
- External HTTP instrumentation (Net::HTTP, Excon, HTTPClient)
|
|
48
|
+
- Safe fallback if initialization fails (no-op tracer)
|
|
49
|
+
|
|
50
|
+
### Optional Configuration
|
|
51
|
+
|
|
52
|
+
**`GITLAB_TRACING_INCLUDE_STACKTRACE`** - Comma-separated list of operation name prefixes to include stack traces
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
```bash
|
|
56
|
+
export GITLAB_TRACING_INCLUDE_STACKTRACE="redis,active_record"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Connection String Configuration
|
|
60
|
+
|
|
61
|
+
The following connection string formats and query parameters are supported:
|
|
62
|
+
|
|
63
|
+
### Endpoints
|
|
64
|
+
|
|
65
|
+
- **HTTP Endpoint** - OTLP HTTP collector endpoint (default port: 4318)
|
|
66
|
+
- Example: `otlp://localhost:4318`
|
|
67
|
+
- Example with custom path: `otlp://localhost:4318/v1/traces`
|
|
68
|
+
- Example with authentication: `otlp://user:password@collector.example.com:4318`
|
|
69
|
+
|
|
70
|
+
- **Console Exporter** - Output spans to stdout (development/testing only)
|
|
71
|
+
- Example: `otlp://console`
|
|
72
|
+
- **Use Case**: Local development, debugging, automated testing
|
|
73
|
+
- **Behavior**: Outputs spans immediately to stdout with no remote export
|
|
74
|
+
- **Benefits**: No external collector required, instant feedback, simple debugging
|
|
75
|
+
|
|
76
|
+
### Sampling
|
|
77
|
+
|
|
78
|
+
**Default Behavior:** When no sampler is specified, probabilistic sampling is used with a **0.1% sample rate** (1 in 1000 traces).
|
|
79
|
+
|
|
80
|
+
- **`sampler`** - Sampling strategy (`probabilistic` or `const`)
|
|
81
|
+
- `probabilistic` - Sample a percentage of traces (default: 0.1%)
|
|
82
|
+
- `const` - Sample all traces (when `sampler_param=1`) or none (when `sampler_param=0`)
|
|
83
|
+
|
|
84
|
+
- **`sampler_param`** - Parameter for the sampler
|
|
85
|
+
- For `probabilistic`: rate between 0.0 and 1.0 (e.g., `0.1` = 10%)
|
|
86
|
+
- For `const`: `1` (sample all) or `0` (sample none)
|
|
87
|
+
|
|
88
|
+
- **`service_name`** - Set the service name for this tracer (defaults to `"labkit-service"`)
|
|
89
|
+
- The service name identifies your application in traces
|
|
90
|
+
- Appears in trace UI and helps distinguish between services
|
|
91
|
+
- Example: `service_name=checkout-api`
|
|
92
|
+
|
|
93
|
+
### Examples
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Development: Console output with full sampling and service name
|
|
97
|
+
export GITLAB_TRACING="otlp://console?service_name=my-app&sampler=const&sampler_param=1"
|
|
98
|
+
|
|
99
|
+
# Staging: HTTP endpoint with probabilistic sampling (1%)
|
|
100
|
+
export GITLAB_TRACING="otlp://collector.staging.example.com:4318?service_name=my-app&sampler=probabilistic&sampler_param=0.01"
|
|
101
|
+
|
|
102
|
+
# Production: HTTP endpoint with custom path and authentication
|
|
103
|
+
export GITLAB_TRACING="otlp://user:pass@collector.prod.example.com:4318/v1/traces?service_name=my-app&sampler=probabilistic&sampler_param=0.001"
|
|
104
|
+
|
|
105
|
+
# Testing: Console output with no sampling (useful for test verification)
|
|
106
|
+
export GITLAB_TRACING="otlp://console?service_name=my-app&sampler=const&sampler_param=0"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Using OpenTelemetry APIs Directly
|
|
110
|
+
|
|
111
|
+
**Labkit is a thin wrapper around OpenTelemetry** - it handles initialization and provides sensible defaults, but you should use OpenTelemetry APIs directly for instrumentation.
|
|
112
|
+
|
|
113
|
+
### What Labkit Provides Out-of-the-Box
|
|
114
|
+
|
|
115
|
+
- **Automatic tracer initialization** from `GITLAB_TRACING` environment variable
|
|
116
|
+
- **Connection string parsing** for OTLP endpoints, samplers, and exporters
|
|
117
|
+
- **Default service name** (`labkit-service`) with query parameter override
|
|
118
|
+
- **Automatic instrumentation** for Rails, Redis, and external HTTP requests
|
|
119
|
+
- **Correlation ID injection** into all spans automatically
|
|
120
|
+
- **Security sanitization** for SQL queries, URLs, and Redis commands
|
|
121
|
+
|
|
122
|
+
### What You Should Use Directly
|
|
123
|
+
|
|
124
|
+
For all span creation and manipulation, use OpenTelemetry APIs:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# Get Labkit-configured tracer
|
|
128
|
+
tracer = Labkit::Tracing.tracer
|
|
129
|
+
|
|
130
|
+
# Get current span
|
|
131
|
+
span = Labkit::Tracing.current_span
|
|
132
|
+
|
|
133
|
+
# Use OpenTelemetry APIs for everything else
|
|
134
|
+
tracer.in_span("operation") do |span|
|
|
135
|
+
span.set_attribute("key", "value")
|
|
136
|
+
span.add_event("event_name", attributes: { "detail" => "info" })
|
|
137
|
+
span.record_exception(exception) if exception
|
|
138
|
+
span.status = OpenTelemetry::Trace::Status.error("Failed")
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Benefits of direct OTel usage:**
|
|
143
|
+
- Full access to OpenTelemetry capabilities (events, exceptions, status, links)
|
|
144
|
+
- Works with all OpenTelemetry documentation and examples
|
|
145
|
+
- Compatible with other OTel libraries and tools
|
|
146
|
+
- Future-proof as OpenTelemetry evolves
|
|
147
|
+
|
|
148
|
+
### Context Sharing
|
|
149
|
+
|
|
150
|
+
Labkit and OpenTelemetry share the same global tracer provider and context propagation mechanism. This means spans created by Labkit are visible to OpenTelemetry APIs and vice versa.
|
|
151
|
+
|
|
152
|
+
This seamless integration means you can:
|
|
153
|
+
- Use Labkit for initialization and defaults
|
|
154
|
+
- Use OpenTelemetry APIs directly for instrumentation
|
|
155
|
+
- Mix both approaches in the same codebase
|
|
156
|
+
- Trust that context propagates correctly across both
|
|
157
|
+
|
|
158
|
+
## Usage
|
|
159
|
+
|
|
160
|
+
### Automatic Initialization (Default Behavior)
|
|
161
|
+
|
|
162
|
+
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer when the gem loads:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Set environment variable with service name
|
|
166
|
+
export GITLAB_TRACING="otlp://localhost:4318?service_name=my-api&sampler=probabilistic&sampler_param=0.01"
|
|
167
|
+
|
|
168
|
+
# Or use default service name "labkit-service"
|
|
169
|
+
export GITLAB_TRACING="otlp://localhost:4318"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Auto-initialization:
|
|
173
|
+
- Creates tracer with connection string settings (sampler, exporter, endpoints)
|
|
174
|
+
- Uses service name from `service_name` query parameter (defaults to `"labkit-service"`)
|
|
175
|
+
- Enables all available OpenTelemetry instrumentation
|
|
176
|
+
- Sets up the global `OpenTelemetry.tracer_provider`
|
|
177
|
+
|
|
178
|
+
**No application code changes required** - just set the environment variable.
|
|
179
|
+
|
|
180
|
+
### Automatic Rails Middleware Insertion
|
|
181
|
+
|
|
182
|
+
When using Rails, the `Labkit::Tracing::RackMiddleware` is automatically inserted into your middleware stack when `GITLAB_TRACING` is set. No manual configuration needed!
|
|
183
|
+
|
|
184
|
+
**How it works:**
|
|
185
|
+
- Detected automatically via Rails Railtie
|
|
186
|
+
- Inserted after `Labkit::Middleware::Rack` for proper correlation ID propagation
|
|
187
|
+
- Only activates when `GITLAB_TRACING` is set
|
|
188
|
+
- Logs insertion to Rails logger for visibility
|
|
189
|
+
|
|
190
|
+
**Middleware Positioning:**
|
|
191
|
+
|
|
192
|
+
The middleware is automatically inserted, but you can still customize its position if needed:
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
# config/application.rb
|
|
196
|
+
module MyApp
|
|
197
|
+
class Application < Rails::Application
|
|
198
|
+
# Move tracing middleware before authentication for earlier tracing
|
|
199
|
+
config.middleware.move_before AuthenticationMiddleware, Labkit::Tracing::RackMiddleware
|
|
200
|
+
|
|
201
|
+
# Or move it after error handling
|
|
202
|
+
config.middleware.move_after ErrorHandlingMiddleware, Labkit::Tracing::RackMiddleware
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Note:** The middleware is automatically added, so you only need to use `move_before` or `move_after` if you need custom positioning. Don't use `insert_after` or `use` as that would duplicate the middleware.
|
|
208
|
+
|
|
209
|
+
### Manual Initialization (Override Auto-initialization)
|
|
210
|
+
|
|
211
|
+
You can override auto-initialization by calling `Factory.create_tracer` in your application initializers. This is useful when you need custom configuration:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
# config/initializers/tracing.rb
|
|
215
|
+
# Override auto-initialization with custom configuration
|
|
216
|
+
Labkit::Tracing::Factory.create_tracer("my-custom-service", ENV["GITLAB_TRACING"]) do |c|
|
|
217
|
+
# Selective instrumentation instead of c.use_all()
|
|
218
|
+
c.use 'OpenTelemetry::Instrumentation::Rails'
|
|
219
|
+
c.use 'OpenTelemetry::Instrumentation::Sidekiq'
|
|
220
|
+
|
|
221
|
+
# Add custom span processors
|
|
222
|
+
c.add_span_processor(MyCustomProcessor.new)
|
|
223
|
+
|
|
224
|
+
# Add additional resource attributes
|
|
225
|
+
c.resource = c.resource.merge(
|
|
226
|
+
OpenTelemetry::SDK::Resources::Resource.create(
|
|
227
|
+
'deployment.environment' => Rails.env,
|
|
228
|
+
'service.version' => MyApp::VERSION
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
end
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**When to use manual initialization:**
|
|
235
|
+
- You need selective instrumentation (not `c.use_all()`)
|
|
236
|
+
- You need custom span processors
|
|
237
|
+
- You need to add resource attributes
|
|
238
|
+
- You want to override the service name from code instead of the connection string
|
|
239
|
+
|
|
240
|
+
**Note:** Manual calls to `Factory.create_tracer` reconfigure the global OpenTelemetry tracer provider. The last call wins, so manual initialization overrides auto-initialization.
|
|
241
|
+
|
|
242
|
+
### Manual Span Creation
|
|
243
|
+
|
|
244
|
+
Labkit provides setup and defaults, but you should use OpenTelemetry APIs directly for creating spans and adding instrumentation:
|
|
245
|
+
|
|
246
|
+
**Creating spans with the tracer:**
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
# Get the tracer (configured by Labkit with connection string settings)
|
|
250
|
+
tracer = Labkit::Tracing.tracer
|
|
251
|
+
|
|
252
|
+
# Create a span with OpenTelemetry API
|
|
253
|
+
tracer.in_span("process_data", attributes: { "user_id" => user.id }) do |span|
|
|
254
|
+
result = process_data(data)
|
|
255
|
+
|
|
256
|
+
span.set_attribute("result_count", result.count)
|
|
257
|
+
span.add_event("processing_complete", attributes: { "duration_ms" => 123 })
|
|
258
|
+
|
|
259
|
+
result
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Working with the current span:**
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
# Get the currently active span
|
|
267
|
+
span = Labkit::Tracing.current_span
|
|
268
|
+
|
|
269
|
+
# Check if the span is being recorded (respects sampling decisions)
|
|
270
|
+
if span.recording?
|
|
271
|
+
span.set_attribute("expensive_data", compute_expensive_data)
|
|
272
|
+
span.add_event("custom_event", attributes: { "key" => "value" })
|
|
273
|
+
end
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Creating child spans:**
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
tracer = Labkit::Tracing.tracer
|
|
280
|
+
|
|
281
|
+
tracer.in_span("parent_operation") do |parent_span|
|
|
282
|
+
# Child span is automatically created within parent context
|
|
283
|
+
tracer.in_span("child_operation") do |child_span|
|
|
284
|
+
child_span.set_attribute("type", "background")
|
|
285
|
+
perform_operation
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Initialization Order and Precedence
|
|
291
|
+
|
|
292
|
+
**Auto-initialization:**
|
|
293
|
+
1. Runs when the gem is loaded (`require 'gitlab-labkit'`)
|
|
294
|
+
2. Only runs if `GITLAB_TRACING` environment variable is set
|
|
295
|
+
3. Uses `service_name` query parameter or defaults to `"labkit-service"`
|
|
296
|
+
4. Attempts to enable all instrumentation with `c.use_all()`
|
|
297
|
+
|
|
298
|
+
**Manual initialization:**
|
|
299
|
+
1. Runs in your application initializer (e.g., `config/initializers/tracing.rb`)
|
|
300
|
+
2. Overrides auto-initialization by reconfiguring the global tracer provider
|
|
301
|
+
3. Last call to `Factory.create_tracer` wins
|
|
302
|
+
|
|
303
|
+
**Configuration precedence within a single `Factory.create_tracer` call:**
|
|
304
|
+
1. **GITLAB_TRACING connection string** settings are applied first:
|
|
305
|
+
- Service name (from `service_name` query parameter or method parameter)
|
|
306
|
+
- Sampler type and parameters (`sampler`, `sampler_param`)
|
|
307
|
+
- Exporter endpoint, protocol, and authentication headers
|
|
308
|
+
- Span processors with OTLP exporter
|
|
309
|
+
|
|
310
|
+
2. **Configuration block** runs second and can:
|
|
311
|
+
- Add automatic instrumentation (`use`, `use_all`)
|
|
312
|
+
- Add additional span processors
|
|
313
|
+
- Merge additional resource attributes
|
|
314
|
+
- Override service_name if explicitly set in the block
|
|
315
|
+
|
|
316
|
+
**Important:** Calling `Factory.create_tracer` multiple times will reconfigure the global OpenTelemetry tracer provider each time. Initialize tracing once during application startup.
|
|
317
|
+
|
|
318
|
+
### Checking if Current Request is Sampled
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
|
|
322
|
+
# checks if span will actually record data
|
|
323
|
+
span = Labkit::Tracing.current_span
|
|
324
|
+
if span.recording?
|
|
325
|
+
span.set_attribute("expensive_data", compute_expensive_data)
|
|
326
|
+
end
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Instrumentation
|
|
330
|
+
|
|
331
|
+
### Rack Middleware
|
|
332
|
+
|
|
333
|
+
Instrument incoming HTTP requests in Rack/Rails applications:
|
|
334
|
+
|
|
335
|
+
```ruby
|
|
336
|
+
# For non-Rails Rack apps (Sinatra, Grape, etc.) - in config.ru
|
|
337
|
+
use Labkit::Tracing::RackMiddleware
|
|
338
|
+
|
|
339
|
+
# For Rails apps: Middleware is automatically inserted when GITLAB_TRACING is set.
|
|
340
|
+
# No manual configuration needed! See "Automatic Rails Middleware Insertion" section.
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
This automatically:
|
|
344
|
+
- Extracts trace context from incoming requests
|
|
345
|
+
- Creates spans for HTTP requests with method and URL
|
|
346
|
+
- Adds HTTP status codes to spans
|
|
347
|
+
- Sanitizes sensitive parameters in URLs
|
|
348
|
+
|
|
349
|
+
### Rails Components
|
|
350
|
+
|
|
351
|
+
**Note:** With auto-initialization enabled (when `GITLAB_TRACING` is set), these LabKit-specific instrumentations are automatically enabled alongside OpenTelemetry's instrumentation. You only need to call these methods manually if you've disabled auto-initialization or need to control when instrumentation starts.
|
|
352
|
+
|
|
353
|
+
Both OpenTelemetry and LabKit instrumentation run side-by-side, providing complementary trace data:
|
|
354
|
+
- **OpenTelemetry Rails instrumentation**: Provides standardized, method-level traces using semantic conventions
|
|
355
|
+
- **LabKit instrumentation**: Adds GitLab-specific details like SQL fingerprints, sanitized queries, and enhanced metadata
|
|
356
|
+
|
|
357
|
+
#### ActiveRecord (Database Queries)
|
|
358
|
+
|
|
359
|
+
```ruby
|
|
360
|
+
# Only needed if auto-initialization is disabled or you need manual control
|
|
361
|
+
unsubscribe = Labkit::Tracing::Rails::ActiveRecord::Subscriber.instrument
|
|
362
|
+
|
|
363
|
+
# Later, to stop instrumentation:
|
|
364
|
+
unsubscribe.call
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Traces:
|
|
368
|
+
- SQL queries with sanitized statements
|
|
369
|
+
- Query fingerprints (via PgQuery)
|
|
370
|
+
- Connection IDs
|
|
371
|
+
- Cached query indicators
|
|
372
|
+
|
|
373
|
+
#### ActionView (Template Rendering)
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
# Only needed if auto-initialization is disabled or you need manual control
|
|
377
|
+
unsubscribe = Labkit::Tracing::Rails::ActionView::Subscriber.instrument
|
|
378
|
+
|
|
379
|
+
# Later, to stop instrumentation:
|
|
380
|
+
unsubscribe.call
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Traces:
|
|
384
|
+
- Template rendering
|
|
385
|
+
- Partial rendering
|
|
386
|
+
- Collection rendering
|
|
387
|
+
- Template identifiers and layouts
|
|
388
|
+
|
|
389
|
+
#### ActiveSupport (Caching)
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
# Only needed if auto-initialization is disabled or you need manual control
|
|
393
|
+
unsubscribe = Labkit::Tracing::Rails::ActiveSupport::Subscriber.instrument
|
|
394
|
+
|
|
395
|
+
# Later, to stop instrumentation:
|
|
396
|
+
unsubscribe.call
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Traces:
|
|
400
|
+
- Cache reads (with hit/miss information)
|
|
401
|
+
- Cache writes
|
|
402
|
+
- Cache deletes
|
|
403
|
+
- Cache fetch operations
|
|
404
|
+
- Cache key information
|
|
405
|
+
|
|
406
|
+
### Redis
|
|
407
|
+
|
|
408
|
+
**Note:** Redis instrumentation is automatically enabled when `GITLAB_TRACING` is set and the Redis gem is loaded.
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
# Only needed if auto-initialization is disabled or you need manual control
|
|
412
|
+
Labkit::Tracing::Redis.instrument
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
This automatically traces:
|
|
416
|
+
- Individual Redis commands
|
|
417
|
+
- Pipelined commands (up to 5 commands shown)
|
|
418
|
+
- Connection details (host, port, scheme)
|
|
419
|
+
- Sanitized command arguments (sensitive commands like AUTH and EVAL are masked)
|
|
420
|
+
|
|
421
|
+
### External HTTP Requests
|
|
422
|
+
|
|
423
|
+
**Note:** External HTTP instrumentation is automatically enabled when `GITLAB_TRACING` is set.
|
|
424
|
+
|
|
425
|
+
Instrument outgoing HTTP requests made by Net::HTTP, Excon, and HTTPClient:
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
# Only needed if auto-initialization is disabled or you need manual control
|
|
429
|
+
unsubscribe = Labkit::Tracing::ExternalHttp.instrument
|
|
430
|
+
|
|
431
|
+
# Later, to stop instrumentation:
|
|
432
|
+
unsubscribe.call
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Automatically traces:
|
|
436
|
+
- HTTP method and URL
|
|
437
|
+
- Response status codes
|
|
438
|
+
- Host, port, and scheme
|
|
439
|
+
- Proxy information (if applicable)
|
|
440
|
+
|
|
441
|
+
## Architecture
|
|
442
|
+
|
|
443
|
+
### Core Components
|
|
444
|
+
|
|
445
|
+
- **`Labkit::Tracing`** - Main module with configuration and utility methods
|
|
446
|
+
- **`Labkit::Tracing::Factory`** - Creates and configures tracer instances
|
|
447
|
+
- **`Labkit::Tracing::OpenTelemetryFactory`** - OpenTelemetry-specific tracer configuration
|
|
448
|
+
- **`Labkit::Tracing::TracingUtils`** - Utilities for span management
|
|
449
|
+
- **`Labkit::Tracing::AbstractInstrumenter`** - Base class for ActiveSupport::Notifications instrumenters
|
|
450
|
+
|
|
451
|
+
### Instrumentation Pattern
|
|
452
|
+
|
|
453
|
+
Most instrumenters follow this pattern:
|
|
454
|
+
|
|
455
|
+
1. Subscribe to `ActiveSupport::Notifications` events
|
|
456
|
+
2. Create spans when events start
|
|
457
|
+
3. Add tags and metadata to spans
|
|
458
|
+
4. Handle exceptions and log them to spans
|
|
459
|
+
5. Close spans when events finish
|
|
460
|
+
|
|
461
|
+
### Span Lifecycle
|
|
462
|
+
|
|
463
|
+
Each span automatically includes:
|
|
464
|
+
- **Correlation ID** - Links traces to logs and other telemetry
|
|
465
|
+
- **Common tags** - Component, operation type, etc.
|
|
466
|
+
- **Exception handling** - Errors are logged with stack traces
|
|
467
|
+
- **Stack traces** - Optional, based on `GITLAB_TRACING_INCLUDE_STACKTRACE`
|
|
468
|
+
|
|
469
|
+
## Security and Privacy
|
|
470
|
+
|
|
471
|
+
The tracing module includes built-in sanitization:
|
|
472
|
+
|
|
473
|
+
- **SQL queries** - Sanitized using `Labkit::Logging::Sanitizer.sanitize_sql`
|
|
474
|
+
- **URLs** - Filtered parameters removed (e.g., tokens, passwords)
|
|
475
|
+
- **Redis commands** - Sensitive commands (AUTH, EVAL) are masked
|
|
476
|
+
- **Redis arguments** - Long arguments are truncated with masking
|
|
477
|
+
- **Exception messages** - Sanitized using `Labkit::Logging::Sanitizer.sanitize_field`
|
|
478
|
+
|
|
479
|
+
## Performance Considerations
|
|
480
|
+
|
|
481
|
+
### Sampling
|
|
482
|
+
|
|
483
|
+
Use sampling to control overhead:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
# Default: 0.1% sampling (no sampler specified)
|
|
487
|
+
export GITLAB_TRACING="otlp://localhost:4318"
|
|
488
|
+
|
|
489
|
+
# Trace 1% of requests
|
|
490
|
+
export GITLAB_TRACING="otlp://localhost:4318?sampler=probabilistic&sampler_param=0.01"
|
|
491
|
+
|
|
492
|
+
# Trace all requests (high overhead)
|
|
493
|
+
export GITLAB_TRACING="otlp://localhost:4318?sampler=const&sampler_param=1"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Conditional Expensive Operations
|
|
497
|
+
|
|
498
|
+
Use OpenTelemetry's `span.recording?` to check if expensive operations should be performed:
|
|
499
|
+
|
|
500
|
+
```ruby
|
|
501
|
+
span = Labkit::Tracing.current_span
|
|
502
|
+
|
|
503
|
+
if span.recording?
|
|
504
|
+
# Only execute when trace is being captured and sampled
|
|
505
|
+
span.set_attribute("expensive_data", compute_expensive_data)
|
|
506
|
+
span.add_event("detailed_event", attributes: expensive_analysis)
|
|
507
|
+
end
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Batch Span Processing
|
|
511
|
+
|
|
512
|
+
Spans are batched and exported to the OTLP endpoint at regular intervals to optimize performance and network usage.
|
|
513
|
+
|
|
514
|
+
## Integration with Correlation
|
|
515
|
+
|
|
516
|
+
Tracing integrates with `Labkit::Correlation` to link traces with logs and other telemetry:
|
|
517
|
+
|
|
518
|
+
```ruby
|
|
519
|
+
# Correlation ID is automatically added to all spans
|
|
520
|
+
correlation_id = Labkit::Correlation::CorrelationId.current_id
|
|
521
|
+
# This ID appears in both logs and traces
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Example: Complete Setup
|
|
525
|
+
|
|
526
|
+
### Development Environment with Console Exporter (Automatic)
|
|
527
|
+
|
|
528
|
+
```ruby
|
|
529
|
+
# config/application.rb
|
|
530
|
+
module MyApp
|
|
531
|
+
class Application < Rails::Application
|
|
532
|
+
# Labkit::Tracing::RackMiddleware is automatically inserted!
|
|
533
|
+
# No manual configuration needed.
|
|
534
|
+
|
|
535
|
+
# Optional: Customize middleware position if needed
|
|
536
|
+
# config.middleware.move_after SomeOtherMiddleware, Labkit::Tracing::RackMiddleware
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# .env.development
|
|
541
|
+
# GITLAB_TRACING="otlp://console?service_name=my-rails-app&sampler=const&sampler_param=1"
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**That's it!** Both the tracer and middleware are automatically configured when the gem loads. This setup outputs all trace spans directly to your development console/logs, making it easy to:
|
|
545
|
+
- Debug request flows without external tools
|
|
546
|
+
- Verify instrumentation is working correctly
|
|
547
|
+
- Test tracing configuration changes
|
|
548
|
+
- Develop and debug trace-dependent features
|
|
549
|
+
|
|
550
|
+
### Production Environment (Automatic with Custom Metadata)
|
|
551
|
+
|
|
552
|
+
Most applications can use automatic initialization:
|
|
553
|
+
|
|
554
|
+
```ruby
|
|
555
|
+
# config/application.rb
|
|
556
|
+
module MyApp
|
|
557
|
+
class Application < Rails::Application
|
|
558
|
+
# Labkit::Tracing::RackMiddleware is automatically inserted!
|
|
559
|
+
# No manual configuration needed.
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# .env.production
|
|
564
|
+
# GITLAB_TRACING="otlp://collector.example.com:4318?service_name=my-rails-app&sampler=probabilistic&sampler_param=0.01"
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
If you need to add custom metadata (deployment environment, version, etc.), override tracer initialization:
|
|
568
|
+
|
|
569
|
+
```ruby
|
|
570
|
+
# config/initializers/tracing.rb
|
|
571
|
+
|
|
572
|
+
Labkit::Tracing::Factory.create_tracer("my-rails-app", ENV["GITLAB_TRACING"]) do |c|
|
|
573
|
+
# Enable all available OpenTelemetry instrumentation
|
|
574
|
+
c.use_all()
|
|
575
|
+
|
|
576
|
+
# Add deployment metadata
|
|
577
|
+
c.resource = c.resource.merge(
|
|
578
|
+
OpenTelemetry::SDK::Resources::Resource.create(
|
|
579
|
+
'deployment.environment' => Rails.env,
|
|
580
|
+
'service.version' => MyApp::VERSION
|
|
581
|
+
)
|
|
582
|
+
)
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# .env.production
|
|
586
|
+
# GITLAB_TRACING="otlp://collector.example.com:4318?service_name=my-rails-app&sampler=probabilistic&sampler_param=0.01"
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Note: Middleware is still automatically inserted even with custom tracer initialization.
|
|
590
|
+
|
|
591
|
+
### With Selective Instrumentation (Manual Override)
|
|
592
|
+
|
|
593
|
+
If you want to control exactly which components are instrumented instead of using `c.use_all()`, override tracer initialization:
|
|
594
|
+
|
|
595
|
+
**Important:** When you manually call `Factory.create_tracer`, auto-initialization still runs but is replaced by your manual configuration. The LabKit-specific instrumentation (Rails, Redis, ExternalHttp) is still automatically enabled unless you explicitly disable auto-initialization.
|
|
596
|
+
|
|
597
|
+
```ruby
|
|
598
|
+
# config/initializers/tracing.rb
|
|
599
|
+
# Override auto-initialization for selective OpenTelemetry instrumentation
|
|
600
|
+
Labkit::Tracing::Factory.create_tracer("my-rails-app", ENV["GITLAB_TRACING"]) do |c|
|
|
601
|
+
# Selective OpenTelemetry instrumentation (instead of c.use_all())
|
|
602
|
+
c.use 'OpenTelemetry::Instrumentation::Rails'
|
|
603
|
+
c.use 'OpenTelemetry::Instrumentation::Sidekiq'
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# LabKit instrumentation is already enabled automatically during auto-initialization.
|
|
607
|
+
# Only call these manually if you need to control timing or disable auto-initialization:
|
|
608
|
+
# Rails.application.config.after_initialize do
|
|
609
|
+
# Labkit::Tracing::Rails::ActiveRecord::Subscriber.instrument
|
|
610
|
+
# Labkit::Tracing::Rails::ActionView::Subscriber.instrument
|
|
611
|
+
# Labkit::Tracing::Rails::ActiveSupport::Subscriber.instrument
|
|
612
|
+
# Labkit::Tracing::ExternalHttp.instrument
|
|
613
|
+
# Labkit::Tracing::Redis.instrument
|
|
614
|
+
# end
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Note: Both middleware insertion and LabKit instrumentation are automatically handled even with manual tracer initialization.
|
|
618
|
+
|
|
619
|
+
## Troubleshooting
|
|
620
|
+
|
|
621
|
+
### Tracing Not Working
|
|
622
|
+
|
|
623
|
+
1. Verify `GITLAB_TRACING` is set correctly:
|
|
624
|
+
```ruby
|
|
625
|
+
puts Labkit::Tracing.connection_string
|
|
626
|
+
puts Labkit::Tracing.enabled?
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
2. Check for tracer creation errors in logs (warnings are emitted on failure during auto-initialization)
|
|
630
|
+
|
|
631
|
+
3. Verify the OTLP collector is reachable
|
|
632
|
+
|
|
633
|
+
### Middleware Not Tracing Requests (Rails)
|
|
634
|
+
|
|
635
|
+
If you're not seeing HTTP request traces in Rails:
|
|
636
|
+
|
|
637
|
+
1. Verify the middleware is in your Rails stack:
|
|
638
|
+
```ruby
|
|
639
|
+
# In Rails console
|
|
640
|
+
Rails.application.middleware.middlewares
|
|
641
|
+
# Should include Labkit::Tracing::RackMiddleware
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
2. Check Rails logs for auto-insertion message:
|
|
645
|
+
```
|
|
646
|
+
Labkit::Tracing: Automatically inserted RackMiddleware after Rails::Rack::Logger
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
3. If using custom middleware positioning, verify the order is correct:
|
|
650
|
+
```bash
|
|
651
|
+
# View middleware stack with positions
|
|
652
|
+
bundle exec rake middleware
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
4. Ensure `GITLAB_TRACING` is set in your environment (not just in `.env` files that might not be loaded)
|
|
656
|
+
|
|
657
|
+
### Missing Spans
|
|
658
|
+
|
|
659
|
+
1. Ensure instrumentation is called after dependencies are loaded
|
|
660
|
+
2. Verify tracing is properly initialized and enabled:
|
|
661
|
+
```ruby
|
|
662
|
+
Labkit::Tracing.enabled?
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Middleware Works But No Traces Appear
|
|
666
|
+
|
|
667
|
+
If your application runs without errors but you don't see traces in your backend (OTLP collector):
|
|
668
|
+
|
|
669
|
+
**1. Verify the tracer provider is initialized:**
|
|
670
|
+
|
|
671
|
+
```ruby
|
|
672
|
+
OpenTelemetry.tracer_provider.class
|
|
673
|
+
# Expected: OpenTelemetry::SDK::Trace::TracerProvider
|
|
674
|
+
# Problem: OpenTelemetry::Internal::ProxyTracerProvider (auto-initialization failed)
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**2. Check for auto-initialization warnings in logs:**
|
|
678
|
+
|
|
679
|
+
Auto-initialization warnings indicate what went wrong:
|
|
680
|
+
```
|
|
681
|
+
Labkit::Tracing auto-initialization failed: <error message>
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
**3. Common causes:**
|
|
685
|
+
|
|
686
|
+
- `GITLAB_TRACING` environment variable not set or not visible to the process
|
|
687
|
+
- Connection string format error (should start with `otlp://`)
|
|
688
|
+
- OpenTelemetry gem dependency issues
|
|
689
|
+
- OTLP collector not reachable
|
|
690
|
+
|
|
691
|
+
**4. Test with console exporter:**
|
|
692
|
+
|
|
693
|
+
Use the console exporter to verify tracing works locally:
|
|
694
|
+
```bash
|
|
695
|
+
export GITLAB_TRACING="otlp://console?service_name=test&sampler=const&sampler_param=1"
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
You should see trace output in your application logs.
|
|
699
|
+
|
|
700
|
+
### High Overhead
|
|
701
|
+
|
|
702
|
+
1. Reduce sampling rate:
|
|
703
|
+
```bash
|
|
704
|
+
export GITLAB_TRACING="otlp://localhost:4318?sampler=probabilistic&sampler_param=0.001"
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
2. Disable stack traces or limit to specific operations:
|
|
708
|
+
```bash
|
|
709
|
+
export GITLAB_TRACING_INCLUDE_STACKTRACE=""
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
## References
|
|
713
|
+
|
|
714
|
+
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/languages/ruby/)
|
|
715
|
+
- [OpenTelemetry Protocol (OTLP) Specification](https://opentelemetry.io/docs/specs/otlp/)
|
|
716
|
+
- [Rails Instrumentation Guide](https://guides.rubyonrails.org/active_support_instrumentation.html)
|