Skip to content

Commit fb89e0f

Browse files
committed
feat: events
1 parent 1dd109b commit fb89e0f

11 files changed

+1298
-59
lines changed

src/Watchers/CacheWatcher.php

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,30 @@
99
use Illuminate\Cache\Events\KeyForgotten;
1010
use Illuminate\Cache\Events\KeyWritten;
1111
use Illuminate\Contracts\Foundation\Application;
12-
use OpenTelemetry\API\Trace\SpanKind;
13-
use OpenTelemetry\Context\Context;
1412
use Overtrue\LaravelOpenTelemetry\Facades\Measure;
15-
use Overtrue\LaravelOpenTelemetry\Support\SpanNameHelper;
1613

1714
class CacheWatcher extends Watcher
1815
{
1916
public function register(Application $app): void
2017
{
21-
$app['events']->listen(CacheHit::class, fn ($event) => $this->recordSpan('hit', $event));
22-
$app['events']->listen(CacheMissed::class, fn ($event) => $this->recordSpan('miss', $event));
23-
$app['events']->listen(KeyWritten::class, fn ($event) => $this->recordSpan('set', $event));
24-
$app['events']->listen(KeyForgotten::class, fn ($event) => $this->recordSpan('forget', $event));
18+
$app['events']->listen(CacheHit::class, fn ($event) => $this->recordEvent('cache.hit', $event));
19+
$app['events']->listen(CacheMissed::class, fn ($event) => $this->recordEvent('cache.miss', $event));
20+
$app['events']->listen(KeyWritten::class, fn ($event) => $this->recordEvent('cache.set', $event));
21+
$app['events']->listen(KeyForgotten::class, fn ($event) => $this->recordEvent('cache.forget', $event));
2522
}
2623

27-
protected function recordSpan(string $operation, object $event): void
24+
protected function recordEvent(string $eventName, object $event): void
2825
{
29-
$span = Measure::tracer()
30-
->spanBuilder(SpanNameHelper::cache($operation, $event->key))
31-
->setSpanKind(SpanKind::KIND_INTERNAL)
32-
->setParent(Context::getCurrent())
33-
->startSpan();
34-
3526
$attributes = [
3627
'cache.key' => $event->key,
37-
'cache.operation' => $operation,
3828
'cache.store' => $this->getStoreName($event),
3929
];
4030

4131
if ($event instanceof KeyWritten) {
4232
$attributes['cache.ttl'] = property_exists($event, 'seconds') ? $event->seconds : null;
4333
}
4434

45-
$span->setAttributes($attributes);
46-
$span->end();
35+
Measure::addEvent($eventName, $attributes);
4736
}
4837

4938
private function getStoreName(object $event): ?string

src/Watchers/EventWatcher.php

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ class EventWatcher extends Watcher
1818
* @var string[]
1919
*/
2020
protected array $eventsToSkip = [
21-
'Illuminate\Log\Events\MessageLogged',
22-
'Illuminate\Database\Events\QueryExecuted',
23-
'Illuminate\Cache\Events\CacheHit',
24-
'Illuminate\Cache\Events\CacheMissed',
25-
'Illuminate\Cache\Events\KeyWritten',
26-
'Illuminate\Cache\Events\KeyForgotten',
27-
'Illuminate\Queue\Events\JobProcessing',
28-
'Illuminate\Queue\Events\JobProcessed',
29-
'Illuminate\Queue\Events\JobFailed',
30-
'Illuminate\Auth\Events\Attempting',
31-
'Illuminate\Auth\Events\Authenticated',
32-
'Illuminate\Auth\Events\Login',
33-
'Illuminate\Auth\Events\Failed',
34-
'Illuminate\Auth\Events\Logout',
35-
'Illuminate\Redis\Events\CommandExecuted',
36-
'Illuminate\Http\Client\Events\RequestSending',
37-
'Illuminate\Http\Client\Events\ResponseReceived',
38-
'Illuminate\Http\Client\Events\ConnectionFailed',
21+
'Illuminate\\Log\\Events\\MessageLogged',
22+
'Illuminate\\Database\\Events\\QueryExecuted',
23+
'Illuminate\\Cache\\Events\\CacheHit',
24+
'Illuminate\\Cache\\Events\\CacheMissed',
25+
'Illuminate\\Cache\\Events\\KeyWritten',
26+
'Illuminate\\Cache\\Events\\KeyForgotten',
27+
'Illuminate\\Queue\\Events\\JobProcessing',
28+
'Illuminate\\Queue\\Events\\JobProcessed',
29+
'Illuminate\\Queue\\Events\\JobFailed',
30+
'Illuminate\\Auth\\Events\\Attempting',
31+
'Illuminate\\Auth\\Events\\Authenticated',
32+
'Illuminate\\Auth\\Events\\Login',
33+
'Illuminate\\Auth\\Events\\Failed',
34+
'Illuminate\\Auth\\Events\\Logout',
35+
'Illuminate\\Redis\\Events\\CommandExecuted',
36+
'Illuminate\\Http\\Client\\Events\\RequestSending',
37+
'Illuminate\\Http\\Client\\Events\\ResponseReceived',
38+
'Illuminate\\Http\\Client\\Events\\ConnectionFailed',
3939
];
4040

4141
public array $events = [
@@ -47,22 +47,22 @@ public function register(Application $app): void
4747
$app['events']->listen('*', [$this, 'recordEvent']);
4848
}
4949

50-
public function recordEvent($event): void
50+
public function recordEvent($eventName, $payload = []): void
5151
{
52-
if ($this->shouldSkip($event)) {
52+
if ($this->shouldSkip($eventName)) {
5353
return;
5454
}
5555

5656
$attributes = [
57-
'event.payload_count' => is_array($event) ? count($event) : 0,
57+
'event.payload_count' => is_array($payload) ? count($payload) : 0,
5858
];
5959

60-
$firstPayload = is_array($event) ? ($event[0] ?? null) : null;
60+
$firstPayload = is_array($payload) ? ($payload[0] ?? null) : null;
6161
if (is_object($firstPayload)) {
6262
$attributes['event.object_type'] = get_class($firstPayload);
6363
}
6464

65-
Measure::addEvent($event, $attributes);
65+
Measure::addEvent($eventName, $attributes);
6666
}
6767

6868
protected function shouldSkip(string $eventName): bool
@@ -71,6 +71,6 @@ protected function shouldSkip(string $eventName): bool
7171
return true;
7272
}
7373

74-
return array_any($this->eventsToSkip, fn ($eventToSkip) => fnmatch($eventToSkip, $eventName));
74+
return in_array($eventName, $this->eventsToSkip);
7575
}
7676
}

