From 088204b6024aa6e3f6ced995d413f9081ced5fe2 Mon Sep 17 00:00:00 2001 From: Mykhailo Shtanko Date: Fri, 10 May 2024 11:35:50 +0300 Subject: [PATCH] [MetricsPower] Added logstash processor --- .env.dist | 6 ++ Handler/MetricsHandler.php | 11 ++-- Handler/MetricsHandlerInterface.php | 5 +- Logger/Processor/LogstashProcessor.php | 64 +++++++++++++++++++ .../OptionsResolverLocatorException.php | 20 ++++++ OptionsResolver/OptionsResolverLocator.php | 31 +++++---- .../OptionsResolverLocatorInterface.php | 4 +- .../Resolver/PrometheusOptionsResolver.php | 10 ++- Tests/Unit/Handler/MetricsHandlerTest.php | 3 - .../Resolver/PrometheusOptionResolverTest.php | 4 +- 10 files changed, 128 insertions(+), 30 deletions(-) create mode 100644 Logger/Processor/LogstashProcessor.php create mode 100644 OptionsResolver/Exception/OptionsResolverLocatorException.php diff --git a/.env.dist b/.env.dist index 1af9ca1..628e72f 100644 --- a/.env.dist +++ b/.env.dist @@ -13,6 +13,12 @@ # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration +###> monolog ### +MONOLOG_LOG_LEVEL=debug +MONOLOG_LOG_BUBBLE=true +MONOLOG_SOURCE=default +###< monolog ### + ###> sentry ### SENTRY_DSN=https://KEY@INSTANCE.ingest.us.sentry.io/NAMESPACE SENTRY_ENVIRONMENT=${APP_ENV} diff --git a/Handler/MetricsHandler.php b/Handler/MetricsHandler.php index 426f0e7..087de9a 100644 --- a/Handler/MetricsHandler.php +++ b/Handler/MetricsHandler.php @@ -18,21 +18,20 @@ use FRZB\Component\DependencyInjection\Attribute\AsService; use FRZB\Component\MetricsPower\Helper\MetricalHelper; use FRZB\Component\MetricsPower\OptionsResolver\OptionsResolverLocatorInterface; -use Symfony\Component\Messenger\Event\AbstractWorkerMessageEvent; -use Symfony\Component\Messenger\Event\SendMessageToTransportsEvent; #[AsService] class MetricsHandler implements MetricsHandlerInterface { public function __construct( private readonly OptionsResolverLocatorInterface $locator, - ) { - } + ) {} - public function handle(AbstractWorkerMessageEvent|SendMessageToTransportsEvent $event): void + public function handle(object $event): void { foreach (MetricalHelper::getOptions($event->getEnvelope()->getMessage()) as $options) { - $this->locator->get($options)->resolve($event, $options); + if ($resolver = $this->locator->get($options)) { + (new \Fiber($resolver->resolve(...)))->start($event, $options); + } } } } diff --git a/Handler/MetricsHandlerInterface.php b/Handler/MetricsHandlerInterface.php index ec0cf4c..04e8b2c 100644 --- a/Handler/MetricsHandlerInterface.php +++ b/Handler/MetricsHandlerInterface.php @@ -16,11 +16,10 @@ namespace FRZB\Component\MetricsPower\Handler; use FRZB\Component\DependencyInjection\Attribute\AsAlias; -use Symfony\Component\Messenger\Event\AbstractWorkerMessageEvent; -use Symfony\Component\Messenger\Event\SendMessageToTransportsEvent; #[AsAlias(MetricsHandler::class)] interface MetricsHandlerInterface { - public function handle(AbstractWorkerMessageEvent|SendMessageToTransportsEvent $event): void; + /** @throws \Throwable */ + public function handle(object $event): void; } diff --git a/Logger/Processor/LogstashProcessor.php b/Logger/Processor/LogstashProcessor.php new file mode 100644 index 0000000..9391e39 --- /dev/null +++ b/Logger/Processor/LogstashProcessor.php @@ -0,0 +1,64 @@ +datetime, + $record->channel, + $record->level, + json_validate($record->message) ? $this->formatJson($record->message) : $record->message, + $this->mapContext($record), + $this->mapExtra($record), + json_validate($record->formatted) ? $this->formatJson($record->formatted) : $record->formatted, + ); + } + + private function formatJson(string $json): string + { + $decodedJson = json_decode($json, true); + + return json_encode($decodedJson, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE); + } + + private function mapExtra(LogRecord $record): array + { + return [ + ...$record->extra, + 'source' => $this->source, + ]; + } + + private function mapContext(LogRecord $record): array + { + return [ + ...$record->context, + ]; + } +} diff --git a/OptionsResolver/Exception/OptionsResolverLocatorException.php b/OptionsResolver/Exception/OptionsResolverLocatorException.php new file mode 100644 index 0000000..cb00c02 --- /dev/null +++ b/OptionsResolver/Exception/OptionsResolverLocatorException.php @@ -0,0 +1,20 @@ + */ - private readonly HashMap $resolvers; - public function __construct( - #[TaggedIterator(OptionsResolverInterface::class, defaultIndexMethod: 'getType')] - iterable $resolvers, - ) { - $this->resolvers = HashMap::collect($resolvers); - } + #[TaggedLocator(OptionsResolverInterface::class, defaultIndexMethod: 'getType')] + private readonly ContainerInterface $resolvers, + ) {} - public function get(OptionsInterface $option): OptionsResolverInterface + public function get(OptionsInterface $option): ?OptionsResolverInterface { - return $this->resolvers->get($option::class)->get() - ?? $this->resolvers->get(OptionsInterface::class)->getUnsafe(); + try { + return $this->resolvers->get($option::class); + } catch (NotFoundExceptionInterface) { + return null; + } catch (ContainerExceptionInterface $e) { + throw OptionsResolverLocatorException::fromThrowable($e); + } } } diff --git a/OptionsResolver/OptionsResolverLocatorInterface.php b/OptionsResolver/OptionsResolverLocatorInterface.php index 2d44af8..38a6704 100644 --- a/OptionsResolver/OptionsResolverLocatorInterface.php +++ b/OptionsResolver/OptionsResolverLocatorInterface.php @@ -17,10 +17,12 @@ use FRZB\Component\DependencyInjection\Attribute\AsAlias; use FRZB\Component\MetricsPower\Attribute\OptionsInterface; +use FRZB\Component\MetricsPower\OptionsResolver\Exception\OptionsResolverLocatorException; use FRZB\Component\MetricsPower\OptionsResolver\Resolver\OptionsResolverInterface; #[AsAlias(OptionsResolverLocator::class)] interface OptionsResolverLocatorInterface { - public function get(OptionsInterface $option): OptionsResolverInterface; + /** @throws OptionsResolverLocatorException */ + public function get(OptionsInterface $option): ?OptionsResolverInterface; } diff --git a/OptionsResolver/Resolver/PrometheusOptionsResolver.php b/OptionsResolver/Resolver/PrometheusOptionsResolver.php index e3af79f..42a7c00 100644 --- a/OptionsResolver/Resolver/PrometheusOptionsResolver.php +++ b/OptionsResolver/Resolver/PrometheusOptionsResolver.php @@ -53,11 +53,17 @@ public function resolve(AbstractWorkerMessageEvent|SendMessageToTransportsEvent try { $this->registry - ->getOrRegisterCounter($this->namespace, CounterHelper::makeName($options, postfix: $postfix), $options->help, $options->labels) + ->getOrRegisterCounter( + $this->namespace, + CounterHelper::makeName($options, postfix: $postfix), + $options->help, + $options->labels + ) ->inc($options->values); } catch (BaseMetricsRegistrationException $e) { throw MetricsRegistrationException::fromThrowable($e); - } catch (StorageException $e) {} + } catch (StorageException $e) { + } } public static function getType(): string diff --git a/Tests/Unit/Handler/MetricsHandlerTest.php b/Tests/Unit/Handler/MetricsHandlerTest.php index fa56456..5602a60 100644 --- a/Tests/Unit/Handler/MetricsHandlerTest.php +++ b/Tests/Unit/Handler/MetricsHandlerTest.php @@ -18,7 +18,6 @@ use FRZB\Component\MetricsPower\Attribute\OptionsInterface; use FRZB\Component\MetricsPower\Attribute\PrometheusOptions; use FRZB\Component\MetricsPower\Handler\MetricsHandler; -use FRZB\Component\MetricsPower\Logger\MetricsPowerLoggerInterface; use FRZB\Component\MetricsPower\OptionsResolver\OptionsResolverLocatorInterface; use FRZB\Component\MetricsPower\OptionsResolver\Resolver\OptionsResolverInterface; use FRZB\Component\MetricsPower\Tests\Stub\Exception\SomethingGoesWrongException; @@ -27,7 +26,6 @@ test('it can handle and log event with message', function (): void { $locator = \Mockery::mock(OptionsResolverLocatorInterface::class); - $logger = \Mockery::mock(MetricsPowerLoggerInterface::class); $resolver = \Mockery::mock(OptionsResolverInterface::class); $event = new SendMessageToTransportsEvent(createTestEnvelope(), [TestConstants::DEFAULT_RECEIVER_NAME]); $handler = new MetricsHandler($locator); @@ -51,7 +49,6 @@ test('it can handle and log when caught', function (): void { $locator = \Mockery::mock(OptionsResolverLocatorInterface::class); - $logger = \Mockery::mock(MetricsPowerLoggerInterface::class); $resolver = \Mockery::mock(OptionsResolverInterface::class); $event = new SendMessageToTransportsEvent(createTestEnvelope(), [TestConstants::DEFAULT_RECEIVER_NAME]); $handler = new MetricsHandler($locator); diff --git a/Tests/Unit/OptionsResolver/Resolver/PrometheusOptionResolverTest.php b/Tests/Unit/OptionsResolver/Resolver/PrometheusOptionResolverTest.php index ae21dc2..5385c97 100644 --- a/Tests/Unit/OptionsResolver/Resolver/PrometheusOptionResolverTest.php +++ b/Tests/Unit/OptionsResolver/Resolver/PrometheusOptionResolverTest.php @@ -114,6 +114,6 @@ ->once() ->andThrow(new StorageException('something goes wrong')); - /** @noinspection PhpUnhandledExceptionInspection */ + // @noinspection PhpUnhandledExceptionInspection $prometheusOptionResolver->resolve($event, $options); -}); \ No newline at end of file +});