diff --git a/config/statsd-adapter.php b/config/statsd-adapter.php index 30b1a24..e9cc6f3 100644 --- a/config/statsd-adapter.php +++ b/config/statsd-adapter.php @@ -14,11 +14,15 @@ * league * datadog * log_datadog + * event */ "channels" => [ "memory" => [ "adapter" => "memory", ], + "event" => [ + "adapter" => "event", + ], "league" => [ // see configuration options: https://github.com/thephpleague/statsd?tab=readme-ov-file#configuring "adapter" => "league", diff --git a/src/AdapterManager.php b/src/AdapterManager.php index e59ba1a..018bf0d 100644 --- a/src/AdapterManager.php +++ b/src/AdapterManager.php @@ -4,6 +4,8 @@ use Carbon\FactoryImmutable; use Carbon\WrapperClock; +use Cosmastech\LaravelStatsDAdapter\Adapters\EventDispatchingAdapter; +use Cosmastech\LaravelStatsDAdapter\Adapters\EventDispatchingStatsRecord; use Cosmastech\StatsDClientAdapter\Adapters\Datadog\DatadogStatsDClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\InMemory\InMemoryClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\League\LeagueStatsDClientAdapter; @@ -180,6 +182,20 @@ protected function createLeagueAdapter(array $config): LeagueStatsDClientAdapter ); } + /** + * @param array $config + * @return EventDispatchingAdapter + * @throws BindingResolutionException + */ + protected function createEventAdapter(array $config): EventDispatchingAdapter + { + return new EventDispatchingAdapter( + $this->getDefaultTags(), + new EventDispatchingStatsRecord($this->app->make('events')), + clock: $this->getClockImplementation() + ); + } + protected function getClockImplementation(): ClockInterface { return new WrapperClock(FactoryImmutable::getDefaultInstance()); diff --git a/src/Adapters/EventDispatchingAdapter.php b/src/Adapters/EventDispatchingAdapter.php new file mode 100644 index 0000000..d274c27 --- /dev/null +++ b/src/Adapters/EventDispatchingAdapter.php @@ -0,0 +1,9 @@ +dispatcher = $dispatcher; + + parent::__construct(); + } + + public function recordTiming(InMemoryTimingRecord $inMemoryTimingRecord): void + { + $this->dispatch(new TimingRecordedEvent($inMemoryTimingRecord)); + } + + public function recordCount(InMemoryCountRecord $inMemoryCountRecord): void + { + $this->dispatch(new CountRecordedEvent($inMemoryCountRecord)); + } + + public function recordGauge(InMemoryGaugeRecord $inMemoryGaugeRecord): void + { + $this->dispatch(new GaugeRecordedEvent($inMemoryGaugeRecord)); + } + + public function recordSet(InMemorySetRecord $inMemorySetRecord): void + { + $this->dispatch(new SetRecordedEvent($inMemorySetRecord)); + } + + public function recordHistogram(InMemoryHistogramRecord $inMemoryHistogramRecord): void + { + $this->dispatch(new HistogramRecordedEvent($inMemoryHistogramRecord)); + } + + public function recordDistribution(InMemoryDistributionRecord $inMemoryDistributionRecord): void + { + $this->dispatch(new DistributionRecordedEvent($inMemoryDistributionRecord)); + } + + protected function dispatch(StatRecordedEvent $statRecordedEvent): void + { + $this->dispatcher->dispatch($statRecordedEvent); + } +} diff --git a/src/Events/CountRecordedEvent.php b/src/Events/CountRecordedEvent.php new file mode 100644 index 0000000..c0506e0 --- /dev/null +++ b/src/Events/CountRecordedEvent.php @@ -0,0 +1,7 @@ + $app->make(AdapterManager::class)->instance() ); + + $this->app->singleton( + EventDispatchingStatsRecord::class, + function (Application $app): EventDispatchingStatsRecord { + return new EventDispatchingStatsRecord($app->make('events')); + } + ); } /** @@ -32,7 +40,7 @@ public function register(): void */ public function provides(): array { - return [AdapterManager::class, StatsDClientAdapter::class]; + return [AdapterManager::class, StatsDClientAdapter::class, EventDispatchingStatsRecord::class]; } protected function offerPublishing(): void diff --git a/tests/AdapterManagerTest.php b/tests/AdapterManagerTest.php index 1ba6a09..9043c27 100644 --- a/tests/AdapterManagerTest.php +++ b/tests/AdapterManagerTest.php @@ -2,6 +2,7 @@ namespace Cosmastech\LaravelStatsDAdapter\Tests; +use Cosmastech\LaravelStatsDAdapter\Adapters\EventDispatchingAdapter; use Cosmastech\StatsDClientAdapter\Adapters\Datadog\DatadogStatsDClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\InMemory\InMemoryClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\League\LeagueStatsDClientAdapter; @@ -144,4 +145,19 @@ public function setDefaultTags_passesToNewInstance(): void // Then self::assertEquals(["abc" => "123"], $adapterManager->instance("memory")->getDefaultTags()); } + + #[Test] + public function instance_event_returnsConfiguredEventDispatchingAdapter(): void + { + // Given + $adapterManager = $this->createAdapterManager(); + + // And events is configured + + // When + $eventDispatchingAdapter = $adapterManager->instance('event'); + + // Then + self::assertInstanceOf(EventDispatchingAdapter::class, $eventDispatchingAdapter); + } } diff --git a/tests/Adapters/EventDispatchingStatsRecordTest.php b/tests/Adapters/EventDispatchingStatsRecordTest.php new file mode 100644 index 0000000..30fe1fd --- /dev/null +++ b/tests/Adapters/EventDispatchingStatsRecordTest.php @@ -0,0 +1,62 @@ +dispatcher = Event::fake(); + + $this->eventDispatchingStatsRecord = new EventDispatchingStatsRecord($this->dispatcher); + } + + #[Test] + public function time_dispatchesTimingRecordedEvent(): void + { + // Given + $inMemoryClient = new InMemoryClientAdapter([], $this->eventDispatchingStatsRecord); + + // And + $closure = fn () => ["hello" => "world"]; + + // When + $output = $inMemoryClient->time($closure, "my-timing-stat", 0.92, [ + "my-tag" => "my-value", + ]); + + // Then output should be returned from the client + self::assertEquals(["hello" => "world"], $output); + + // And an event should be dispatched + /** @var Collection> $eventsCollection */ + $eventsCollection = $this->dispatcher->dispatched(TimingRecordedEvent::class); + self::assertCount(1, $eventsCollection); + self::assertCount(1, $eventsCollection->first()); + + /** @var TimingRecordedEvent $event */ + $event = $eventsCollection->first()[0]; + + // And the record should have expected properties + $record = $event->record; + self::assertInstanceOf(InMemoryTimingRecord::class, $record); + self::assertEquals("my-timing-stat", $record->stat); + self::assertEquals(0.92, $record->sampleRate); + self::assertEquals(["my-tag" => "my-value"], $record->tags); + } +} diff --git a/tests/StatsDAdapterServiceProviderTest.php b/tests/StatsDAdapterServiceProviderTest.php index 2186a49..518c214 100644 --- a/tests/StatsDAdapterServiceProviderTest.php +++ b/tests/StatsDAdapterServiceProviderTest.php @@ -2,6 +2,7 @@ namespace Cosmastech\LaravelStatsDAdapter\Tests; +use Cosmastech\LaravelStatsDAdapter\Adapters\EventDispatchingStatsRecord; use Cosmastech\StatsDClientAdapter\Adapters\Datadog\DatadogStatsDClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\InMemory\InMemoryClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\StatsDClientAdapter; @@ -59,4 +60,17 @@ public function passesDefaultTagsToAdapterManager(): void // Then self::assertEqualsCanonicalizing($defaultTags, $clientAdapter->getDefaultTags()); } + + #[Test] + public function eventDispatchingStatsRecord_isSingleton(): void + { + // Given + $firstRecord = $this->app->make(EventDispatchingStatsRecord::class); + + // When + $secondRecord = $this->app->make(EventDispatchingStatsRecord::class); + + // Then + self::assertSame($firstRecord, $secondRecord); + } }