diff --git a/composer.json b/composer.json index 54819f2..5b8f0d0 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ }, "require": { "php": "^8.2", - "cosmastech/statsd-client-adapter": "^0.0.2", + "cosmastech/statsd-client-adapter": "^0.1.1", "illuminate/support": "^10.0|^11.0", "illuminate/contracts": "^10.0|^11.0" }, diff --git a/config/statsd-adapter.php b/config/statsd-adapter.php index 8674d17..98b794a 100644 --- a/config/statsd-adapter.php +++ b/config/statsd-adapter.php @@ -3,6 +3,11 @@ return [ 'default' => env("STATSD_ADAPTER_DEFAULT", "memory"), + /** + * These are tags which should be added to every outgoing stat. + */ + "default_tags" => [], + /** * You may name your channel anything you wish. Valid drivers are: * memory diff --git a/src/AdapterManager.php b/src/AdapterManager.php index f61f31e..29ac00d 100644 --- a/src/AdapterManager.php +++ b/src/AdapterManager.php @@ -7,14 +7,19 @@ use Cosmastech\StatsDClientAdapter\Adapters\Datadog\DatadogStatsDClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\InMemory\InMemoryClientAdapter; use Cosmastech\StatsDClientAdapter\Adapters\League\LeagueStatsDClientAdapter; +use Cosmastech\StatsDClientAdapter\Adapters\StatsDClientAdapter; use Cosmastech\StatsDClientAdapter\Clients\Datadog\DatadogLoggingClient; use Cosmastech\StatsDClientAdapter\Utility\SampleRateDecider\SampleRateSendDecider; use DataDog\DogStatsd; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Support\MultipleInstanceManager; use League\StatsD\Client; use League\StatsD\Exception\ConfigurationException; /** + * @property array $instances + * @method StatsDClientAdapter instance($name = null) + * * @mixin \Cosmastech\StatsDClientAdapter\Adapters\StatsDClientAdapter */ class AdapterManager extends MultipleInstanceManager @@ -30,6 +35,11 @@ class AdapterManager extends MultipleInstanceManager */ protected $config; + /** + * @var array + */ + protected array $defaultTags; + /** * @inheritDoc */ @@ -64,6 +74,35 @@ public function getInstanceConfig($name) return $this->config->get("statsd-adapter.channels.{$name}"); } + /** + * @param array $tags + * @return void + */ + public function setDefaultTags(array $tags): void + { + $this->defaultTags = $tags; + + foreach ($this->instances as $instance) { + $instance->setDefaultTags($this->defaultTags); + } + } + + /** + * @return array + */ + public function getDefaultTags(): array + { + return $this->defaultTags ?? $this->getDefaultTagsFromConfig(); + } + + /** + * @return array + */ + protected function getDefaultTagsFromConfig(): array + { + return $this->config->get('statsd-adapter.default_tags', []); + } + /** * @param array $config * @return InMemoryClientAdapter @@ -72,12 +111,14 @@ protected function createMemoryAdapter(array $config): InMemoryClientAdapter { $wrapperClock = new WrapperClock(FactoryImmutable::getDefaultInstance()); - return new InMemoryClientAdapter($wrapperClock); + return new InMemoryClientAdapter($wrapperClock, $this->getDefaultTags()); } /** * @param array $config * @return DatadogStatsDClientAdapter + * + * @throws BindingResolutionException */ protected function createLog_datadogAdapter(array $config): DatadogStatsDClientAdapter { @@ -87,14 +128,16 @@ protected function createLog_datadogAdapter(array $config): DatadogStatsDClientA /** * @param array $config * @return DatadogStatsDClientAdapter - * @throws \Illuminate\Contracts\Container\BindingResolutionException + * + * @throws BindingResolutionException */ protected function createLogDatadogAdapter(array $config): DatadogStatsDClientAdapter { $logLevel = $config['log_level'] ?? 'debug'; return new DatadogStatsDClientAdapter( - new DatadogLoggingClient($this->app->make('log'), $logLevel, $config) + new DatadogLoggingClient($this->app->make('log'), $logLevel, $config), + $this->getDefaultTags() ); } @@ -104,12 +147,13 @@ protected function createLogDatadogAdapter(array $config): DatadogStatsDClientAd */ protected function createDatadogAdapter(array $config): DatadogStatsDClientAdapter { - return new DatadogStatsDClientAdapter(new DogStatsd($config)); + return new DatadogStatsDClientAdapter(new DogStatsd($config), $this->getDefaultTags()); } /** - * @param array $config + * @param array $config * @return LeagueStatsDClientAdapter + * * @throws ConfigurationException */ protected function createLeagueAdapter(array $config): LeagueStatsDClientAdapter @@ -117,6 +161,10 @@ protected function createLeagueAdapter(array $config): LeagueStatsDClientAdapter $leagueClient = new Client($config['instance_id'] ?? null); $leagueClient->configure($config); - return new LeagueStatsDClientAdapter($leagueClient, new SampleRateSendDecider()); + return new LeagueStatsDClientAdapter( + $leagueClient, + new SampleRateSendDecider(), + $this->getDefaultTags() + ); } } diff --git a/src/Stats.php b/src/Stats.php index a05518f..6a5489f 100644 --- a/src/Stats.php +++ b/src/Stats.php @@ -8,6 +8,8 @@ * @method static void getDefaultInstance() * @method static void setDefaultInstance(string $name) * @method static array|null getInstanceConfig(string $name) + * @method static void setDefaultTags(array $tags) + * @method static array getDefaultTags() * @method static mixed instance(string|null $name = null) * @method static \Cosmastech\LaravelStatsDAdapter\AdapterManager forgetInstance(array|string|null $name = null) * @method static void purge(string|null $name = null) diff --git a/src/StatsDAdapterServiceProvider.php b/src/StatsDAdapterServiceProvider.php index 5e3cc83..db22939 100644 --- a/src/StatsDAdapterServiceProvider.php +++ b/src/StatsDAdapterServiceProvider.php @@ -3,21 +3,22 @@ namespace Cosmastech\LaravelStatsDAdapter; use Cosmastech\StatsDClientAdapter\Adapters\StatsDClientAdapter; +use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider; class StatsDAdapterServiceProvider extends ServiceProvider implements DeferrableProvider { - public function register() + public function register(): void { $this->app->singleton( AdapterManager::class, - fn ($app) => new AdapterManager($app) + fn (Application $app) => new AdapterManager($app), ); - $this->app->singleton( + $this->app->bind( StatsDClientAdapter::class, - fn ($app) => $app->make(AdapterManager::class)->instance() + fn (Application $app) => $app->make(AdapterManager::class)->instance() ); } diff --git a/tests/AdapterManagerTest.php b/tests/AdapterManagerTest.php index ef7fe2f..1ba6a09 100644 --- a/tests/AdapterManagerTest.php +++ b/tests/AdapterManagerTest.php @@ -42,7 +42,7 @@ public function setDefaultInstance_overridesConfigDefault(): void } #[Test] - public function memoryAdapter_instance_returnsInMemoryClientAdapter(): void + public function instance_memoryAdapter_returnsInMemoryClientAdapter(): void { // Given $adapterManager = $this->createAdapterManager(); @@ -59,7 +59,7 @@ public function memoryAdapter_instance_returnsInMemoryClientAdapter(): void } #[Test] - public function logDatadog_instance_returnsConfiguredDatadogClient(): void + public function instance_logDatadog_returnsConfiguredDatadogClient(): void { // Given $adapterManager = $this->createAdapterManager(); @@ -74,7 +74,7 @@ public function logDatadog_instance_returnsConfiguredDatadogClient(): void self::assertInstanceOf(DatadogLoggingClient::class, $datadogClientAdapter->getClient()); } - public function league_instance_returnsConfiguredLeagueStatsDClient(): void + public function instance_league_returnsConfiguredLeagueStatsDClient(): void { // Given $adapterManager = $this->createAdapterManager(); @@ -89,4 +89,59 @@ public function league_instance_returnsConfiguredLeagueStatsDClient(): void self::assertInstanceOf(LeagueStatsDClientAdapter::class, $leagueStatsDClientAdapter); self::assertInstanceOf(StatsDClient::class, $leagueStatsDClientAdapter->getClient()); } + + #[Test] + public function getDefaultTags_withNoDefaultTagsSet_returnsEmptyArray(): void + { + // Given + $adapterManager = $this->createAdapterManager(); + + // Then + self::assertEquals([], $adapterManager->getDefaultTags()); + } + + #[Test] + public function getDefaultTags_withDefaultTagsSet_returnsDefaultTagsArray(): void + { + // Given + $adapterManager = $this->createAdapterManager(); + + // When + $adapterManager->setDefaultTags(['abc' => true]); + + // Then + self::assertSame(["abc" => true], $adapterManager->getDefaultTags()); + } + + #[Test] + public function setDefaultTags_passesTagsToExistingInstance(): void + { + // Given + $adapterManager = $this->createAdapterManager(); + + /** @var InMemoryClientAdapter $inMemoryClientAdapter */ + $inMemoryClientAdapter = $adapterManager->instance("memory"); + + // When + $adapterManager->setDefaultTags(["abc" => "hello"]); + + // Then + self::assertSame(["abc" => "hello"], $inMemoryClientAdapter->getDefaultTags()); + } + + #[Test] + public function setDefaultTags_passesToNewInstance(): void + { + // Given + $adapterManager = $this->createAdapterManager(); + + // And an instance is created + $adapterManager->instance("log_datadog"); + + // When + $adapterManager->setDefaultTags(["abc" => "123"]); + + // Then + self::assertEquals(["abc" => "123"], $adapterManager->instance("memory")->getDefaultTags()); + } } diff --git a/tests/StatsDAdapterServiceProviderTest.php b/tests/StatsDAdapterServiceProviderTest.php index a703a1e..2186a49 100644 --- a/tests/StatsDAdapterServiceProviderTest.php +++ b/tests/StatsDAdapterServiceProviderTest.php @@ -41,4 +41,22 @@ public function makeStatsDClientAdapter_returnsSingleton(): void // And self::assertSame($clientAdapter, $this->app->make(StatsDClientAdapter::class)); } + + #[Test] + public function passesDefaultTagsToAdapterManager(): void + { + // Given + $defaultTags = [ + "my_first_tag" => 1, + "my_second_tag" => 2, + "my_third_tag" => 3.0, + ]; + Config::set("statsd-adapter.default_tags", $defaultTags); + + // When + $clientAdapter = $this->app->make(StatsDClientAdapter::class); + + // Then + self::assertEqualsCanonicalizing($defaultTags, $clientAdapter->getDefaultTags()); + } }