Skip to content

Commit

Permalink
[MetricsPower] Added ContextExtractor for logger
Browse files Browse the repository at this point in the history
[MetricsPower] Added ContextExtractor for logger
  • Loading branch information
fractalzombie committed Mar 28, 2024
1 parent 1bfc556 commit e0948b2
Show file tree
Hide file tree
Showing 32 changed files with 874 additions and 79 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,9 @@ jobs:
strategy:
matrix:
php:
- "8.1"
- "8.2"
- "8.3"
include:
- php-version: "8.1"
composer-options: "--ignore-platform-reqs"
- php-version: "8.2"
composer-options: "--ignore-platform-reqs"
- php-version: "8.3"
Expand Down
12 changes: 11 additions & 1 deletion Attribute/LoggerOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@
namespace FRZB\Component\MetricsPower\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class LoggerOptions implements OptionsInterface {}
final class LoggerOptions implements OptionsInterface
{
public function __construct(
public readonly bool $isSerializable = true,
) {}

public function isSerializable(): bool
{
return $this->isSerializable;
}
}
5 changes: 4 additions & 1 deletion Attribute/OptionsInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@

namespace FRZB\Component\MetricsPower\Attribute;

interface OptionsInterface {}
interface OptionsInterface
{
public function isSerializable(): bool;
}
10 changes: 6 additions & 4 deletions Attribute/PrometheusOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

namespace FRZB\Component\MetricsPower\Attribute;

use FRZB\Component\MetricsPower\Helper\MetricalHelper;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class PrometheusOptions implements OptionsInterface
{
Expand All @@ -28,7 +26,11 @@ public function __construct(
public readonly string $help,
public readonly array $labels,
public readonly array $values,
) {
$this->counterName = MetricalHelper::getCounterName($this);
public readonly bool $isSerializable = true,
) {}

public function isSerializable(): bool
{
return $this->isSerializable;
}
}
6 changes: 6 additions & 0 deletions Attribute/SentryOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ final class SentryOptions implements OptionsInterface
public function __construct(
public readonly bool $waitRetry = true,
public readonly bool $onHandleFlush = true,
public readonly bool $isSerializable = true,
) {}

