Remix.run Logo
wesammikhail 5 hours ago

The way I have solved for this in my own framework in PHP is by having a Logging class with the following interface

  interface LoggerInterface {

    // calls $this->system(LEVEL_ERROR, ...);

    public function exception(Throwable $e): void;

    // Typical system logs

    public function system(string $level, string $message, ?string $category = null, mixed ...$extra): void;

    // User specific logs that can be seen in the user's "my history" 

    public function log(string $event, int|string|null $user_id = null, ?string $category = null, ?string $message = null, mixed ...$extra): void;
  }
I also have a global exception handler that is registered at application bootstrap time that takes any exception that happens at runtime and runs $logger->exception($e);

There is obviously a tiny bit more of boilerplating to this to ensure reliability, but it works so well that I can't live without it anymore. The logs are then inserted into a wide DB table with all the field one could ever want to examine thanks to the variadic parameter.

hu3 4 hours ago | parent [-]

Nice. I guess you write logs on the "final" block of a global try/catch/final?

Something like:

  try {
    // handle request code
  } catch (...) {
    // add exceptions to log
  } final {
    // insert logs into DB
  }
wesammikhail 4 hours ago | parent [-]

I used to do it like that and it worked really well but I changed the flow to where exceptions are actually part of the control flow of the app using PHP's set_exception_handler(), set_error_handler() and register_shutdown_function().

Example, lets say a user forgot to provide a password when authenticating, then I will throw a ClientSideException(400, "need password yada yada");

That exception will bubble up to the exception_handler that logs and outputs the proper message to the screen. Similarly if ANY exception is thrown, regardless of where it originated, the same will happen.

When you embrace exceptions as control flow rather than try to avoid them, suddenly everything gets 10x easier and I end up writing much less code overall.

hu3 2 hours ago | parent [-]

I love Exceptions as control flow! Thanks for the suggestion.

I too use specialized exceptions. Some have friendly messages that can be displayed to the user, like "Please fill the password". But critical exceptions display a generic error to the user ("Ops, sorry something went wrong on our side...") but log specifics to devs, like database connection errors, for example.