src/Watchers/QueueWatcher.php

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@
99
use Illuminate\Queue\Events\JobProcessed;
1010
use Illuminate\Queue\Events\JobProcessing;
1111
use Illuminate\Queue\Events\JobQueued;
12-
use OpenTelemetry\API\Trace\SpanKind;
13-
use OpenTelemetry\Context\Context;
1412
use OpenTelemetry\SemConv\TraceAttributes;
1513
use Overtrue\LaravelOpenTelemetry\Facades\Measure;
16-
use Overtrue\LaravelOpenTelemetry\Support\SpanNameHelper;
1714

1815
/**
1916
* Queue Watcher
@@ -34,12 +31,6 @@ public function recordJobQueued(JobQueued $event): void
3431
{
3532
$jobClass = is_object($event->job) ? get_class($event->job) : $event->job;
3633

37-
$span = Measure::tracer()
38-
->spanBuilder(SpanNameHelper::queue('publish', $jobClass))
39-
->setSpanKind(SpanKind::KIND_PRODUCER)
40-
->setParent(Context::getCurrent())
41-
->startSpan();
42-
4334
$attributes = [
4435
TraceAttributes::MESSAGING_SYSTEM => $event->connectionName,
4536
TraceAttributes::MESSAGING_DESTINATION_NAME => $event->queue,
@@ -51,21 +42,14 @@ public function recordJobQueued(JobQueued $event): void
5142
$attributes['messaging.job.delay_seconds'] = $event->job->delay;
5243
}
5344

54-
$span->setAttributes($attributes);
55-
$span->end();
45+
Measure::addEvent('queue.job.queued', $attributes);
5646
}
5747

5848
public function recordJobProcessing(JobProcessing $event): void
5949
{
6050
$payload = $event->job->payload();
6151
$jobClass = $payload['displayName'] ?? 'unknown';
6252

63-
$span = Measure::tracer()
64-
->spanBuilder(SpanNameHelper::queue('process', $jobClass))
65-
->setSpanKind(SpanKind::KIND_CONSUMER)
66-
->setParent(Context::getCurrent())
67-
->startSpan();
68-
6953
$attributes = [
7054
TraceAttributes::MESSAGING_SYSTEM => $event->connectionName,
7155
TraceAttributes::MESSAGING_DESTINATION_NAME => $event->job->getQueue(),
@@ -80,7 +64,7 @@ public function recordJobProcessing(JobProcessing $event): void
8064
$attributes['messaging.job.data_size'] = strlen(serialize($payload['data']));
8165
}
8266

83-
$span->setAttributes($attributes)->end();
67+
Measure::addEvent('queue.job.processing', $attributes);
8468
}
8569

8670
public function recordJobProcessed(JobProcessed $event): void
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
namespace Overtrue\LaravelOpenTelemetry\Tests\Watchers;
4+
5+
use Illuminate\Auth\Events\Attempting;
6+
use Illuminate\Auth\Events\Authenticated;
7+
use Illuminate\Auth\Events\Failed;
8+
use Illuminate\Auth\Events\Login;
9+
use Illuminate\Auth\Events\Logout;
10+
use Mockery;
11+
use OpenTelemetry\API\Trace\SpanBuilderInterface;
12+
use OpenTelemetry\API\Trace\SpanInterface;
13+
use OpenTelemetry\API\Trace\TracerInterface;
14+
use OpenTelemetry\SemConv\TraceAttributes;
15+
use Overtrue\LaravelOpenTelemetry\Facades\Measure;
16+
use Overtrue\LaravelOpenTelemetry\Tests\TestCase;
17+
use Overtrue\LaravelOpenTelemetry\Watchers\AuthenticateWatcher;
18+
19+
class AuthenticateWatcherTest extends TestCase
20+
{
21+
private AuthenticateWatcher $watcher;
22+
23+
protected function setUp(): void
24+
{
25+
parent::setUp();
26+
$this->watcher = new AuthenticateWatcher;
27+
}
28+
29+
public function test_registers_auth_event_listeners()
30+
{
31+
$events = Mockery::mock();
32+
$events->shouldReceive('listen')->with(Attempting::class, [$this->watcher, 'recordAttempting'])->once();
33+
$events->shouldReceive('listen')->with(Authenticated::class, [$this->watcher, 'recordAuthenticated'])->once();
34+
$events->shouldReceive('listen')->with(Login::class, [$this->watcher, 'recordLogin'])->once();
35+
$events->shouldReceive('listen')->with(Failed::class, [$this->watcher, 'recordFailed'])->once();
36+
$events->shouldReceive('listen')->with(Logout::class, [$this->watcher, 'recordLogout'])->once();
37+
38+
$app = Mockery::mock(\Illuminate\Contracts\Foundation\Application::class, \ArrayAccess::class);
39+
$app->shouldReceive('offsetGet')->with('events')->andReturn($events);
40+
41+
$this->watcher->register($app);
42+
}
43+
44+
public function test_records_attempting_event()
45+
{
46+
$event = new Attempting('web', ['email' => 'test@example.com'], false);
47+
48+
$span = Mockery::mock(SpanInterface::class);
49+
$span->shouldReceive('setAttributes')->with([
50+
'auth.guard' => 'web',
51+
'auth.credentials.count' => 1,
52+
'auth.remember' => false,
53+
])->andReturnSelf();
54+
$span->shouldReceive('end');
55+
56+
$spanBuilder = Mockery::mock(SpanBuilderInterface::class);
57+
$spanBuilder->shouldReceive('setSpanKind')->andReturnSelf();
58+
$spanBuilder->shouldReceive('setParent')->andReturnSelf();
59+
$spanBuilder->shouldReceive('startSpan')->andReturn($span);
60+
61+
$tracer = Mockery::mock(TracerInterface::class);
62+
$tracer->shouldReceive('spanBuilder')->with('AUTH ATTEMPTING')->andReturn($spanBuilder);
63+
64+
Measure::shouldReceive('tracer')->andReturn($tracer);
65+
66+
$this->watcher->recordAttempting($event);
67+
68+
// Assert that the test executed successfully
69+
$this->assertTrue(true);
70+
}
71+
72+
public function test_records_authenticated_event()
73+
{
74+
$user = Mockery::mock();
75+
$user->shouldReceive('getAuthIdentifier')->andReturn(123);
76+
77+
$event = new Authenticated('web', $user);
78+
79+
$span = Mockery::mock(SpanInterface::class);
80+
$span->shouldReceive('setAttributes')->with([
81+
'auth.guard' => 'web',
82+
TraceAttributes::ENDUSER_ID => 123,
83+
'auth.user.type' => get_class($user),
84+
])->andReturnSelf();
85+
$span->shouldReceive('end');
86+
87+
$spanBuilder = Mockery::mock(SpanBuilderInterface::class);
88+
$spanBuilder->shouldReceive('setSpanKind')->andReturnSelf();
89+
$spanBuilder->shouldReceive('setParent')->andReturnSelf();
90+
$spanBuilder->shouldReceive('startSpan')->andReturn($span);
91+
92+
$tracer = Mockery::mock(TracerInterface::class);
93+
$tracer->shouldReceive('spanBuilder')->with('AUTH AUTHENTICATED')->andReturn($spanBuilder);
94+
95+
Measure::shouldReceive('tracer')->andReturn($tracer);
96+
97+
$this->watcher->recordAuthenticated($event);
98+
99+
// Assert that the test executed successfully
100+
$this->assertTrue(true);
101+
}
102+
103+
public function test_records_login_event()
104+
{
105+
$user = Mockery::mock();
106+
$user->shouldReceive('getAuthIdentifier')->andReturn(456);
107+
108+
$event = new Login('web', $user, true);
109+
110+
$span = Mockery::mock(SpanInterface::class);
111+
$span->shouldReceive('setAttributes')->with([
112+
'auth.guard' => 'web',
113+
TraceAttributes::ENDUSER_ID => 456,
114+
'auth.user.type' => get_class($user),
115+
'auth.remember' => true,
116+
])->andReturnSelf();
117+
$span->shouldReceive('end');
118+
119+
$spanBuilder = Mockery::mock(SpanBuilderInterface::class);
120+
$spanBuilder->shouldReceive('setSpanKind')->andReturnSelf();
121+
$spanBuilder->shouldReceive('setParent')->andReturnSelf();
122+
$spanBuilder->shouldReceive('startSpan')->andReturn($span);
123+
124+
$tracer = Mockery::mock(TracerInterface::class);
125+
$tracer->shouldReceive('spanBuilder')->with('AUTH LOGIN')->andReturn($spanBuilder);
126+
127+
Measure::shouldReceive('tracer')->andReturn($tracer);
128+
129+
$this->watcher->recordLogin($event);
130+
131+
// Assert that the test executed successfully
132+
$this->assertTrue(true);
133+
}
134+
135+
public function test_records_failed_event()
136+
{
137+
$event = new Failed('web', null, ['email' => 'test@example.com']);
138+
139+
$span = Mockery::mock(SpanInterface::class);
140+
$span->shouldReceive('setAttributes')->with([
141+
'auth.guard' => 'web',
142+
'auth.credentials.count' => 1,
143+
TraceAttributes::ENDUSER_ID => null,
144+
])->andReturnSelf();
145+
$span->shouldReceive('end');
146+
147+
$spanBuilder = Mockery::mock(SpanBuilderInterface::class);
148+
$spanBuilder->shouldReceive('setSpanKind')->andReturnSelf();
149+
$spanBuilder->shouldReceive('setParent')->andReturnSelf();
150+
$spanBuilder->shouldReceive('startSpan')->andReturn($span);
151+
152+
$tracer = Mockery::mock(TracerInterface::class);
153+
$tracer->shouldReceive('spanBuilder')->with('AUTH FAILED')->andReturn($spanBuilder);
154+
155+
Measure::shouldReceive('tracer')->andReturn($tracer);
156+
157+
$this->watcher->recordFailed($event);
158+
159+
// Assert that the test executed successfully
160+
$this->assertTrue(true);
161+
}
162+
163+
public function test_records_logout_event()
164+
{
165+
$user = Mockery::mock();
166+
$user->shouldReceive('getAuthIdentifier')->andReturn(111);
167+
168+
$event = new Logout('web', $user);
169+
170+
$span = Mockery::mock(SpanInterface::class);
171+
$span->shouldReceive('setAttributes')->with([
172+
'auth.guard' => 'web',
173+
TraceAttributes::ENDUSER_ID => 111,
174+
'auth.user.type' => get_class($user),
175+
])->andReturnSelf();
176+
$span->shouldReceive('end');
177+
178+
$spanBuilder = Mockery::mock(SpanBuilderInterface::class);
179+
$spanBuilder->shouldReceive('setSpanKind')->andReturnSelf();
180+
$spanBuilder->shouldReceive('setParent')->andReturnSelf();
181+
$spanBuilder->shouldReceive('startSpan')->andReturn($span);
182+
183+
$tracer = Mockery::mock(TracerInterface::class);
184+
$tracer->shouldReceive('spanBuilder')->with('AUTH LOGOUT')->andReturn($spanBuilder);
185+
186+
Measure::shouldReceive('tracer')->andReturn($tracer);
187+
188+
$this->watcher->recordLogout($event);
189+
190+
// Assert that the test executed successfully
191+
$this->assertTrue(true);
192+
}
193+
}

0 commit comments

Comments
 (0)