public function isSerializable(): bool
{
return $this->isSerializable;
}
}
4 changes: 2 additions & 2 deletions Handler/MetricsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public function handle(AbstractWorkerMessageEvent|SendMessageToTransportsEvent $
foreach (MetricalHelper::getOptions($event->getEnvelope()->getMessage()) as $option) {
try {
$this->locator->get($option)($event, $option);
$this->logger->logInfo($event, $option);
$this->logger->info($event, $option);
} catch (\Throwable $e) {
$this->logger->logError($event, $option, $e);
$this->logger->error($event, $option, $e);
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions Logger/ContextExtractor/ContextExtractorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/** @noinspection PhpDocSignatureInspection */

declare(strict_types=1);

/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* Copyright (c) 2024 Mykhailo Shtanko fractalzombie@gmail.com
*
* For the full copyright and license information, please view the LICENSE.MD
* file that was distributed with this source code.
*/

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Logger\Data\Context;

/**
* @template TTarget of mixed
* @template TOptions of OptionsInterface
*/
interface ContextExtractorInterface
{
/**
* @param TTarget $target
* @param TOptions $options
* @param ?\Throwable $exception
*/
public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context;

public static function getType(): string;
}
71 changes: 71 additions & 0 deletions Logger/ContextExtractor/DefaultContextExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* Copyright (c) 2024 Mykhailo Shtanko fractalzombie@gmail.com
*
* For the full copyright and license information, please view the LICENSE.MD
* file that was distributed with this source code.
*/

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\DependencyInjection\Attribute\AsService;
use FRZB\Component\DependencyInjection\Attribute\AsTagged;
use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Helper\ClassHelper;
use FRZB\Component\MetricsPower\Logger\Data\Context;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @implements ContextExtractorInterface<object, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
class DefaultContextExtractor implements ContextExtractorInterface
{
public const DEFAULT_TYPE = 'Default';
private const MESSAGE_INFO = '[MetricsPower] [INFO] [MESSAGE: Operation succeed] [OPTIONS_CLASS: {options_class}] [TARGET_CLASS: {target_class}]';
private const MESSAGE_ERROR = '[MetricsPower] [ERROR] [MESSAGE: Operation failed] [OPTIONS_CLASS: {options_class}] [MESSAGE_CLASS: {message_class}] [EXCEPTION_CLASS: {exception_class}] [EXCEPTION_MESSAGE: {exception_message}] [OPTIONS_VALUES: {option_values}]';

public function __construct(
private readonly SerializerInterface $serializer,
) {}

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
{
$message = $exception ? self::MESSAGE_ERROR : self::MESSAGE_INFO;
$context = ['target_class' => ClassHelper::getShortName($target)];

if ($options?->isSerializable()) {
$context += ['target_values' => $this->serializer->serialize($target, JsonEncoder::FORMAT)];
}

if ($options) {
$context += [
'options_class' => ClassHelper::getShortName($options),
'option_values' => ClassHelper::getProperties($options),
];
}

if ($exception) {
$context += [
'exception_class' => ClassHelper::getShortName($exception),
'exception_message' => $exception->getMessage(),
'exception_trace' => $exception->getTrace(),
];
}

return new Context($message, $context);
}

public static function getType(): string
{
return self::DEFAULT_TYPE;
}
}
54 changes: 54 additions & 0 deletions Logger/ContextExtractor/ExceptionEventContextExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* Copyright (c) 2024 Mykhailo Shtanko fractalzombie@gmail.com
*
* For the full copyright and license information, please view the LICENSE.MD
* file that was distributed with this source code.
*/

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\DependencyInjection\Attribute\AsService;
use FRZB\Component\DependencyInjection\Attribute\AsTagged;
use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Helper\ClassHelper;
use FRZB\Component\MetricsPower\Logger\Data\Context;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;

/**
* @implements ContextExtractorInterface<ExceptionEvent, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
class ExceptionEventContextExtractor implements ContextExtractorInterface
{
private const MESSAGE = '[MetricsPower] [ERROR] [MESSAGE: Operation failed] [TARGET_CLASS: {target_class}] [EXCEPTION_CLASS: {exception_class}] [EXCEPTION_MESSAGE: {exception_message}]';

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
{
$context = [
'target_class' => ClassHelper::getShortName($target),
];

if ($exception) {
$context += [
'exception_class' => ClassHelper::getShortName($target->getThrowable()),
'exception_message' => $target->getThrowable()->getMessage(),
'exception_trace' => $target->getThrowable()->getTrace(),
];
}

return new Context(self::MESSAGE, $context);
}

public static function getType(): string
{
return ExceptionEvent::class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* Copyright (c) 2024 Mykhailo Shtanko fractalzombie@gmail.com
*
* For the full copyright and license information, please view the LICENSE.MD
* file that was distributed with this source code.
*/

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\DependencyInjection\Attribute\AsService;
use FRZB\Component\DependencyInjection\Attribute\AsTagged;
use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Helper\ClassHelper;
use FRZB\Component\MetricsPower\Logger\Data\Context;
use Symfony\Component\Messenger\Event\SendMessageToTransportsEvent;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @implements ContextExtractorInterface<SendMessageToTransportsEvent, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
class SendMessageToTransportsEventContextExtractor implements ContextExtractorInterface
{
private const MESSAGE = '[MetricsPower] [INFO] [MESSAGE: Sent to transport] [OPTIONS_CLASS: {options_class}] [TARGET_CLASS: {target_class}] [MESSAGE_CLASS: {message_class}]';

public function __construct(
private readonly SerializerInterface $serializer,
) {}

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
{
$context = [
'target_class' => ClassHelper::getShortName($target),
'message_class' => ClassHelper::getShortName($target->getEnvelope()->getMessage()),
];

if ($options?->isSerializable()) {
$context += ['message_values' => $this->serializer->serialize($target->getEnvelope()->getMessage(), JsonEncoder::FORMAT)];
}

if ($options) {
$context += [
'options_class' => ClassHelper::getShortName($options),
];
}

return new Context(self::MESSAGE, $context);
}

public static function getType(): string
{
return SendMessageToTransportsEvent::class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*
* Copyright (c) 2024 Mykhailo Shtanko fractalzombie@gmail.com
*
* For the full copyright and license information, please view the LICENSE.MD
* file that was distributed with this source code.
*/

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\DependencyInjection\Attribute\AsService;
use FRZB\Component\DependencyInjection\Attribute\AsTagged;
use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Helper\ClassHelper;
use FRZB\Component\MetricsPower\Logger\Data\Context;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @implements ContextExtractorInterface<WorkerMessageFailedEvent, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
final class WorkerMessageFailedEventContextExtractor implements ContextExtractorInterface
{
private const MESSAGE = '[MetricsPower] [ERROR] [MESSAGE: Handle failed] [TARGET_CLASS: {target_class}] [OPTIONS_CLASS: {options_class}] [MESSAGE_CLASS: {message_class}] [EXCEPTION_CLASS: {exception_class}] [EXCEPTION_MESSAGE: {exception_message}] [OPTIONS_VALUES: {option_values}]';

public function __construct(
private readonly SerializerInterface $serializer,
) {}

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
{
$context = [
'target_class' => ClassHelper::getShortName($target),
];

if ($options?->isSerializable()) {
$context += ['target_values' => $this->serializer->serialize($target->getEnvelope()->getMessage(), JsonEncoder::FORMAT)];
}

if ($options) {
$context += [
'options_class' => ClassHelper::getShortName($options),
'option_values' => ClassHelper::getProperties($options),
];
}

if ($exception) {
$context += [
'exception_class' => ClassHelper::getShortName($exception),
'exception_message' => $exception->getMessage(),
'exception_trace' => $exception->getTrace(),
];
}

return new Context(self::MESSAGE, $context);
}

public static function getType(): string
{
return WorkerMessageFailedEvent::class;
}
}
Loading

0 comments on commit e0948b2

Please sign in to comment.