Skip to content

Commit 732b96c

Browse files
author
Fumio Nakajima
committed
Add selective instrumentation configuration
Support OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS and OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS environment variables to enable/disable specific instrumentations (http, console, queue, eloquent, cache, db, etc.)
1 parent 4d13696 commit 732b96c

File tree

5 files changed

+389
-17
lines changed

5 files changed

+389
-17
lines changed

src/Instrumentation/Laravel/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,55 @@ The extension can be disabled via [runtime configuration](https://opentelemetry.
2222
```shell
2323
OTEL_PHP_DISABLED_INSTRUMENTATIONS=laravel
2424
```
25+
26+
### Selective Instrumentation
27+
28+
You can selectively enable or disable specific instrumentations using the following environment variables:
29+
30+
| Environment Variable | Description |
31+
|---------------------|-------------|
32+
| `OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS` | Comma-separated list of instrumentations to enable (only these will be active) |
33+
| `OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS` | Comma-separated list of instrumentations to disable |
34+
35+
#### Available Instrumentation Names
36+
37+
| Name | Description |
38+
|------|-------------|
39+
| `http` | HTTP request/response handling |
40+
| `console` | Artisan CLI commands |
41+
| `queue` | Queue jobs (push, process, worker) |
42+
| `eloquent` | Eloquent ORM operations |
43+
| `serve` | Local development server (`php artisan serve`) |
44+
| `cache` | Cache events (hit, miss, write, forget) |
45+
| `db` | Database queries |
46+
| `http-client` | HTTP client requests (external API calls) |
47+
| `exception` | Exception recording |
48+
| `log` | Log messages |
49+
| `redis` | Redis commands |
50+
51+
#### Group Aliases
52+
53+
| Alias | Expands To |
54+
|-------|------------|
55+
| `all` | All instrumentations |
56+
| `watchers` | `cache`, `db`, `http-client`, `exception`, `log`, `redis` |
57+
58+
#### Priority
59+
60+
- If neither variable is set: all instrumentations are enabled (default behavior)
61+
- If only `OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS` is set: only specified instrumentations are enabled
62+
- If only `OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS` is set: specified instrumentations are disabled
63+
- If both are set: `OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS` takes priority (disabled items are removed from enabled list)
64+
65+
#### Examples
66+
67+
```shell
68+
# Enable only HTTP and Queue instrumentation
69+
OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS=http,queue
70+
71+
# Disable only Redis and Log (all others remain enabled)
72+
OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS=redis,log
73+
74+
# Enable only watchers (cache, db, http-client, exception, log, redis)
75+
OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS=watchers
76+
```

src/Instrumentation/Laravel/src/Hooks/Illuminate/Foundation/Application.php

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Foundation\Application as FoundationalApplication;
99
use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHook;
1010
use OpenTelemetry\Contrib\Instrumentation\Laravel\Hooks\LaravelHookTrait;
11+
use OpenTelemetry\Contrib\Instrumentation\Laravel\InstrumentationConfig;
1112
use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\CacheWatcher;
1213
use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\ClientRequestWatcher;
1314
use OpenTelemetry\Contrib\Instrumentation\Laravel\Watchers\ExceptionWatcher;
@@ -24,17 +25,31 @@ class Application implements LaravelHook
2425

2526
public function instrument(): void
2627
{
28+
$config = InstrumentationConfig::getInstance();
29+
2730
/** @psalm-suppress UnusedFunctionCall */
2831
hook(
2932
FoundationalApplication::class,
3033
'__construct',
31-
post: function (FoundationalApplication $application, array $_params, mixed $_returnValue, ?Throwable $_exception) {
32-
$this->registerWatchers($application, new CacheWatcher());
33-
$this->registerWatchers($application, new ClientRequestWatcher($this->instrumentation));
34-
$this->registerWatchers($application, new ExceptionWatcher());
35-
$this->registerWatchers($application, new LogWatcher($this->instrumentation));
36-
$this->registerWatchers($application, new QueryWatcher($this->instrumentation));
37-
$this->registerWatchers($application, new RedisCommandWatcher($this->instrumentation));
34+
post: function (FoundationalApplication $application, array $_params, mixed $_returnValue, ?Throwable $_exception) use ($config) {
35+
if ($config->isInstrumentationEnabled(InstrumentationConfig::CACHE)) {
36+
$this->registerWatchers($application, new CacheWatcher());
37+
}
38+
if ($config->isInstrumentationEnabled(InstrumentationConfig::HTTP_CLIENT)) {
39+
$this->registerWatchers($application, new ClientRequestWatcher($this->instrumentation));
40+
}
41+
if ($config->isInstrumentationEnabled(InstrumentationConfig::EXCEPTION)) {
42+
$this->registerWatchers($application, new ExceptionWatcher());
43+
}
44+
if ($config->isInstrumentationEnabled(InstrumentationConfig::LOG)) {
45+
$this->registerWatchers($application, new LogWatcher($this->instrumentation));
46+
}
47+
if ($config->isInstrumentationEnabled(InstrumentationConfig::DB)) {
48+
$this->registerWatchers($application, new QueryWatcher($this->instrumentation));
49+
}
50+
if ($config->isInstrumentationEnabled(InstrumentationConfig::REDIS)) {
51+
$this->registerWatchers($application, new RedisCommandWatcher($this->instrumentation));
52+
}
3853
},
3954
);
4055
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Contrib\Instrumentation\Laravel;
6+
7+
use OpenTelemetry\SDK\Common\Configuration\Configuration;
8+
9+
final class InstrumentationConfig
10+
{
11+
public const HTTP = 'http';
12+
public const CONSOLE = 'console';
13+
public const QUEUE = 'queue';
14+
public const ELOQUENT = 'eloquent';
15+
public const SERVE = 'serve';
16+
public const CACHE = 'cache';
17+
public const DB = 'db';
18+
public const HTTP_CLIENT = 'http-client';
19+
public const EXCEPTION = 'exception';
20+
public const LOG = 'log';
21+
public const REDIS = 'redis';
22+
23+
private const GROUP_WATCHERS = [
24+
self::CACHE,
25+
self::DB,
26+
self::HTTP_CLIENT,
27+
self::EXCEPTION,
28+
self::LOG,
29+
self::REDIS,
30+
];
31+
32+
private const ALL_INSTRUMENTATIONS = [
33+
self::HTTP,
34+
self::CONSOLE,
35+
self::QUEUE,
36+
self::ELOQUENT,
37+
self::SERVE,
38+
self::CACHE,
39+
self::DB,
40+
self::HTTP_CLIENT,
41+
self::EXCEPTION,
42+
self::LOG,
43+
self::REDIS,
44+
];
45+
46+
private const ALIASES = [
47+
'all' => self::ALL_INSTRUMENTATIONS,
48+
'watchers' => self::GROUP_WATCHERS,
49+
];
50+
51+
private const ENV_ENABLED = 'OTEL_LARAVEL_ENABLED_INSTRUMENTATIONS';
52+
private const ENV_DISABLED = 'OTEL_LARAVEL_DISABLED_INSTRUMENTATIONS';
53+
54+
private static ?self $instance = null;
55+
56+
/** @var array<string> */
57+
private array $enabledInstrumentations;
58+
59+
private function __construct()
60+
{
61+
$this->enabledInstrumentations = $this->resolveEnabledInstrumentations();
62+
}
63+
64+
public static function getInstance(): self
65+
{
66+
return self::$instance ??= new self();
67+
}
68+
69+
public function isInstrumentationEnabled(string $name): bool
70+
{
71+
return in_array($name, $this->enabledInstrumentations, true);
72+
}
73+
74+
public function hasAnyWatcherEnabled(): bool
75+
{
76+
return !empty(array_intersect(self::GROUP_WATCHERS, $this->enabledInstrumentations));
77+
}
78+
79+
/**
80+
* @return array<string>
81+
*/
82+
private function resolveEnabledInstrumentations(): array
83+
{
84+
$enabled = $this->expandAliases($this->getConfigList(self::ENV_ENABLED));
85+
$disabled = $this->expandAliases($this->getConfigList(self::ENV_DISABLED));
86+
87+
return array_values(array_diff($enabled ?: self::ALL_INSTRUMENTATIONS, $disabled));
88+
}
89+
90+
/**
91+
* @param array<string> $names
92+
* @return array<string>
93+
*/
94+
private function expandAliases(array $names): array
95+
{
96+
$result = [];
97+
foreach ($names as $name) {
98+
$name = trim($name);
99+
$result = array_merge($result, self::ALIASES[$name] ?? ($name !== '' ? [$name] : []));
100+
}
101+
102+
return array_unique($result);
103+
}
104+
105+
/**
106+
* @return array<string>
107+
*/
108+
private function getConfigList(string $key): array
109+
{
110+
if (class_exists(Configuration::class)) {
111+
return Configuration::getList($key, []);
112+
}
113+
114+
$value = $_ENV[$key] ?? getenv($key);
115+
116+
return ($value !== false && $value !== '') ? explode(',', $value) : [];
117+
}
118+
119+
/**
120+
* Reset the singleton instance (for testing purposes).
121+
*/
122+
public static function reset(): void
123+
{
124+
self::$instance = null;
125+
}
126+
}

src/Instrumentation/Laravel/src/LaravelInstrumentation.php

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,35 @@ public static function register(): void
2020
'https://opentelemetry.io/schemas/1.32.0',
2121
);
2222

23-
Hooks\Illuminate\Console\Command::hook($instrumentation);
24-
Hooks\Illuminate\Contracts\Console\Kernel::hook($instrumentation);
25-
Hooks\Illuminate\Contracts\Http\Kernel::hook($instrumentation);
26-
Hooks\Illuminate\Contracts\Queue\Queue::hook($instrumentation);
27-
Hooks\Illuminate\Foundation\Application::hook($instrumentation);
28-
Hooks\Illuminate\Foundation\Console\ServeCommand::hook($instrumentation);
29-
Hooks\Illuminate\Queue\SyncQueue::hook($instrumentation);
30-
Hooks\Illuminate\Queue\Queue::hook($instrumentation);
31-
Hooks\Illuminate\Queue\Worker::hook($instrumentation);
32-
Hooks\Illuminate\Database\Eloquent\Model::hook($instrumentation);
23+
$config = InstrumentationConfig::getInstance();
24+
25+
if ($config->isInstrumentationEnabled(InstrumentationConfig::HTTP)) {
26+
Hooks\Illuminate\Contracts\Http\Kernel::hook($instrumentation);
27+
}
28+
29+
if ($config->isInstrumentationEnabled(InstrumentationConfig::CONSOLE)) {
30+
Hooks\Illuminate\Console\Command::hook($instrumentation);
31+
Hooks\Illuminate\Contracts\Console\Kernel::hook($instrumentation);
32+
}
33+
34+
if ($config->isInstrumentationEnabled(InstrumentationConfig::QUEUE)) {
35+
Hooks\Illuminate\Contracts\Queue\Queue::hook($instrumentation);
36+
Hooks\Illuminate\Queue\SyncQueue::hook($instrumentation);
37+
Hooks\Illuminate\Queue\Queue::hook($instrumentation);
38+
Hooks\Illuminate\Queue\Worker::hook($instrumentation);
39+
}
40+
41+
if ($config->isInstrumentationEnabled(InstrumentationConfig::ELOQUENT)) {
42+
Hooks\Illuminate\Database\Eloquent\Model::hook($instrumentation);
43+
}
44+
45+
if ($config->isInstrumentationEnabled(InstrumentationConfig::SERVE)) {
46+
Hooks\Illuminate\Foundation\Console\ServeCommand::hook($instrumentation);
47+
}
48+
49+
if ($config->hasAnyWatcherEnabled()) {
50+
Hooks\Illuminate\Foundation\Application::hook($instrumentation);
51+
}
3352
}
3453

3554
public static function shouldTraceCli(): bool

0 commit comments

Comments
 (0)