Laravel OpenTelemetry Integration
repository·main·Indexed 41 days ago
https://github.com/keepsuit/laravel-opentelemetryA package providing OpenTelemetry integration for Laravel applications to generate and export traces, metrics, and logs. It includes automatic instrumentation for HTTP requests, database queries, Redis, queues, cache, events, views, Livewire, and Scout, as well as support for head and tail sampling strategies.
What's inside Laravel OpenTelemetry
- This package provides a way to integrate OpenTelemetry into Laravel applications, allowing you to instrument your application to generate, collect, and export telemetry data such as metrics, logs, and traces to analyze performance and behavior.
Configure Tail Sampling for intelligent trace filtering
mainTail sampling allows you to make sampling decisions based on complete trace data (e.g., keeping traces that contain errors or are slow) rather than just the initial span.
Note: Application-level tail sampling is best suited for single-service scenarios. For multi-service environments, use an OpenTelemetry Collector.
Built-in Rules
ErrorsRule: Keeps traces containing error/exception spans.SlowTraceRule: Keeps traces exceeding a specificthreshold_ms.
Configuration
enabled: Boolean to enable/disable tail sampling.decision_wait: Maximum time (ms) to wait for the end of a trace before making a decision.rules: An array of rules to apply.
'tail_sampling' => [ 'enabled' => false, 'decision_wait' => 5000, 'rules' => [ TailSampling\Rules\ErrorsRule::class => true, TailSampling\Rules\SlowTraceRule::class => [ 'enabled' => true, 'threshold_ms' => 2000, ], ], ],Configure Trace Sampling (Head vs Tail)
mainThe package supports two sampling strategies to control which traces are exported:
Head Sampling
Decisions are made at the start of a span. It is fast and ideal for rate limiting or percentage-based sampling. Configure this in the
traces.samplersection of your config.Tail Sampling
Decisions are made after the trace completes, allowing you to keep traces based on full data (e.g., errors or slow duration).
Note: Tail sampling implemented at the application level is best for single-service scenarios. For multi-service environments, use an OpenTelemetry Collector.
Built-in Tail Sampling Rules
- Errors Rule: Keeps traces containing any span with an error status.
- Slow Trace Rule: Keeps traces exceeding a duration threshold (default 2000ms).
Tail Sampling Configuration Variables
Variable Description Default OTEL_TRACES_TAIL_SAMPLING_ENABLEDEnable tail sampling falseOTEL_TRACES_TAIL_SAMPLING_DECISION_WAITMax time to wait for trace completion (ms) 5000OTEL_TRACES_TAIL_SAMPLING_RULE_KEEP_ERRORSEnable Errors Rule trueOTEL_TRACES_TAIL_SAMPLING_RULE_SLOW_TRACESEnable Slow Trace Rule trueOTEL_TRACES_TAIL_SAMPLING_SLOW_TRACES_THRESHOLD_MSThreshold for Slow Trace Rule (ms) 2000Install laravel-opentelemetry via Composer
mainTo add OpenTelemetry integration to your Laravel application, install the package using Composer:
composer require keepsuit/laravel-opentelemetrySend logs to OpenTelemetry
mainYou can send logs to OpenTelemetry using two methods:
- Laravel Log Channels: Use the injected
otlpchannel in yourconfig/logging.php. This uses theOpenTelemetryMonologHandler. - Logger Facade: Use the
Loggerfacade to send logs directly to OpenTelemetry.
If you use the
otlplogs driver, trace IDs are automatically injected into the log context.// Using the Logger facade use Keepsuit\LaravelOpenTelemetry\Facades\Logger; Logger::error('my log message'); Logger::info('my log message');- Laravel Log Channels: Use the injected
Use the Makefile for development setup
mainThe project includes a
Makefileto simplify development using Docker. The containers are configured to mirror your host user's UID and GID to prevent file permission issues.Workflow Note: Before using
make shell, ensure the containers are already running by executingmake startin a separate terminal.# Build the Docker image with your UID/GID make build # Start containers in the background make start # Stop and remove containers make stop # Open a Bash shell in the 'app' container make shellTrace Console Commands
mainConsole commands are not traced by default. To trace them, add their signature or class name to the
commandsarray within theConsoleInstrumentationconfiguration.Disabling: Set
OTEL_INSTRUMENTATION_CONSOLE=falseor removeConsoleInstrumentation::class.// By signature 'instrumentation' => [ Instrumentation\ConsoleInstrumentation::class => [ 'enabled' => env('OTEL_INSTRUMENTATION_CONSOLE', true), 'commands' => [ 'app:example-command', ], ], ], // By class name 'instrumentation' => [ Instrumentation\ConsoleInstrumentation::class => [ 'enabled' => env('OTEL_INSTRUMENTATION_CONSOLE', true), 'commands' => [ App\Console\Commands\ExampleCommand::class, ], ], ],Publish the OpenTelemetry configuration file
mainAfter installing the package, publish the configuration file to your application's
configdirectory using the following Artisan command:php artisan vendor:publish --provider="Keepsuit\LaravelOpenTelemetry\LaravelOpenTelemetryServiceProvider" --tag="opentelemetry-config"Upgrade to v2.0: Breaking Changes
mainUpgrading to v2.0 involves several breaking changes in configuration, namespaces, and the Meter API. If you have a published configuration file, you must compare it with the new version and update it manually.
Configuration Changes
- Event Instrumentation: Rename
ignoredtoexcluded. - Console Instrumentation: Behavior is reversed. Instead of
excluded, usecommandsto specify which commands to include. By default, commands are not traced. - Logs Configuration: The
trace_id_fielddefault value changed fromtraceidtotrace_id. - HTTP Instrumentation: A new
sensitive_query_parametersoption is available for both server and client instrumentations to redact sensitive data.
Namespace Changes
If you import these classes directly, update your imports:
Keepsuit\LaravelOpenTelemetry\Support\InstrumentationUtilities$\rightarrow$Keepsuit\LaravelOpenTelemetry\Instrumentation\Support\InstrumentationUtilitiesKeepsuit\LaravelOpenTelemetry\Support\View\TracedViewEngine$\rightarrow$Keepsuit\LaravelOpenTelemetry\Instrumentation\Support\View\TracedViewEngineKeepsuit\LaravelOpenTelemetry\Support\HttpClient\GuzzleTraceMiddleware$\rightarrow$Keepsuit\LaravelOpenTelemetry\Instrumentation\Support\Http\Client\GuzzleTraceMiddlewareKeepsuit\LaravelOpenTelemetry\Support\HttpServer\TraceRequestMiddleware$\rightarrow$Keepsuit\LaravelOpenTelemetry\Instrumentation\Support\Http\Server\TraceRequestMiddleware
Exporter Behavior
- In v2.0, setting the exporter to
nulluses a no-op exporter that discards all data. - To use the in-memory exporter, set the exporter to
memory.
- Event Instrumentation: Rename
Enable Scout Instrumentation
mainv2.0 provides first-class support for tracing Laravel Scout search, update, and delete operations.
Requirements:
- Laravel Scout 10.23+
- OpenTelemetry PHP extension installed
Configuration: Enable it in your configuration array using the
ScoutInstrumentationclass.Instrumentation\ScoutInstrumentation::class => env('OTEL_INSTRUMENTATION_SCOUT', true),Install OpenTelemetry integration for Laravel
mainTo integrate OpenTelemetry into your Laravel application, install the
keepsuit/laravel-opentelemetrypackage via Composer.composer require keepsuit/laravel-opentelemetryConfigure Http Client instrumentation
mainHttp client requests are traced by default. You can switch to manual mode by setting
'manual' => truein the configuration. In manual mode, you must explicitly callwithTrace()on the request builder.Route Name Resolution: Because low-cardinality URL templates cannot be automatically detected for client requests, you can manually resolve them using
HttpClientInstrumentation::setRouteNameResolver()in your service provider.Metrics:
http.client.request.duration(histogram, seconds): Outgoing HTTP request duration.
Disabling: Set
OTEL_INSTRUMENTATION_HTTP_CLIENT=falseor removeHttpClientInstrumentation::classfrom the config.// Manual mode usage Http::withTrace()->get('https://example.com'); // Custom route name resolver use Keepsuit\LaravelOpenTelemetry\Instrumentation\HttpClientInstrumentation; use Psr\Http\Message\RequestInterface; public function boot(): void { HttpClientInstrumentation::setRouteNameResolver(function (RequestInterface $request): ?string { return match (true) { str_starts_with($request->getUri()->getPath(), '/products/') => '/products/{id}', default => null, }; }); }