Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Cookbook
  4. Logging
  5. How to Use Monolog to Write Logs
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Usage
  • Handlers and Channels: Writing Logs to different Locations
    • Using several Handlers
    • Changing the Formatter
  • How to Rotate your Log Files
  • How to Disable Microseconds Precision
  • Adding some extra Data in the Log Messages
    • Adding a Session/Request Token
  • Registering Processors per Handler
  • Registering Processors per Channel

How to Use Monolog to Write Logs

Edit this page

Warning: You are browsing the documentation for Symfony 2.3, which is no longer maintained.

Read the updated version of this page for Symfony 6.3 (the current stable version).

How to Use Monolog to Write Logs

Monolog is a logging library for PHP used by Symfony. It is inspired by the Python LogBook library.

Usage

To log a message simply get the logger service from the container in your controller:

1
2
3
4
5
6
7
8
public function indexAction()
{
    $logger = $this->get('logger');
    $logger->info('I just got the logger');
    $logger->error('An error occurred');

    // ...
}

The logger service has different methods for different logging levels. See LoggerInterface for details on which methods are available.

Handlers and Channels: Writing Logs to different Locations

In Monolog each logger defines a logging channel, which organizes your log messages into different "categories". Then, each channel has a stack of handlers to write the logs (the handlers can be shared).

Tip

When injecting the logger in a service you can use a custom channel control which "channel" the logger will log to.

The basic handler is the StreamHandler which writes logs in a stream (by default in the app/logs/prod.log in the prod environment and app/logs/dev.log in the dev environment).

Monolog comes also with a powerful built-in handler for the logging in prod environment: FingersCrossedHandler. It allows you to store the messages in a buffer and to log them only if a message reaches the action level (error in the configuration provided in the Symfony Standard Edition) by forwarding the messages to another handler.

Using several Handlers

The logger uses a stack of handlers which are called successively. This allows you to log the messages in several ways easily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# app/config/config.yml
monolog:
    handlers:
        applog:
            type: stream
            path: /var/log/symfony.log
            level: error
        main:
            type: fingers_crossed
            action_level: warning
            handler: file
        file:
            type: stream
            level: debug
        syslog:
            type: syslog
            level: error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <monolog:config>
        <monolog:handler
            name="applog"
            type="stream"
            path="/var/log/symfony.log"
            level="error"
        />
        <monolog:handler
            name="main"
            type="fingers_crossed"
            action-level="warning"
            handler="file"
        />
        <monolog:handler
            name="file"
            type="stream"
            level="debug"
        />
        <monolog:handler
            name="syslog"
            type="syslog"
            level="error"
        />
    </monolog:config>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app/config/config.php
$container->loadFromExtension('monolog', array(
    'handlers' => array(
        'applog' => array(
            'type'  => 'stream',
            'path'  => '/var/log/symfony.log',
            'level' => 'error',
        ),
        'main' => array(
            'type'         => 'fingers_crossed',
            'action_level' => 'warning',
            'handler'      => 'file',
        ),
        'file' => array(
            'type'  => 'stream',
            'level' => 'debug',
        ),
        'syslog' => array(
            'type'  => 'syslog',
            'level' => 'error',
        ),
    ),
));

The above configuration defines a stack of handlers which will be called in the order they are defined.

Tip

The handler named "file" will not be included in the stack itself as it is used as a nested handler of the fingers_crossed handler.

Note

If you want to change the config of MonologBundle in another config file you need to redefine the whole stack. It cannot be merged because the order matters and a merge does not allow to control the order.

Changing the Formatter

The handler uses a Formatter to format the record before logging it. All Monolog handlers use an instance of Monolog\Formatter\LineFormatter by default but you can replace it easily. Your formatter must implement Monolog\Formatter\FormatterInterface.

1
2
3
4
5
6
7
8
9
10
# app/config/config.yml
services:
    my_formatter:
        class: Monolog\Formatter\JsonFormatter
