Skip to content

[12.x] Introduce ContextLogProcessor #54851

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 13 commits into from
Mar 4, 2025
Merged

Conversation

cosmastech
Copy link
Contributor

@cosmastech cosmastech commented Feb 28, 2025

Why

There are two particular things I would like to be able to do with how context is used with logs.

  1. I do not want the Context data set on extra, but instead, want it merged into the existing log context. This makes querying DataDog easier, since it doesn't require me to know if it's a value set in Context versus added via Log::withContext() or added to the log context via Log::info('some important log', ['a-value-i-care-about' => true]).
  2. I want to be able to set a consistent log channel for my actions. For a fuller example, see [11.x] Introduce storing log channel name on Context #54692

Number two is solvable by storing the log channel in the Context's hidden data and pushing an additional processor to the set the log channel based on that.

What is proposed in this PR is that we move the context log processor into a separate class. I can achieve both of my goals with minimal impact to the framework's code. Now in userland, I can easily override the bound processor class. For 99.99% of users, they won't ever use this. For those who want control over how context is merged into the log record, they have an idiomatic way of configuring that.

An example of how I might use this
// inside AppServiceProvider::boot()
$this->app->bind(ContextLogProcessor::class, fn () => new class implements ProcessorInterface {
    public function __invoke(LogRecord $record): LogRecord
    {
        $logChannel = Context::getHidden('log_channel_name');

        return $record->with(
            // allow overriding the context from what's been set on the log
            context: array_merge(Context::all(), $record->context),
            // use the log channel we've set in context, or fallback to the current channel
            channel: $logChannel ?? $record->channel,
        );
    }
});

Other Possible Approaches

  • Create a setContextToLogProcessor on either the LogManager or the Context\Repository. This was the initial approach I took, but it seemed kinda wonky/unwieldy for something so few people might ever use. Binding a concrete class and allowing override makes this much more seamless.
  • Ignore this approach, and let user-land just Log::popProcessor(), Log::pushProcessor() themselves. It's workable, but might not be reliable, since it's possible another processor got pushed by a third-party package
    • A user could also just push another processor to the end and then remove the extra but.... eh.... I find that to be annoying.
  • Allow setting a property that determines whether Context values get merged into extra or context on the Monolog\LogRecord. This feels considerably less robust.

Also...

While playing around with this, I realized you cannot add processors to the single driver via the config. Wouldn't solve the problem, but it seems weird that it doesn't allow for this.

Copy link

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

@cosmastech
Copy link
Contributor Author

cosmastech commented Feb 28, 2025

@timacdonald I still want to add tests for this, but would be curious to get your thoughts on which approach makes the most sense.

edit: I thought about this more, and I think it makes sense to just create a ProcessorInterface in the Context namespace and bind it via the ContextServiceProvider. User land can override it, and there's no need to clutter the LogManager.

edit 2: Added the changes and updated the description.

@cosmastech cosmastech changed the title [12.x] Introduce LogManager::setContextToLogProcessor() [12.x] Introduce LogManager::setAddContextToLogProcessor() Feb 28, 2025
@cosmastech cosmastech changed the title [12.x] Introduce LogManager::setAddContextToLogProcessor() [12.x] Introduce ContextLogProcessor Mar 1, 2025
@cosmastech cosmastech marked this pull request as ready for review March 1, 2025 03:31
@timacdonald
Copy link
Member

timacdonald commented Mar 2, 2025

This seems reasonable to me, is not introducing complexity, and creates some additional and interesting flexibility for Context. I like it!

My only casual thought is we may want to introduce a dedicated ContextLogProcessor interface in the Illuminate\Contracts namespace to bind against, instead of referencing the concrete classname. Re-bind against a concrete class always feels like weird to me.

@cosmastech
Copy link
Contributor Author

Re-bind against a concrete class always feels like weird to me.

Good call. 👍 I agree that feels much better.

@taylorotwell taylorotwell merged commit 78d85fb into laravel:12.x Mar 4, 2025
38 of 39 checks passed
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.

3 participants