Skip to content

Commit

Permalink
[MetricsPower] Refactoring for ContextExtractors and ContextExtractor…
Browse files Browse the repository at this point in the history
…Locator
  • Loading branch information
fractalzombie committed Mar 29, 2024
1 parent 08cec6a commit 0d57ace
Show file tree
Hide file tree
Showing 30 changed files with 229 additions and 380 deletions.
4 changes: 2 additions & 2 deletions Attribute/LoggerOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
namespace FRZB\Component\MetricsPower\Attribute;

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

public function isSerializable(): bool
Expand Down
6 changes: 3 additions & 3 deletions Attribute/Metrical.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@

namespace FRZB\Component\MetricsPower\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
final class Metrical
#[\Attribute(\Attribute::TARGET_CLASS)]
final readonly class Metrical
{
/** @var OptionsInterface[] */
public readonly array $options;
public array $options;

public function __construct(
OptionsInterface ...$options,
Expand Down
16 changes: 8 additions & 8 deletions Attribute/PrometheusOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
namespace FRZB\Component\MetricsPower\Attribute;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class PrometheusOptions implements OptionsInterface
final readonly class PrometheusOptions implements OptionsInterface
{
public readonly string $counterName;
public string $counterName;

public function __construct(
public readonly string $topic,
public readonly string $name,
public readonly string $help,
public readonly array $labels,
public readonly array $values,
public readonly bool $isSerializable = true,
public string $topic,
public string $name,
public string $help,
public array $labels,
public array $values,
public bool $isSerializable = true,
) {}

public function isSerializable(): bool
Expand Down
8 changes: 4 additions & 4 deletions Attribute/SentryOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
namespace FRZB\Component\MetricsPower\Attribute;

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

public function isSerializable(): bool
Expand Down
8 changes: 4 additions & 4 deletions Handler/MetricsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ public function __construct(

public function handle(AbstractWorkerMessageEvent|SendMessageToTransportsEvent $event): void
{
foreach (MetricalHelper::getOptions($event->getEnvelope()->getMessage()) as $option) {
foreach (MetricalHelper::getOptions($event->getEnvelope()->getMessage()) as $options) {
try {
$this->locator->get($option)($event, $option);
$this->logger->info($event, $option);
$this->locator->get($options)($event, $options);
$this->logger->info($event);
} catch (\Throwable $e) {
$this->logger->error($event, $option, $e);
$this->logger->error($event, $e);
}
}
}
Expand Down
17 changes: 4 additions & 13 deletions Helper/MetricalHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,23 @@ public static function isMetrical(object|string $target): bool
return AttributeHelper::hasAttribute($target, Metrical::class);
}

public static function getMetrical(object|string $target): array
public static function getMetrical(object|string $target): ?Metrical
{
return AttributeHelper::getAttributes($target, Metrical::class);
}

public static function getFirstMetrical(object|string $target): ?Metrical
{
return ArrayList::collect(self::getMetrical($target))
->firstElement()
->get();
return ArrayList::collect(AttributeHelper::getAttributes($target, Metrical::class))->firstElement()->get();
}

/** @return array<OptionsInterface> */
public static function getOptions(object|string $target): array
{
return ArrayList::collect(self::getMetrical($target))
->map(static fn (Metrical $metrical) => $metrical->options)
->flatten()
return ArrayList::collect(self::getMetrical($target)?->options ?? [])
->toList();
}

public static function getFirstOptions(object|string $target): ?OptionsInterface
{
return ArrayList::collect(self::getOptions($target))
->firstElement()
->getOrElse(fn () => new LoggerOptions());
->getOrElse(new LoggerOptions());
}

public static function getCounterName(PrometheusOptions $options): string
Expand Down
27 changes: 14 additions & 13 deletions Logger/ContextExtractor/AbstractWorkerMessageExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,38 @@

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

use FRZB\Component\MetricsPower\Attribute\OptionsInterface;
use FRZB\Component\MetricsPower\Helper\ClassHelper;
use FRZB\Component\MetricsPower\Logger\Data\Context;
use FRZB\Component\MetricsPower\Helper\MetricalHelper;
use FRZB\Component\MetricsPower\Logger\Data\LoggerContext;
use Symfony\Component\Messenger\Event\AbstractWorkerMessageEvent;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

abstract class AbstractWorkerMessageExtractor
abstract readonly class AbstractWorkerMessageExtractor implements ContextExtractorInterface
{
public function __construct(
private readonly SerializerInterface $serializer,
private SerializerInterface $serializer,
) {}

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
public function extract(AbstractWorkerMessageEvent $target): LoggerContext
{
$message = $target->getEnvelope()->getMessage();
$context = [
'target_class' => ClassHelper::getShortName($target),
'message_class' => ClassHelper::getShortName($target->getEnvelope()->getMessage()),
'target_values' => $this->serializer->serialize($target, JsonEncoder::FORMAT),
];

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

if ($options) {
if (($options = MetricalHelper::getFirstOptions($message)) && $options->isSerializable()) {
$context += [
'message_class' => ClassHelper::getShortName($message),
'message_values' => $this->serializer->serialize($message, JsonEncoder::FORMAT),
'options_class' => ClassHelper::getShortName($options),
'options_values' => $this->serializer->serialize($options, JsonEncoder::FORMAT),
];
}

return new Context($this->getMessage(), $context);
return new LoggerContext(static::getMessage(), $context);
}

abstract protected function getMessage(): string;
abstract protected static function getMessage(): string;
}
15 changes: 2 additions & 13 deletions Logger/ContextExtractor/ContextExtractorInterface.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?php

/** @noinspection PhpDocSignatureInspection */

declare(strict_types=1);

/**
Expand All @@ -17,21 +15,12 @@

namespace FRZB\Component\MetricsPower\Logger\ContextExtractor;

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

/**
* @template TTarget of mixed
* @template TOptions of OptionsInterface
* @method LoggerContext extract(object $target)
*/
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;
}
43 changes: 14 additions & 29 deletions Logger/ContextExtractor/DefaultContextExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,40 @@

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 FRZB\Component\MetricsPower\Helper\MetricalHelper;
use FRZB\Component\MetricsPower\Logger\Data\LoggerContext;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @implements ContextExtractorInterface<object, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
class DefaultContextExtractor implements ContextExtractorInterface
final readonly 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}]';
private const MESSAGE = '[MetricsPower] [INFO] [MESSAGE: Operation succeed] [TARGET_CLASS: {target_class}]';

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

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
public function extract(object $target): LoggerContext
{
$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)];
}
$context = [
'target_class' => ClassHelper::getShortName($target),
];