monolog:
    handlers:
        file:
            type: stream
            level: debug
            formatter: my_formatter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <services>
        <service id="my_formatter" class="Monolog\Formatter\JsonFormatter" />
    </services>

    <monolog:config>
        <monolog:handler
            name="file"
            type="stream"
            level="debug"
            formatter="my_formatter"
        />
    </monolog:config>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
// app/config/config.php
$container
    ->register('my_formatter', 'Monolog\Formatter\JsonFormatter');

$container->loadFromExtension('monolog', array(
    'handlers' => array(
        'file' => array(
            'type'      => 'stream',
            'level'     => 'debug',
            'formatter' => 'my_formatter',
        ),
    ),
));

How to Rotate your Log Files

Over time, log files can grow to be huge, both while developing and on production. One best-practice solution is to use a tool like the logrotate Linux command to rotate log files before they become too large.

Another option is to have Monolog rotate the files for you by using the rotating_file handler. This handler creates a new log file every day and can also remove old files automatically. To use it, just set the type option of your handler to rotating_file:

1
2
3
4
5
6
7
8
9
10
# app/config/config_dev.yml
monolog:
    handlers:
        main:
            type:  rotating_file
            path:  '%kernel.logs_dir%/%kernel.environment%.log'
            level: debug
            # max number of log files to keep
            # defaults to zero, which means infinite files
            max_files: 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- app/config/config_dev.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <monolog:config>
        <!-- "max_files": max number of log files to keep
             defaults to zero, which means infinite files -->
        <monolog:handler name="main"
            type="rotating_file"
            path="%kernel.logs_dir%/%kernel.environment%.log"
            level="debug"
            max_files="10"
        />
    </monolog:config>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
// app/config/config_dev.php
$container->loadFromExtension('monolog', array(
    'handlers' => array(
        'main' => array(
            'type'  => 'rotating_file',
            'path'  => '%kernel.logs_dir%/%kernel.environment%.log',
            'level' => 'debug',
            // max number of log files to keep
            // defaults to zero, which means infinite files
            'max_files' => 10,
        ),
    ),
));

How to Disable Microseconds Precision

2.11

The use_microseconds option was introduced in MonologBundle 2.11.

Setting the parameter use_microseconds to false forces the logger to reduce the precision in the datetime field of the log messages from microsecond to second, avoiding a call to the microtime(true) function and the subsequent parsing. Disabling the use of microseconds can provide a small performance gain speeding up the log generation. This is recommended for systems that generate a large number of log events.

1
2
3
4
5
6
7
8
# app/config/config.yml
monolog:
    use_microseconds: false
    handlers:
        applog:
            type: stream
            path: /var/log/symfony.log
            level: error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <monolog:config use_microseconds="false">
        <monolog:handler
            name="applog"
            type="stream"
            path="/var/log/symfony.log"
            level="error"
        />
    </monolog:config>
</container>
1
2
3
4
5
6
7
8
9
10
11
// app/config/config.php
$container->loadFromExtension('monolog', array(
    'use_microseconds' => false,
    'handlers' => array(
        'applog' => array(
            'type'  => 'stream',
            'path'  => '/var/log/symfony.log',
            'level' => 'error',
        ),
    ),
));

Adding some extra Data in the Log Messages

Monolog allows you to process the record before logging it to add some extra data. A processor can be applied for the whole handler stack or only for a specific handler.

A processor is simply a callable receiving the record as its first argument. Processors are configured using the monolog.processor DIC tag. See the reference about it.

Adding a Session/Request Token

Sometimes it is hard to tell which entries in the log belong to which session and/or request. The following example will add a unique token for each request using a processor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace AppBundle;

use Symfony\Component\HttpFoundation\Session\Session;

class SessionRequestProcessor
{
    private $session;
    private $token;

    public function __construct(Session $session)
    {
        $this->session = $session;
    }

    public function processRecord(array $record)
    {
        if (null === $this->token) {
            try {
                $this->token = substr($this->session->getId(), 0, 8);
            } catch (\RuntimeException $e) {
                $this->token = '????????';
            }
            $this->token .= '-' . substr(uniqid(), -8);
        }
        $record['extra']['token'] = $this->token;

        return $record;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# app/config/config.yml
services:
    monolog.formatter.session_request:
        class: Monolog\Formatter\LineFormatter
        arguments:
            - "[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%% %%context%% %%extra%%\n"

    monolog.processor.session_request:
        class: AppBundle\SessionRequestProcessor
        arguments:  ['@session']
        tags:
            - { name: monolog.processor, method: processRecord }

monolog:
    handlers:
        main:
            type: stream
            path: '%kernel.logs_dir%/%kernel.environment%.log'
            level: debug
            formatter: monolog.formatter.session_request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <services>
        <service id="monolog.formatter.session_request"
            class="Monolog\Formatter\LineFormatter">

            <argument>[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%% %%context%% %%extra%%&#xA;</argument>
        </service>

        <service id="monolog.processor.session_request"
            class="AppBundle\SessionRequestProcessor">

            <argument type="service" id="session" />
            <tag name="monolog.processor" method="processRecord" />
        </service>
    </services>

    <monolog:config>
        <monolog:handler
            name="main"
            type="stream"
            path="%kernel.logs_dir%/%kernel.environment%.log"
            level="debug"
            formatter="monolog.formatter.session_request"
        />
    </monolog:config>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// app/config/config.php
$container
    ->register(
        'monolog.formatter.session_request',
        'Monolog\Formatter\LineFormatter'
    )
    ->addArgument('[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%% %%context%% %%extra%%\n');

$container
    ->register(
        'monolog.processor.session_request',
        'AppBundle\SessionRequestProcessor'
    )
    ->addArgument(new Reference('session'))
    ->addTag('monolog.processor', array('method' => 'processRecord'));

$container->loadFromExtension('monolog', array(
    'handlers' => array(
        'main' => array(
            'type'      => 'stream',
            'path'      => '%kernel.logs_dir%/%kernel.environment%.log',
            'level'     => 'debug',
            'formatter' => 'monolog.formatter.session_request',
        ),
    ),
));

Note

If you use several handlers, you can also register a processor at the handler level or at the channel level instead of registering it globally (see the following sections).

Registering Processors per Handler

You can register a processor per handler using the handler option of the monolog.processor tag:

1
2
3
4
5
6
7
# app/config/config.yml
services:
    monolog.processor.session_request:
        class: AppBundle\SessionRequestProcessor
        arguments:  ['@session']
        tags:
            - { name: monolog.processor, method: processRecord, handler: main }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <services>
        <service id="monolog.processor.session_request"
            class="AppBundle\SessionRequestProcessor">

            <argument type="service" id="session" />
            <tag name="monolog.processor" method="processRecord" handler="main" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
// app/config/config.php
$container
    ->register(
        'monolog.processor.session_request',
        'AppBundle\SessionRequestProcessor'
    )
    ->addArgument(new Reference('session'))
    ->addTag('monolog.processor', array('method' => 'processRecord', 'handler' => 'main'));

Registering Processors per Channel

You can register a processor per channel using the channel option of the monolog.processor tag:

1
2
3
4
5
6
7
# app/config/config.yml
services:
    monolog.processor.session_request:
        class: AppBundle\SessionRequestProcessor
        arguments:  ['@session']
        tags:
            - { name: monolog.processor, method: processRecord, channel: main }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:monolog="http://symfony.com/schema/dic/monolog"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/monolog
        http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

    <services>
        <service id="monolog.processor.session_request"
            class="AppBundle\SessionRequestProcessor">

            <argument type="service" id="session" />
            <tag name="monolog.processor" method="processRecord" channel="main" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
// app/config/config.php
$container
    ->register(
        'monolog.processor.session_request',
        'AppBundle\SessionRequestProcessor'
    )
    ->addArgument(new Reference('session'))
    ->addTag('monolog.processor', array('method' => 'processRecord', 'channel' => 'main'));
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Code consumes server resources. Blackfire tells you how

    Code consumes server resources. Blackfire tells you how

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Ivan Pepelko, a Symfony contributor

    Thanks Ivan Pepelko (@pepelko) for being a Symfony contributor

    1 commit • 30 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Meilisearch