Logging
Symfony comes with a minimalist PSR-3 logger: Logger
.In conformance with the twelve-factor app methodology, it sends messages starting from theWARNING
level to stderr).
The minimal log level can be changed by setting the SHELL_VERBOSITY
environment variable:
SHELL_VERBOSITY value | Minimum log level |
---|---|
-1 | ERROR |
1 | NOTICE |
2 | INFO |
3 | DEBUG |
The minimum log level, the default output and the log format can also be changed bypassing the appropriate arguments to the constructor of Logger
.To do so, override the "logger" service definition.
Logging a Message
To log a message, inject the default logger in your controller:
- use Psr\Log\LoggerInterface;
- public function index(LoggerInterface $logger)
- {
- $logger->info('I just got the logger');
- $logger->error('An error occurred');
- $logger->critical('I left the oven on!', [
- // include extra "context" info in your logs
- 'cause' => 'in_hurry',
- ]);
- // ...
- }
The logger
service has different methods for different logging levels/priorities.See LoggerInterface for a list of all of the methods on the logger.
Monolog
Symfony integrates seamlessly with Monolog, the most popular PHP logginglibrary, to create and store log messages in a variety of different placesand trigger various actions.
For instance, using Monolog you can configure the logger to do different things based on thelevel of a message (e.g. send an email when an error occurs).
Run this command to install the Monolog based logger before using it:
- $ composer require symfony/monolog-bundle
The following sections assume that Monolog is installed.
Where Logs are Stored
By default, log entries are written to the var/log/dev.log
file when you're inthe dev
environment. In the prod
environment, logs are written to var/log/prod.log
,but only during a request where an error or high-priority log entry was made(i.e. error()
, critical()
, alert()
or emergency()
).
To control this, you'll configure different handlers that handle log entries, sometimesmodify them, and ultimately store them.
Handlers: Writing Logs to different Locations
The logger has a stack of handlers, and each can be used to write the log entriesto different locations (e.g. files, database, Slack, etc).
Tip
You can also configure logging "channels", which are like categories. Eachchannel can have its own handlers, which means you can store different logmessages in different places. See How to Log Messages to different Files.
Symfony pre-configures some basic handlers in the default monolog.yaml
config files. Check these out for some real-world examples.
This example uses two handlers: stream
(to write to a file) and syslog
to write logs using the syslog
function:
- YAML
- # config/packages/prod/monolog.yaml
- monolog:
- handlers:
- # this "file_log" key could be anything
- file_log:
- type: stream
- # log to var/log/(environment).log
- path: "%kernel.logs_dir%/%kernel.environment%.log"
- # log *all* messages (debug is lowest level)
- level: debug
- syslog_handler:
- type: syslog
- # log error-level messages and higher
- level: error
- XML
- <!-- config/packages/prod/monolog.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
- https://symfony.com/schema/dic/services/services-1.0.xsd
- http://symfony.com/schema/dic/monolog
- https://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
- <monolog:config>
- <monolog:handler
- name="file_log"
- type="stream"
- path="%kernel.logs_dir%/%kernel.environment%.log"
- level="debug"
- />
- <monolog:handler
- name="syslog_handler"
- type="syslog"
- level="error"
- />
- </monolog:config>
- </container>
- PHP
- // config/packages/prod/monolog.php
- $container->loadFromExtension('monolog', [
- 'handlers' => [
- 'file_log' => [
- 'type' => 'stream',
- 'path' => '%kernel.logs_dir%/%kernel.environment%.log',
- 'level' => 'debug',
- ],
- 'syslog_handler' => [
- 'type' => 'syslog',
- 'level' => 'error',
- ],
- ],
- ]);
This defines a stack of handlers and each handler is called in the order that it'sdefined.
Handlers that Modify Log Entries
Instead of writing log files somewhere, some handlers are used to filter or modifylog entries before sending them to other handlers. One powerful, built-in handlercalled fingerscrossed
is used in the prod
environment by default. It stores_all log messages during a request but only passes them to a second handler ifone of the messages reaches an action_level
. Take this example:
- YAML
- # config/packages/prod/monolog.yaml
- monolog:
- handlers:
- filter_for_errors:
- type: fingers_crossed
- # if *one* log is error or higher, pass *all* to file_log
- action_level: error
- handler: file_log
- # now passed *all* logs, but only if one log is error or higher
- file_log:
- type: stream
- path: "%kernel.logs_dir%/%kernel.environment%.log"
- # still passed *all* logs, and still only logs error or higher
- syslog_handler:
- type: syslog
- level: error
- XML
- <!-- config/packages/prod/monolog.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
- https://symfony.com/schema/dic/services/services-1.0.xsd
- http://symfony.com/schema/dic/monolog
- https://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
- <monolog:config>
- <monolog:handler
- name="filter_for_errors"
- type="fingers_crossed"
- action-level="error"
- handler="file_log"
- />
- <monolog:handler
- name="file_log"
- type="stream"
- path="%kernel.logs_dir%/%kernel.environment%.log"
- level="debug"
- />
- <monolog:handler
- name="syslog_handler"
- type="syslog"
- level="error"
- />
- </monolog:config>
- </container>
- PHP
- // config/packages/prod/monolog.php
- $container->loadFromExtension('monolog', [
- 'handlers' => [
- 'filter_for_errors' => [
- 'type' => 'fingers_crossed',
- 'action_level' => 'error',
- 'handler' => 'file_log',
- ],
- 'file_log' => [
- 'type' => 'stream',
- 'path' => '%kernel.logs_dir%/%kernel.environment%.log',
- 'level' => 'debug',
- ],
- 'syslog_handler' => [
- 'type' => 'syslog',
- 'level' => 'error',
- ],
- ],
- ]);
Now, if even one log entry has an error
level or higher, then all log entriesfor that request are saved to a file via the filelog
handler. That means thatyour log file will contain _all the details about the problematic request - makingdebugging much easier!
Tip
The handler named "file_log" will not be included in the stack itself asit is used as a nested handler of the fingers_crossed
handler.
Note
If you want to override the monolog
configuration via another configfile, you will need to redefine the entire handlers
stack. The configurationfrom the two files cannot be merged because the order matters and a merge doesnot allow to control the order.
All Built-in Handlers
Monolog comes with many built-in handlers for emailing logs, sending them to Loggly,or notifying you in Slack. These are documented inside of MonologBundle itself. Fora full list, see Monolog Configuration.
How to Rotate your Log Files
Over time, log files can grow to be huge, both while developing and onproduction. One best-practice solution is to use a tool like the logrotateLinux command to rotate log files before they become too large.
Another option is to have Monolog rotate the files for you by using therotating_file
handler. This handler creates a new log file every dayand can also remove old files automatically. To use it, set the type
option of your handler to rotating_file
:
- YAML
- # config/packages/prod/monolog.yaml
- 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
- XML
- <!-- config/packages/prod/monolog.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
- https://symfony.com/schema/dic/services/services-1.0.xsd
- http://symfony.com/schema/dic/monolog
- https://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>
- PHP
- // config/packages/prod/monolog.php
- $container->loadFromExtension('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,
- ],
- ],
- ]);
Using a Logger inside a Service
If your application uses service autoconfiguration,any service whose class implements Psr\Log\LoggerAwareInterface
willreceive a call to its method setLogger()
with the default logger servicepassed as a service.
New in version 4.2: The automatic call to setLogger()
when implementing LoggerAwareInterface
was introduced in Symfony 4.2.
If you want to use in your own services a pre-configured logger which uses aspecific channel (app
by default), you can either autowire monolog channelsor use the monolog.logger
tag with the channel
property as explained in theDependency Injection reference.
Adding extra Data to each Log (e.g. a unique request token)
Monolog also supports processors: functions that can dynamically add extrainformation to your log entries.
See How to Add extra Data to Log Messages via a Processor for details.