From 848de7ee6c72820a61a127be6f6f52af0b6e9d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 25 Jan 2024 14:19:26 +0100 Subject: [PATCH] Upgrade to monolog3 --- composer.json | 2 +- .../LogHandler/TracingHandlerSpec.php | 43 ++++++++++++++----- src/DependencyInjection/Configuration.php | 7 ++- src/Logging/Formatter/JsonFormatter.php | 3 +- .../Processor/NormalizeExceptionProcessor.php | 15 +++---- .../Processor/TraceContextProcessor.php | 32 +++++++------- .../LogHandler/TracingHandler.php | 16 ++++--- 7 files changed, 70 insertions(+), 48 deletions(-) diff --git a/composer.json b/composer.json index 95bafbb..2f3337f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "email": "developers@worldia.com" }], "require": { - "monolog/monolog": "^2.0", + "monolog/monolog": "^3.0", "nyholm/dsn": "^2.0", "nyholm/psr7": "^1.5", "open-telemetry/api": ">=1.0.2", diff --git a/spec/Tracing/Instrumentation/LogHandler/TracingHandlerSpec.php b/spec/Tracing/Instrumentation/LogHandler/TracingHandlerSpec.php index b1d82cd..611705d 100644 --- a/spec/Tracing/Instrumentation/LogHandler/TracingHandlerSpec.php +++ b/spec/Tracing/Instrumentation/LogHandler/TracingHandlerSpec.php @@ -8,7 +8,9 @@ namespace spec\Instrumentation\Tracing\Instrumentation\LogHandler; use Instrumentation\Tracing\Instrumentation\MainSpanContextInterface; -use Monolog\Logger; +use Monolog\DateTimeImmutable; +use Monolog\Level; +use Monolog\LogRecord; use OpenTelemetry\API\Trace\SpanInterface; use OpenTelemetry\API\Trace\TracerProviderInterface; use PhpSpec\ObjectBehavior; @@ -21,13 +23,20 @@ public function it_adds_event_from_all_channels( MainSpanContextInterface $mainSpanContext, SpanInterface $span ): void { - $this->beConstructedWith($tracerProvider, $mainSpanContext, Logger::INFO, []); + $this->beConstructedWith($tracerProvider, $mainSpanContext, Level::Info, []); $mainSpanContext->getMainSpan()->willReturn($span); $span->addEvent(Argument::any())->willReturn($span); - $this->handle(['message' => 'Error from channel "foo"', 'channel' => 'foo', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); - $this->handle(['message' => 'Error from channel "bar"', 'channel' => 'bar', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); + $record = new LogRecord( + new DateTimeImmutable(true), + '', + Level::Error, + '', + ); + + $this->handle($record->with(['message' => 'Error from channel "foo"', 'channel' => 'foo'])); + $this->handle($record->with(['message' => 'Error from channel "bar"', 'channel' => 'bar'])); $span->addEvent('Error from channel "foo"')->shouldHaveBeenCalled(); $span->addEvent('Error from channel "bar"')->shouldHaveBeenCalled(); @@ -38,13 +47,20 @@ public function it_adds_event_from_specific_channel_only( MainSpanContextInterface $mainSpanContext, SpanInterface $span ): void { - $this->beConstructedWith($tracerProvider, $mainSpanContext, Logger::INFO, ['foo']); + $this->beConstructedWith($tracerProvider, $mainSpanContext, Level::Info, ['foo']); $mainSpanContext->getMainSpan()->willReturn($span); $span->addEvent(Argument::any())->willReturn($span); - $this->handle(['message' => 'Error from channel "foo"', 'channel' => 'foo', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); - $this->handle(['message' => 'Error from channel "bar"', 'channel' => 'bar', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); + $record = new LogRecord( + new DateTimeImmutable(true), + '', + Level::Error, + '', + ); + + $this->handle($record->with(['message' => 'Error from channel "foo"', 'channel' => 'foo'])); + $this->handle($record->with(['message' => 'Error from channel "bar"', 'channel' => 'bar'])); $span->addEvent('Error from channel "foo"')->shouldHaveBeenCalled(); $span->addEvent('Error from channel "bar"')->shouldNotHaveBeenCalled(); @@ -55,13 +71,20 @@ public function it_ignores_event_from_specific_channel( MainSpanContextInterface $mainSpanContext, SpanInterface $span ): void { - $this->beConstructedWith($tracerProvider, $mainSpanContext, Logger::INFO, ['!foo']); + $this->beConstructedWith($tracerProvider, $mainSpanContext, Level::Info, ['!foo']); $mainSpanContext->getMainSpan()->willReturn($span); $span->addEvent(Argument::any())->willReturn($span); - $this->handle(['message' => 'Error from channel "foo"', 'channel' => 'foo', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); - $this->handle(['message' => 'Error from channel "bar"', 'channel' => 'bar', 'level' => Logger::ERROR, 'extra' => [], 'context' => []]); + $record = new LogRecord( + new DateTimeImmutable(true), + '', + Level::Error, + '', + ); + + $this->handle($record->with(['message' => 'Error from channel "foo"', 'channel' => 'foo'])); + $this->handle($record->with(['message' => 'Error from channel "bar"', 'channel' => 'bar'])); $span->addEvent('Error from channel "foo"')->shouldNotHaveBeenCalled(); $span->addEvent('Error from channel "bar"')->shouldHaveBeenCalled(); diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 606003e..a5b1683 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -8,7 +8,6 @@ namespace Instrumentation\DependencyInjection; use Monolog\Level; -use Monolog\Logger; use OpenTelemetry\SemConv\ResourceAttributes; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -90,9 +89,9 @@ public function getConfigTreeBuilder(): TreeBuilder ->addDefaultsIfNotSet() ->children() ->enumNode('level') - ->defaultValue(Logger::INFO) - ->values(method_exists(Logger::class, 'getLevels') ? Logger::getLevels() : Level::cases()) // @phpstan-ignore-line - ->info(sprintf('One of the %s levels.', Logger::class)) + ->defaultValue(Level::Info) + ->values(Level::cases()) + ->info(sprintf('One of the %s levels.', Level::class)) ->end() ->arrayNode('channels') ->defaultValue([]) diff --git a/src/Logging/Formatter/JsonFormatter.php b/src/Logging/Formatter/JsonFormatter.php index bd02ccb..fbcf29b 100644 --- a/src/Logging/Formatter/JsonFormatter.php +++ b/src/Logging/Formatter/JsonFormatter.php @@ -10,13 +10,14 @@ namespace Instrumentation\Logging\Formatter; use Monolog\Formatter\JsonFormatter as BaseJsonFormatter; +use Monolog\LogRecord; use Monolog\Utils; class JsonFormatter extends BaseJsonFormatter { private ?int $lengthLimit = null; - public function format(array $record): string + public function format(LogRecord $record): string { $normalized = $this->normalize($record); diff --git a/src/Logging/Processor/NormalizeExceptionProcessor.php b/src/Logging/Processor/NormalizeExceptionProcessor.php index 08cb26d..a9d6329 100644 --- a/src/Logging/Processor/NormalizeExceptionProcessor.php +++ b/src/Logging/Processor/NormalizeExceptionProcessor.php @@ -8,18 +8,15 @@ namespace Instrumentation\Logging\Processor; use Instrumentation\Semantics\Normalizer\ExceptionNormalizer; +use Monolog\LogRecord; +use Monolog\Processor\ProcessorInterface; -class NormalizeExceptionProcessor +class NormalizeExceptionProcessor implements ProcessorInterface { - /** - * @param array $record - * - * @return array - */ - public function __invoke(array $record): array + public function __invoke(LogRecord $record): LogRecord { - if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { - $record['context']['exception'] = ExceptionNormalizer::normalizeException($record['context']['exception']); + if (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) { + $record->context['exception'] = ExceptionNormalizer::normalizeException($record->context['exception']); } return $record; diff --git a/src/Logging/Processor/TraceContextProcessor.php b/src/Logging/Processor/TraceContextProcessor.php index 917d064..a59b10c 100644 --- a/src/Logging/Processor/TraceContextProcessor.php +++ b/src/Logging/Processor/TraceContextProcessor.php @@ -7,9 +7,11 @@ namespace Instrumentation\Logging\Processor; +use Monolog\LogRecord; +use Monolog\Processor\ProcessorInterface; use OpenTelemetry\SDK\Trace\Span; -class TraceContextProcessor +class TraceContextProcessor implements ProcessorInterface { /** * @param array{trace:array,span:array,sampled:array,operation:array} $map @@ -18,12 +20,7 @@ public function __construct(private array $map) { } - /** - * @param array $record - * - * @return array - */ - public function __invoke(array $record): array + public function __invoke(LogRecord $record): LogRecord { $span = Span::getCurrent(); $spanContext = $span->getContext(); @@ -32,28 +29,31 @@ public function __invoke(array $record): array return $record; } - $this->setRecordKey($record, $this->map['trace'], $spanContext->getTraceId()); - $this->setRecordKey($record, $this->map['span'], $spanContext->getSpanId()); - $this->setRecordKey($record, $this->map['sampled'], $spanContext->isSampled()); + $record = $this->withRecordKey($record, $this->map['trace'], $spanContext->getTraceId()); + $record = $this->withRecordKey($record, $this->map['span'], $spanContext->getSpanId()); + $record = $this->withRecordKey($record, $this->map['sampled'], $spanContext->isSampled()); if ($span instanceof Span) { - $this->setRecordKey($record, $this->map['operation'], $span->getName()); + $record = $this->withRecordKey($record, $this->map['operation'], $span->getName()); } return $record; } /** - * @param array $record - * @param array $keys - * @param string|bool|int $value + * @param LogRecord $record + * @param array $keys + * @param string|bool|int $value */ - private function setRecordKey(array &$record, array $keys, $value): void + private function withRecordKey(LogRecord $record, array $keys, $value): LogRecord { - $temp = &$record; + $recordAsArray = $record->toArray(); + $temp = &$recordAsArray; foreach ($keys as $key) { $temp = &$temp[$key]; } $temp = $value; + + return $record->with($recordAsArray); } } diff --git a/src/Tracing/Instrumentation/LogHandler/TracingHandler.php b/src/Tracing/Instrumentation/LogHandler/TracingHandler.php index d624bfd..714bb3e 100644 --- a/src/Tracing/Instrumentation/LogHandler/TracingHandler.php +++ b/src/Tracing/Instrumentation/LogHandler/TracingHandler.php @@ -11,7 +11,9 @@ use Instrumentation\Tracing\Instrumentation\MainSpanContextInterface; use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Level; use Monolog\Logger; +use Monolog\LogRecord; use Monolog\Processor\PsrLogMessageProcessor; use OpenTelemetry\API\Trace\TracerProviderInterface; use OpenTelemetry\SDK\Trace\Span; @@ -29,7 +31,7 @@ class TracingHandler extends AbstractProcessingHandler /** * @param array $channels */ - public function __construct(protected TracerProviderInterface $tracerProvider, protected MainSpanContextInterface $mainSpanContext, $level = Logger::INFO, private array $channels = [], private string $strategy = self::STRATEGY_MAIN_SPAN, bool $bubble = true) + public function __construct(protected TracerProviderInterface $tracerProvider, protected MainSpanContextInterface $mainSpanContext, $level = Level::Info, private array $channels = [], private string $strategy = self::STRATEGY_MAIN_SPAN, bool $bubble = true) { parent::__construct($level, $bubble); @@ -43,13 +45,13 @@ public function __construct(protected TracerProviderInterface $tracerProvider, p $this->channels = array_filter($this->channels, fn (string $channel) => !str_starts_with($channel, '!')); } - protected function write(array $record): void + protected function write(LogRecord $record): void { - if ($this->channels && !\in_array($record['channel'], $this->channels)) { + if ($this->channels && !\in_array($record->channel, $this->channels)) { return; } - if ($this->excludedChannels && \in_array($record['channel'], $this->excludedChannels)) { + if ($this->excludedChannels && \in_array($record->channel, $this->excludedChannels)) { return; } @@ -59,10 +61,10 @@ protected function write(array $record): void default => throw new \InvalidArgumentException(sprintf('Unkown strategy "%s".', $this->strategy)) }; - if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) { - $span->recordException($record['context']['exception'], ['raw_stacktrace' => $record['context']['exception']->getTraceAsString()]); + if (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) { + $span->recordException($record->context['exception'], ['raw_stacktrace' => $record->context['exception']->getTraceAsString()]); } else { - $span->addEvent($record['message']); + $span->addEvent($record->message); } } }