-
Notifications
You must be signed in to change notification settings - Fork 1
Enhanced context format #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
a2aff82
feat: implement context processors for structured logging
hungthai1401 93024f0
test: add comprehensive tests for context processors
hungthai1401 3ca8bb6
feat: implement context processors for structured logging with array,…
hungthai1401 00ac89b
refactor: consolidate array, null, and scalar processing into BuiltIn…
hungthai1401 af98022
refactor: simplify context processors
roxblnfk 4cbf8ac
test: Update tests
roxblnfk 0027fd2
feat: Make processors non-internal
roxblnfk 0406f7f
chore: Fix `test` script in `composer.json`
roxblnfk 50ba224
chore: Cover DefaultProcessor with comments
roxblnfk 33f5a80
chore: Add symfony/polyfill-php83 for the `Override` attribute
roxblnfk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for arrays and nested arrays. | ||
| * | ||
| * Recursively processes array elements to handle complex nested structures. | ||
| * | ||
| * @implements ContextProcessorInterface<array<array-key, mixed>, array<array-key, mixed>> | ||
| */ | ||
| class ArrayProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return \is_array($value); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<array-key, mixed> $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return array<array-key, mixed> | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| /** @var array<array-key, mixed> $processed */ | ||
| $processed = []; | ||
|
|
||
| /** | ||
| * @var array-key $key | ||
| * @var mixed $item | ||
| */ | ||
| foreach ($value as $key => $item) { | ||
| /** @psalm-suppress MixedAssignment - Intentionally processing mixed types */ | ||
| $processed[$key] = $recursiveProcessor($item); | ||
| } | ||
|
|
||
| return $processed; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Interface for context data processors. | ||
| * | ||
| * Each processor handles a specific type of data and converts it to a | ||
| * format suitable for structured logging. | ||
| * | ||
| * @template TValue The input value type | ||
| * @template TProcessed The processed output type | ||
| */ | ||
| interface ContextProcessorInterface | ||
| { | ||
| /** | ||
| * Check if this processor can handle the given value. | ||
| */ | ||
| public function canProcess(mixed $value): bool; | ||
|
|
||
| /** | ||
| * Process the value and return a serializable representation. | ||
| * | ||
| * @param TValue $value The value to process | ||
| * @param callable(mixed): mixed $recursiveProcessor Function to process nested values recursively | ||
| * @return TProcessed Processed value suitable for logging | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Manager for context data processors. | ||
| * | ||
| * Coordinates multiple processors to handle different data types for structured logging. | ||
| * Processors are executed in registration order, with the first matching processor handling the value. | ||
| */ | ||
| class ContextProcessorManager | ||
| { | ||
| /** @var array<ContextProcessorInterface> */ | ||
| private array $processors = []; | ||
|
|
||
| public function __construct() | ||
| { | ||
| $this->registerDefaultProcessors(); | ||
| } | ||
|
|
||
| /** | ||
| * Register a processor. | ||
| * Processors are checked in the order they are added. | ||
| */ | ||
| public function addProcessor(ContextProcessorInterface $processor): void | ||
| { | ||
| $this->processors[] = $processor; | ||
| } | ||
|
|
||
| /** | ||
| * Process context data recursively. | ||
| * | ||
| * @template TKey of array-key | ||
| * @template TValue | ||
| * @param array<TKey, TValue> $context | ||
| * @return array<string, mixed> | ||
| */ | ||
| public function processContext(array $context): array | ||
| { | ||
| if (empty($context)) { | ||
| return []; | ||
| } | ||
|
|
||
| /** @var array<string, mixed> $processed */ | ||
| $processed = []; | ||
|
|
||
| /** | ||
| * @var TKey $key | ||
| * @var TValue $value | ||
| */ | ||
| foreach ($context as $key => $value) { | ||
| $stringKey = (string) $key; | ||
| /** @psalm-suppress MixedAssignment - Intentionally processing mixed types */ | ||
| $processed[$stringKey] = $this->processValue($value); | ||
| } | ||
|
|
||
| return $processed; | ||
| } | ||
|
|
||
| /** | ||
| * Process a single value using the appropriate processor. | ||
| */ | ||
| public function processValue(mixed $value): mixed | ||
| { | ||
| foreach ($this->processors as $processor) { | ||
| if ($processor->canProcess($value)) { | ||
| return $processor->process($value, [$this, 'processValue']); | ||
| } | ||
| } | ||
|
|
||
| // This should never happen due to FallbackProcessor, but just in case | ||
| return \gettype($value); | ||
| } | ||
|
|
||
| /** | ||
| * Register the default set of processors in the correct order. | ||
| * Order matters: more specific processors should be registered first. | ||
| */ | ||
| private function registerDefaultProcessors(): void | ||
| { | ||
| // Null values first (most specific) | ||
| $this->addProcessor(new NullProcessor()); | ||
|
|
||
| // Scalar values (very common, but after null) | ||
| $this->addProcessor(new ScalarProcessor()); | ||
|
|
||
| // Specific object types (before generic object processor) | ||
| $this->addProcessor(new DateTimeProcessor()); | ||
| $this->addProcessor(new ThrowableProcessor()); | ||
| $this->addProcessor(new StringableProcessor()); | ||
|
|
||
| // Collections and resources | ||
| $this->addProcessor(new ArrayProcessor()); | ||
| $this->addProcessor(new ResourceProcessor()); | ||
|
|
||
| // Generic object processor (before fallback) | ||
| $this->addProcessor(new ObjectProcessor()); | ||
|
|
||
| // Fallback processor (last resort) | ||
| $this->addProcessor(new FallbackProcessor()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for DateTime objects. | ||
| * | ||
| * Converts DateTime and DateTimeImmutable objects to ISO 8601 format | ||
| * for consistent structured logging. | ||
| * | ||
| * @implements ContextProcessorInterface<\DateTimeInterface, string> | ||
| */ | ||
| class DateTimeProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return $value instanceof \DateTimeInterface; | ||
| } | ||
|
|
||
| /** | ||
| * @param \DateTimeInterface $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return string | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| return $value->format(\DateTimeInterface::ATOM); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Fallback processor for unknown types. | ||
| * | ||
| * Returns the type name for any value that couldn't be processed | ||
| * by more specific processors. | ||
| * | ||
| * @implements ContextProcessorInterface<mixed, string> | ||
| */ | ||
| class FallbackProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| // This processor can handle anything as a last resort | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return string | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| return \gettype($value); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for null values. | ||
| * | ||
| * Handles null values explicitly, passing them through as-is | ||
| * since null is already suitable for structured logging. | ||
| * | ||
| * @implements ContextProcessorInterface<null, null> | ||
| */ | ||
| class NullProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return $value === null; | ||
| } | ||
|
|
||
| /** | ||
| * @param null $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return null | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| // Null values are already suitable for logging | ||
| return null; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for generic objects. | ||
| * | ||
| * Attempts to convert objects to array representation using public properties, | ||
| * or falls back to class name if no public properties are available. | ||
| * | ||
| * @implements ContextProcessorInterface<object, array<string, mixed>|string> | ||
| */ | ||
| class ObjectProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return \is_object($value); | ||
| } | ||
|
|
||
| /** | ||
| * @param object $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return array<string, mixed>|string | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| // Try to convert to array (for objects with public properties) | ||
| $objectVars = \get_object_vars($value); | ||
|
|
||
| if (!empty($objectVars)) { | ||
| /** @var array<string, mixed> $processed */ | ||
| $processed = []; | ||
| /** | ||
| * @var string $property | ||
| * @var mixed $propertyValue | ||
| */ | ||
| foreach ($objectVars as $property => $propertyValue) { | ||
| /** @psalm-suppress MixedAssignment - Intentionally processing mixed types */ | ||
| $processed[$property] = $recursiveProcessor($propertyValue); | ||
| } | ||
| return $processed; | ||
| } | ||
|
|
||
| // Fallback to class name if no public properties | ||
| return \get_class($value); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for resource types. | ||
| * | ||
| * Converts resources to string representation indicating the resource type. | ||
| * | ||
| * @implements ContextProcessorInterface<resource, string> | ||
| */ | ||
| class ResourceProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return \is_resource($value); | ||
| } | ||
|
|
||
| /** | ||
| * @param resource $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return string | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| return \get_resource_type($value) . ' resource'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace RoadRunner\PsrLogger\ContextProcessor; | ||
|
|
||
| /** | ||
| * Processor for scalar values (string, int, float, bool). | ||
| * | ||
| * These values are passed through as-is since they are already | ||
| * suitable for structured logging. Note: null values are handled | ||
| * by the dedicated NullProcessor. | ||
| * | ||
| * @implements ContextProcessorInterface<scalar, scalar> | ||
| */ | ||
| class ScalarProcessor implements ContextProcessorInterface | ||
| { | ||
| public function canProcess(mixed $value): bool | ||
| { | ||
| return \is_scalar($value); | ||
| } | ||
|
|
||
| /** | ||
| * @param scalar $value | ||
| * @param callable(mixed): mixed $recursiveProcessor | ||
| * @return scalar | ||
| */ | ||
| public function process(mixed $value, callable $recursiveProcessor): mixed | ||
| { | ||
| // Scalar values are already suitable for logging | ||
| return $value; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.