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.
@@ -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)