Skip to content
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

[9.x] Share logging context across channels and stacks #42276

Merged
merged 3 commits into from
May 9, 2022

Conversation

timacdonald
Copy link
Member

@timacdonald timacdonald commented May 6, 2022

Purpose

This PR bring the ability to share context across all channels and stacks, rather than just one.

Why this feature is useful

Currently when we specify context, it is on a per log or per channel basis.

// per log...

Log::info('per log', ['log' => 'context']);

// per channel...

Log::withContext(['channel' => 'context']);

Log::channel('stderr')->withContext(['channel' => 'context']);

but there is no way to specify context to be shared across all channels and stacks.

This feature is useful in the example currently shown in the docs for the withContext feature of a channel.

The example in the docs being...

class AssignRequestId
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $requestId = (string) Str::uuid();
 
        Log::withContext([
            'request-id' => $requestId
        ]);
 
        return $next($request)->header('Request-Id', $requestId);
    }
}

The problem with this is that it only applies the context the the default channel, but it would be useful if the context was shared across ALL channels and stacks.

Usage

This new feature could be used in a middleware as above, but also in a service provider to apply a unique ID to all invocations of the app, be it from a request or a job or an artisan command.

class AppServiceProvider
{
    public function register()
    {
        //
    }


    public function boot()
    {
        Log::shareContext([
            'invocation-id' => (string) Str::uuid(),
        ]);
    }
}

Now in my application, no matter what logger I use, I'll have the invocation ID in my context.

Log::info(/* ... */);

Log::channel('stderr')->info(/* ... */);

Log::channel('bugsnag')->info(/* ... */);

Other logging systems outside of the Laravel logging system can also hook in and get the shared context to utilise...

$sharedContext = Log::sharedContext();

Notes

  • Not doing anything to the emergency logger. One I couldn't test it, and two if there is something funky happening with the context that is causing the initial logger to fail creation I wouldn't want that to also apply to the emergency logger.
  • Stacks get the shared context at the time they are created, but do not get any additional shared context once resolved - where as channels will always have the latest shared context.
  • Merging is performed in the same manner as withContext i.e. not recursive.

@ankurk91
Copy link
Contributor

ankurk91 commented May 6, 2022

How can we reset this behavior?
How does it work with Octane?

#37847

@timacdonald
Copy link
Member Author

timacdonald commented May 6, 2022

Regarding Octane, you would need to hook into events in the ServiceProvider::boot() method...

$this->app['events']->listen([
    RequestReceived::class,
    TaskReceived::class,
    TickReceived::class,
], fn () => Log::shareContext(['invocation-id' => (string) Str::uuid()]));

We could potentially add some magic into Octane to handle this for you, document it on the Octane documentation, or allow the logger to know about Octane and handle this internally.

I didn't add the ability to reset the shared context as I don't have a use-case for that and didn't want to add the feature without one.

The service provider is also just an example. It would continue to be possible to register a "request-id" in a middleware with this new feature.

@taylorotwell
Copy link
Member

taylorotwell commented May 6, 2022

@timacdonald we would likely need a corresponding PR in Octane to reset the shared context back to its pre-request state after each operation (see other listeners in Octane as examples of this). Marking as draft until we have that worked up as well. 👍

@taylorotwell taylorotwell marked this pull request as draft May 6, 2022 13:33
@timacdonald
Copy link
Member Author

timacdonald commented May 7, 2022

@taylorotwell see: 3e5e28e && laravel/octane#513

note that the new test will fail until this PR is merged / tagged. I've tested locally and got the ✅

@timacdonald timacdonald marked this pull request as ready for review May 7, 2022 09:45
@taylorotwell taylorotwell merged commit 864ef6a into laravel:9.x May 9, 2022
@timacdonald timacdonald deleted the share-context branch May 9, 2022 23:37
@omitobi
Copy link

omitobi commented Dec 18, 2023

I just tested the shareContext() but the logs on my Job does not have the context.
Is this by design or there are other configuration needed?

PS: I tested this on Laravel 10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants