10

I am working on my first Symfony based WebApp project. I have configured Symfony to not only write log messages to the different log files but also to send critical error messages immediately as e-mail. This works fine. However I would like to add some additional information to the default log messages to make it easier to find the actual error source.

Example: The Twig file of one pages loads the localized text from a .yml file. The texts contains an %about_link%placeholder that should be replaced by the route/URL to the About page. I forgot to this replacement, so the link did not point to an URL but to %about_link% instead. This leads to an NotFoundHttpException since no rout to %about_link% can be found...

No big deal. But to find the actual page/controller that contains this error was a bit tricky. The default log message shows the following:

[2015-12-14 17:19:36] request.ERROR: Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET /%25about_link%25"" at /long/path/to/symfony/.../RouterListener.php line 176 []

So the exception was thrown in RouterListener.php when trying to find a route to %about_link%. Fine, this does not give me any hint on which page this bad link is located.

Of course the call to the bad route does not have to be located on any page at all. The user could have entered the bad link directly. Symfony would have to store/remember the last page to give any hint about the possible source. So, is it possible to include this information at all?

Additionally I would like to add information about the Host the problem was reported on. I am running two instances of the WebApp: www.my_web_app.xy and betatest.my_web_app.xy and it would be a great help, if the log message would show if it comes from www or from betatest.

Adding this information to log messages I create on myself is no problem, but how can I add this information to messages generated by Symfony or third party code? I would have to intercept the log message somehow before it reaches the log handler. Is this possible?

Andrei Herford
  • 17,570
  • 19
  • 91
  • 225

3 Answers3

19

If you want to add extra information to log entries, you can do this with a processor. With a processor you can modify the record array, before it's parsed by a formatter. The extra part is shown at the end of the log entry.

<?php

namespace AppBundle\Monolog;

use Symfony\Component\HttpFoundation\RequestStack;

class WebProcessor
{
    private $requestStack;

    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    public function processRecord(array $record)
    {
        $request = $this->requestStack->getCurrentRequest();

        if ($request) {
            $record['extra']['host'] = $request->getHost();
            $record['extra']['url'] = $request->getRequestUri();
            // ...
        }

        return $record;
    }
}

Now add it to your services.yml to register it for all log entries:

app.monolog.processor.web:
    class: AppBundle\Monolog\WebProcessor
    arguments: ["@request_stack"]
    tags:
        - { name: monolog.processor, method: processRecord }
Emii Khaos
  • 9,983
  • 3
  • 34
  • 57
  • 4
    Please note that in the above (good) example, the `$request` can also be _null_, e.g. when running a Symfony command. – Czechnology Jul 31 '16 at 10:00
  • @Czechnology : true ! This snippet should be one of the first thing to do in a Symfony projetc, "standard" logs are so useless sometimes. – COil Jan 06 '17 at 13:57
11

Don't reinvent the wheel! There is no need to write your own WebProcessor, since Monolog already has it.

The only thing you have to do is add it to your services and tag it with monolog.processor:

# app/config/services.yml
services:
    Monolog\Processor\WebProcessor:
        tags: ['monolog.processor']

Monolog has even more built-in processors you can use. I decided to add multiple processors in my application:

# app/config/services/monolog.yml (I included services/*.yml in config.yml)
services:
    _defaults:
        tags: ['monolog.processor']
    Monolog\Processor\WebProcessor: ~
    Monolog\Processor\GitProcessor: ~
    Monolog\Processor\MemoryUsageProcessor: ~
    Monolog\Processor\MemoryPeakUsageProcessor: ~
    Monolog\Processor\IntrospectionProcessor: ~
Stephan Vierkant
  • 9,674
  • 8
  • 61
  • 97
  • 1
    It's not reinventing the wheel. The Monolog WebProcessor uses the `$_SERVER` global, while the code above uses the Symfony request. Thus the answer is still valid in a symfony context and serves as a more extended example for everyone searching in general how to add more context to log messages. – Emii Khaos Sep 15 '17 at 15:02
1

You can use a custom formatter to change the output written to the monolog log files. You can find more information about this subject here: http://symfony.com/doc/current/cookbook/logging/monolog.html#changing-the-formatter

Short version: you can create a custom formatter class that implements Monolog\Formatter\FormatterInterface and you can enable it in your config.yml file this way:

# app/config/config.yml
services:
    my_formatter:
        class: Monolog\Formatter\JsonFormatter
monolog:
    handlers:
        file:
            type: stream
            level: debug
            formatter: my_formatter
Wouter Sioen
  • 282
  • 2
  • 4
  • I have no idea why this answer was down voted since it actually gives a very good starting point. Thank you @Wouter for pointing me to the formatters. However the answer of Patrik Karisch is a little closer to what I need, to I will accept this one. – Andrei Herford Dec 15 '15 at 15:35
  • 1
    I downvoted it because of several reasons. 1. copied simly from the symfony doc without providing any value. 2. code example is copied too without providing a usefull formatter how to do it (knowing the given $record is hard to find). 3. formatters are not the way to add extra informations. – Emii Khaos Dec 15 '15 at 19:17
  • Formatters are a way too add extra information. You can inject stuff (like the request) in your formatter since it's just a plain service using dependency injection. and use it to add extra info to your logs. – Wouter Sioen Dec 16 '15 at 10:39
  • I also copied it from the docs to make sure the answer is still relevant if the links change. – Wouter Sioen Dec 16 '15 at 10:41