Add support for an optional onError callback#17
Conversation
… into feature/psr-logger
… into feature/psr-logger
… into feature/psr-logger
|
@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) |
There was a problem hiding this comment.
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(...)).
There was a problem hiding this comment.
Dependencies belong in constructors... how else can we have dependency inversion?
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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...
});There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
Probably I'd include some info about the request: url, method, and uuid (https://github.com/middlewares/uuid).
There was a problem hiding this comment.
yes, funny that I added it and then forgot to put it back
|
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;
}
} |
|
@oscarotero I like what @shadowhand said, thanks (will add you more frequent here 😄 ) ! I'll just leave the .gitattributes change. |
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
logCallbackoption to fully customize how logging happens (see the changelog and readme).Thanks!