Skip to content

Conversation

@filisko
Copy link
Member

@filisko filisko commented Apr 22, 2025

Hi,

This adds support for an optional PSR-3 compliant logger.

The logger is passed as an optional parameter to the constructor. And it also includes a logCallback option to fully customize how logging happens (see the changelog and readme).

Thanks!

@filisko
Copy link
Member Author

filisko commented Apr 24, 2025

@oscarotero this is done, approve and merge it when you can ;)

* @param FormatterInterface[] $formatters
*/
public function __construct(?array $formatters = null)
public function __construct(?array $formatters = null, ?LoggerInterface $logger = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the logger shouldn't be passed in the constructor because it's an optional feature. It should be a method (for example, $middleware->logger(...)).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependencies belong in constructors... how else can we have dependency inversion?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. But in this case it's an optional feature. People may want to log the errors or not, so I see this as a configuration, not a core dependency of the middleware.

That being said, maybe you're right in your comment about the premise of this feature and it would be better to simply create a specific middleware for this.

@filisko ?

Copy link
Member

@shadowhand shadowhand Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A constructor parameter with a default value of null has no cost. (With PHP named parameters, it doesn't even cost writing null to fill it in.) A method that has to accept a parameter to update the object means the object is no longer readonly. Thus, a method for an optional is far more expensive to create, maintain, and operate than a constructor parameter.

/**
* Set a custom log callback
*/
public function logCallback(callable $callback): self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think logger() could accept a callable or a LoggerInterface, so we don't need two methods for the same purpose.
Other option is ->logger($logger, $callback), but to me is more elegant to have only one handler for logging (the callback can have access to the logger by itself).

Copy link
Member Author

@filisko filisko Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but then it wouldn't be about logging anymore, but a hook for when an error happens, right? Because inside the callback you can put anything, there is no LoggerInterface enforcement anymore so we should change the name.

How does this look like? onError

$errorHandler->onError(function( Throwable $error, ServerRequestInterface $request) use($logger, $other): void {
          $logger->error('Whoops!');
         // $other stuff...
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good to me. Maybe an onError callback is enough to cover all casuistry and we don't need a specific method for logger.

} catch (Throwable $error) {
if ($this->logger) {
if ($this->logCallback) {
($this->logCallback)($this->logger, $error, $request);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the logger is callback, I'd do $this->logger($error, $request). The callback is a callable with access to a logger (or anything else).

$this->logger->critical('Uncaught exception', [
'message' => $error->getMessage(),
'file' => $error->getFile(),
'line' => $error->getLine(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably I'd include some info about the request: url, method, and uuid (https://github.com/middlewares/uuid).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, funny that I added it and then forgot to put it back

@filisko filisko changed the title Add support for an optional PSR-3 logger Add support for an optional onError callback Apr 24, 2025
@shadowhand
Copy link
Member

shadowhand commented Apr 24, 2025

I am actually going to reject the premise of this PR entirely and say that:

Logging exceptions and formatting exceptions are two entirely processes with entirely different goals. This entire PR has become a complicated way to avoid writing a custom middleware:

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        try {
            return $handler->handle($request);
        } catch (Throwable $exception) {
            $this->logger->critical('Uncaught {error}', [
                'error' => $exception->getMessage(),
                'exception' => $exception, // If you use Monolog, this is correct
            ]);

            throw $exception;
        }
    }

@filisko
Copy link
Member Author

filisko commented Apr 24, 2025

@oscarotero I like what @shadowhand said, thanks (will add you more frequent here 😄 ) !

I'll just leave the .gitattributes change.

@filisko filisko closed this Apr 24, 2025
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.

9 participants