if ($options) {
if (($options = MetricalHelper::getFirstOptions($target)) && $options?->isSerializable()) {
$context += [
'target_values' => $this->serializer->serialize($target, JsonEncoder::FORMAT),
'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(),
'options_values' => ClassHelper::getProperties($target),
];
}

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

public static function getType(): string
{
return self::DEFAULT_TYPE;
return 'Default';
}
}
30 changes: 14 additions & 16 deletions Logger/ContextExtractor/ExceptionEventContextExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,32 @@

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 FRZB\Component\MetricsPower\Logger\Data\LoggerContext;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @implements ContextExtractorInterface<ExceptionEvent, OptionsInterface>
*/
#[AsService, AsTagged(ContextExtractorInterface::class)]
class ExceptionEventContextExtractor implements ContextExtractorInterface
final readonly 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
public function __construct(
private SerializerInterface $serializer,
) {}

public function extract(ExceptionEvent $target): LoggerContext
{
$context = [
'target_class' => ClassHelper::getShortName($target),
'target_values' => $this->serializer->serialize($target, JsonEncoder::FORMAT),
'exception_class' => ClassHelper::getShortName($target->getThrowable()),
'exception_message' => $target->getThrowable()->getMessage(),
'exception_trace' => $target->getThrowable()->getTrace(),
];

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);
return new LoggerContext(self::MESSAGE, $context);
}

public static function getType(): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,38 @@

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 FRZB\Component\MetricsPower\Helper\MetricalHelper;
use FRZB\Component\MetricsPower\Logger\Data\LoggerContext;
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
final readonly class SendMessageToTransportsEventContextExtractor implements ContextExtractorInterface
{
private const MESSAGE = '[MetricsPower] [INFO] [MESSAGE: Sent to transports {transport_name}] [OPTIONS_CLASS: {options_class}] [TARGET_CLASS: {target_class}] [MESSAGE_CLASS: {message_class}]';
private const MESSAGE = '[MetricsPower] [INFO] [MESSAGE: Message sent] [TARGET_CLASS: {target_class}]';

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

public function extract(mixed $target, ?OptionsInterface $options = null, ?\Throwable $exception = null): Context
public function extract(SendMessageToTransportsEvent $target): LoggerContext
{
$context = [
'target_class' => ClassHelper::getShortName($target),
'target_values' => $this->serializer->serialize($target, JsonEncoder::FORMAT),
'message_class' => ClassHelper::getShortName($target->getEnvelope()->getMessage()),
'transport_name' => implode(',', $target->getSenders()),
];

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

if ($options) {
if (($options = MetricalHelper::getFirstOptions($target)) && $options->isSerializable()) {
$context += [
'options_class' => ClassHelper::getShortName($options),
'options_values' => $this->serializer->serialize($target->getEnvelope()->getMessage(), JsonEncoder::FORMAT),
];
}

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

public static function getType(): string
Expand Down
Loading

0 comments on commit 0d57ace

Please sign in to comment.