The `Labkit::Tracing` module provides distributed tracing functionality for Ruby applications using the OpenTracing or OpenTelemetry standards. It enables you to trace requests across multiple services and components, helping you understand application performance and debug issues in distributed systems.
## Overview
Distributed tracing allows you to track requests as they flow through your application and external services. The tracing module integrates with OpenTelemetry (OTLP) and Jaeger (OpenTracing) backends, and provides automatic instrumentation for:
Labkit-Ruby supports both OpenTracing and OpenTelemetry protocols. The library automatically detects which protocol to use based on the `GITLAB_TRACING` connection string and provides a unified API that works seamlessly with both.
**Note:** OpenTracing is [archived and deprecated](https://opentracing.io/). OpenTelemetry is the recommended tracing protocol for new projects. Labkit-Ruby maintains OpenTracing support for backward compatibility.
### Protocol Selection
-**OpenTelemetry** (Recommended): Use `otlp://` prefix in connection string
-**OpenTracing** (Deprecated): Use `opentracing://` prefix in connection string
All public APIs (`Labkit::Tracing.with_tracing`, `Labkit::Tracing.sampled?`, etc.) work identically regardless of the protocol used.
## Configuration
Tracing is controlled via environment variables:
### Required Configuration
**`GITLAB_TRACING`** - Connection string for the tracing backend
Format: `opentracing://<driver>?<options>` or `otlp://<host:port>?<options>`
Example:
```bash
# OpenTelemetry with HTTP endpoint (Recommended)
export GITLAB_TRACING="otlp://localhost:4318"
# Jaeger with UDP endpoint (OpenTracing - Deprecated)
Setting `GITLAB_TRACING` alone is not sufficient to collect traces. You must explicitly call `Labkit::Tracing::Factory.create_tracer` in your application's initialization code (see [Creating a Tracer](#creating-a-tracer) section below).
Without calling `Factory.create_tracer`:
- Your application runs safely without errors
- Instrumentation middleware creates span objects
-**No trace data is exported** to your backend (OTLP collector or Jaeger)
- A no-op tracer is used internally
This applies to both OpenTelemetry (`otlp://...`) and OpenTracing (`opentracing://jaeger...`) connection strings.
### Optional Configuration
**`GITLAB_TRACING_URL`** - Template URL for linking to trace views
Supports placeholders:
-`{{ correlation_id }}` - Current correlation ID
-`{{ service }}` - Service name
Example:
```bash
export GITLAB_TRACING_URL="https://jaeger.example.com/trace/{{ correlation_id }}?service={{ service }}"
```
**`GITLAB_TRACING_INCLUDE_STACKTRACE`** - Comma-separated list of operation name prefixes to include stack traces
Setting the `GITLAB_TRACING` environment variable enables tracing functionality, but **you must explicitly initialize the tracer** to collect and export traces:
#### Basic Initialization
The simplest initialization uses GITLAB_TRACING configuration only:
```ruby
# In your application initializer (e.g., config/initializers/tracing.rb)
ifLabkit::Tracing.enabled?
# REQUIRED: Create the tracer to actually collect traces
c.use_all()# Recommended - enables all available instrumentation
# Or selective instrumentation
c.use'OpenTelemetry::Instrumentation::Rails'
c.use'OpenTelemetry::Instrumentation::Sidekiq'
# Add custom span processors
c.add_span_processor(MyCustomProcessor.new)
# Add additional resource attributes
c.resource=c.resource.merge(
OpenTelemetry::SDK::Resources::Resource.create(
'deployment.environment'=>Rails.env,
'service.version'=>MyApp::VERSION
)
)
end
end
```
**Benefits:**
- Single initialization point for all tracing configuration
- GITLAB_TRACING settings (sampler, exporter, endpoints) are preserved
- Full access to OpenTelemetry SDK features (instrumentation, span processors, resources)
- Configuration block is optional
**Note:** Configuration blocks only work with OpenTelemetry connections (`otlp://`). They are ignored for OpenTracing connections (`opentracing://`) with a warning.
**What happens without initialization:**
-**OpenTelemetry (OTLP):** Falls back to a no-op tracer (the default `ProxyTracerProvider` is detected and skipped)
-**OpenTracing (Jaeger):** Uses the default no-op tracer
In both cases, spans are created but not exported, allowing your application to run safely while producing no trace data.
### Manual Span Creation
Use `Labkit::Tracing.with_tracing` to create custom spans:
```ruby
Labkit::Tracing.with_tracing(
operation_name: "process_data",
tags: {"user_id"=>user.id,"data_size"=>data.size}
)do|span|
# Your code here
result=process_data(data)
# Add additional tags during execution
span.set_tag("result_count",result.count)
result
end
```
With parent span context:
```ruby
# Get the current active span (works with both OpenTracing and OpenTelemetry)
- Override service_name if explicitly set in the block
**Important:** Calling `Factory.create_tracer` multiple times will reconfigure the global OpenTelemetry tracer provider each time. The last call wins. This is generally safe but **should be avoided** - initialize tracing once during application startup.
2.**No code changes required** - All Labkit tracing APIs (`Labkit::Tracing.with_tracing`, `Labkit::Tracing.sampled?`, etc.) work identically with both protocols.
That's it! The migration is transparent thanks to Labkit's protocol-agnostic API.
## Deprecations
-**OpenTracing Protocol** - The OpenTracing project is archived and deprecated. Use OpenTelemetry (`otlp://` connection string) for new projects. OpenTracing support (`opentracing://` connection string) is maintained for backward compatibility during migration.
-**`Labkit::Tracing::GRPCInterceptor`** - Deprecated, use `Labkit::Tracing::GRPC::ClientInterceptor` instead
## Example: Complete Setup
### With Automatic Instrumentation (Recommended for OpenTelemetry)
Without calling `Factory.create_tracer`, LabKit uses a no-op tracer that creates span objects for instrumentation but doesn't export them to any backend. This design prevents crashes when tracing is misconfigured, but means no trace data is collected.