diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php index a019fae67c75..5bb9d7e81c5f 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php @@ -25,6 +25,7 @@ use Illuminate\Http\Middleware\TrustProxies; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\JsonApi\JsonApiResource; +use Illuminate\Log\Context\Repository; use Illuminate\Mail\Markdown; use Illuminate\Queue\Console\WorkCommand; use Illuminate\Queue\Queue; @@ -187,6 +188,7 @@ protected function tearDownTheTestEnvironment(): void PreventRequestsDuringMaintenance::flushState(); Queue::createPayloadUsing(null); RegisterProviders::flushState(); + Repository::flushState(); Sleep::fake(false); TrimStrings::flushState(); TrustProxies::flushState(); diff --git a/src/Illuminate/Log/Context/ContextLogProcessor.php b/src/Illuminate/Log/Context/ContextLogProcessor.php index 9ac3e97a77dd..ff5eb45b3d1c 100644 --- a/src/Illuminate/Log/Context/ContextLogProcessor.php +++ b/src/Illuminate/Log/Context/ContextLogProcessor.php @@ -23,9 +23,17 @@ public function __invoke(LogRecord $record): LogRecord return $record; } - return $record->with(extra: [ - ...$record->extra, - ...$app->get(ContextRepository::class)->all(), - ]); + $repository = $app->get(ContextRepository::class); + + return match ($repository->writesContextTo()) { + 'extra' => $record->with(extra: [ + ...$record->extra, + ...$repository->all(), + ]), + 'context' => $record->with(context: [ + ...$repository->all(), + ...$record->context, + ]), + }; } } diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index 8bcfd4eafc46..ee73f3734357 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -47,6 +47,13 @@ class Repository */ protected static $handleUnserializeExceptionsUsing; + /** + * The structured log location to write repository data to. + * + * @var 'extra'|'context' + */ + public static string $writeContextTo = 'extra'; + /** * Create a new Context instance. */ @@ -616,6 +623,14 @@ public function handleUnserializeExceptionsUsing($callback) return $this; } + /** + * @return 'context'|'extra' + */ + public function writesContextTo() + { + return static::$writeContextTo; + } + /** * Flush all context data. * @@ -699,4 +714,12 @@ public function hydrate($context) return $this; } + + /** + * Flush the state of the repository. + */ + public static function flushState(): void + { + self::$writeContextTo = 'extra'; + } } diff --git a/src/Illuminate/Support/Facades/Context.php b/src/Illuminate/Support/Facades/Context.php index be57f00fa6d0..8bfeb34429b1 100644 --- a/src/Illuminate/Support/Facades/Context.php +++ b/src/Illuminate/Support/Facades/Context.php @@ -2,6 +2,8 @@ namespace Illuminate\Support\Facades; +use Illuminate\Log\Context\Repository; + /** * @method static bool has(string $key) * @method static bool missing(string $key) @@ -17,30 +19,30 @@ * @method static array onlyHidden(array $keys) * @method static array except(array $keys) * @method static array exceptHidden(array $keys) - * @method static \Illuminate\Log\Context\Repository add(string|array $key, mixed $value = null) - * @method static \Illuminate\Log\Context\Repository addHidden(string|array $key, mixed $value = null) + * @method static Repository add(string|array $key, mixed $value = null) + * @method static Repository addHidden(string|array $key, mixed $value = null) * @method static mixed remember(string $key, mixed $value) * @method static mixed rememberHidden(string $key, mixed $value) - * @method static \Illuminate\Log\Context\Repository forget(string|array $key) - * @method static \Illuminate\Log\Context\Repository forgetHidden(string|array $key) - * @method static \Illuminate\Log\Context\Repository addIf(string $key, mixed $value) - * @method static \Illuminate\Log\Context\Repository addHiddenIf(string $key, mixed $value) - * @method static \Illuminate\Log\Context\Repository push(string $key, mixed ...$values) + * @method static Repository forget(string|array $key) + * @method static Repository forgetHidden(string|array $key) + * @method static Repository addIf(string $key, mixed $value) + * @method static Repository addHiddenIf(string $key, mixed $value) + * @method static Repository push(string $key, mixed ...$values) * @method static mixed pop(string $key) - * @method static \Illuminate\Log\Context\Repository pushHidden(string $key, mixed ...$values) + * @method static Repository pushHidden(string $key, mixed ...$values) * @method static mixed popHidden(string $key) - * @method static \Illuminate\Log\Context\Repository increment(string $key, int $amount = 1) - * @method static \Illuminate\Log\Context\Repository decrement(string $key, int $amount = 1) + * @method static Repository increment(string $key, int $amount = 1) + * @method static Repository decrement(string $key, int $amount = 1) * @method static bool stackContains(string $key, mixed $value, bool $strict = false) * @method static bool hiddenStackContains(string $key, mixed $value, bool $strict = false) * @method static mixed scope(callable $callback, array $data = [], array $hidden = []) * @method static bool isEmpty() - * @method static \Illuminate\Log\Context\Repository dehydrating(callable $callback) - * @method static \Illuminate\Log\Context\Repository hydrated(callable $callback) - * @method static \Illuminate\Log\Context\Repository handleUnserializeExceptionsUsing(callable|null $callback) - * @method static \Illuminate\Log\Context\Repository flush() - * @method static \Illuminate\Log\Context\Repository|mixed when(\Closure|mixed|null $value = null, callable|null $callback = null, callable|null $default = null) - * @method static \Illuminate\Log\Context\Repository|mixed unless(\Closure|mixed|null $value = null, callable|null $callback = null, callable|null $default = null) + * @method static Repository dehydrating(callable $callback) + * @method static Repository hydrated(callable $callback) + * @method static Repository handleUnserializeExceptionsUsing(callable|null $callback) + * @method static Repository flush() + * @method static Repository|mixed when(\Closure|mixed|null $value = null, callable|null $callback = null, callable|null $default = null) + * @method static Repository|mixed unless(\Closure|mixed|null $value = null, callable|null $callback = null, callable|null $default = null) * @method static void macro(string $name, object|callable $macro) * @method static void mixin(object $mixin, bool $replace = true) * @method static bool hasMacro(string $name) @@ -58,6 +60,22 @@ class Context extends Facade */ protected static function getFacadeAccessor() { - return \Illuminate\Log\Context\Repository::class; + return Repository::class; + } + + /** + * Write context data under the structured log's "extra" key. + */ + public static function writeContextToLogExtra(): void + { + Repository::$writeContextTo = 'extra'; + } + + /** + * Write context data under the structured log's "context" key. + */ + public static function writeContextToLogContext(): void + { + Repository::$writeContextTo = 'context'; } } diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index 45a9fceca1e9..a0df2034196c 100644 --- a/tests/Log/ContextTest.php +++ b/tests/Log/ContextTest.php @@ -632,6 +632,21 @@ public function test_can_rebind_to_separate_class() file_put_contents($path, ''); } + public function test_can_set_the_context_output_location() + { + $path = storage_path('logs/laravel.log'); + file_put_contents($path, ''); + Context::writeContextToLogContext(); + + Context::add(['value_from_context' => 'hello', 'tim' => 'macdonald']); + + Log::info('This is an info log.', ['value_from_context' => 'bye']); + $log = Str::after(file_get_contents($path), '] '); + $this->assertSame('testing.INFO: This is an info log. {"value_from_context":"bye","tim":"macdonald"}', Str::trim($log)); + + file_put_contents($path, ''); + } + public function test_it_increments_a_counter() { Context::increment('foo');