Logging ======= Symfony comes with two minimalist `PSR-3`_ loggers: :class:`Symfony\\Component\\HttpKernel\\Log\\Logger` for the HTTP context and :class:`Symfony\\Component\\Console\\Logger\\ConsoleLogger` for the CLI context. In conformance with `the twelve-factor app methodology`_, they send messages starting from the ``WARNING`` 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 by passing the appropriate arguments to the constructor of :class:`Symfony\\Component\\HttpKernel\\Log\\Logger` and :class:`Symfony\\Component\\Console\\Logger\\ConsoleLogger`. The :class:`Symfony\\Component\\HttpKernel\\Log\\Logger` class is available through the ``logger`` service. To pass your configuration, you can :ref:`override the "logger" service definition `. For more information about ``ConsoleLogger``, see :doc:`/components/console/logger`. Logging a Message ----------------- To log a message, inject the default logger in your controller or service:: use Psr\Log\LoggerInterface; // ... public function index(LoggerInterface $logger): Response { $logger->info('I just got the logger'); $logger->error('An error occurred'); // log messages can also contain placeholders, which are variable names // wrapped in braces whose values are passed as the second argument $logger->debug('User {userId} has logged in', [ 'userId' => $this->getUserId(), ]); $logger->critical('I left the oven on!', [ // include extra "context" info in your logs 'cause' => 'in_hurry', ]); // ... } Adding placeholders to log messages is recommended because: * It's easier to check log messages because many logging tools group log messages that are the same except for some variable values inside them; * It's much easier to translate those log messages; * It's better for security, because escaping can then be done by the implementation in a context-aware fashion. 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 logging library, to create and store log messages in a variety of different places and trigger various actions. For instance, using Monolog you can configure the logger to do different things based on the *level* of a message (e.g. :doc:`send an email when an error occurs `). Run this command to install the Monolog based logger before using it: .. code-block:: terminal $ 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 in the ``dev`` environment. In the ``prod`` environment, logs are written to `STDERR PHP stream`_, which works best in modern containerized applications deployed to servers without disk write permissions. If you prefer to store production logs in a file, set the ``path`` of your log handler(s) to the path of the file to use (e.g. ``var/log/prod.log``). Handlers: Writing Logs to different Locations --------------------------------------------- The logger has a stack of *handlers*, and each can be used to write the log entries to different locations (e.g. files, database, Slack, etc). .. tip:: You can *also* configure logging "channels", which are like categories. Each channel can have its *own* handlers, which means you can store different log messages in different places. See :doc:`/logging/channels_handlers`. 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 :phpfunction:`syslog` function: .. configuration-block:: .. code-block:: 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 .. code-block:: xml .. code-block:: php // config/packages/prod/monolog.php use Psr\Log\LogLevel; use Symfony\Config\MonologConfig; return static function (MonologConfig $monolog): void { // this "file_log" key could be anything $monolog->handler('file_log') ->type('stream') // log to var/logs/(environment).log ->path('%kernel.logs_dir%/%kernel.environment%.log') // log *all* messages (LogLevel::DEBUG is lowest level) ->level(LogLevel::DEBUG); $monolog->handler('syslog_handler') ->type('syslog') // log error-level messages and higher ->level(LogLevel::ERROR); }; This defines a *stack* of handlers and each handler is called in the order that it's defined. .. note:: If you want to override the ``monolog`` configuration via another config file, you will need to redefine the entire ``handlers`` stack. The configuration from the two files cannot be merged because the order matters and a merge does not allow you to control the order. .. _logging-handler-fingers_crossed: Handlers that Modify Log Entries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Instead of writing log files somewhere, *some* handlers are used to filter or modify log entries before sending them to *other* handlers. One powerful, built-in handler called ``fingers_crossed`` is used in the ``prod`` environment by default. It stores *all* log messages during a request but *only* passes them to a second handler if one of the messages reaches an ``action_level``. Take this example: .. configuration-block:: .. code-block:: 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 .. code-block:: xml .. code-block:: php // config/packages/prod/monolog.php use Psr\Log\LogLevel; use Symfony\Config\MonologConfig; return static function (MonologConfig $monolog): void { $monolog->handler('filter_for_errors') ->type('fingers_crossed') // if *one* log is error or higher, pass *all* to file_log ->actionLevel(LogLevel::ERROR) ->handler('file_log') ; // now passed *all* logs, but only if one log is error or higher $monolog->handler('file_log') ->type('stream') ->path('%kernel.logs_dir%/%kernel.environment%.log') ->level(LogLevel::DEBUG) ; // still passed *all* logs, and still only logs error or higher $monolog->handler('syslog_handler') ->type('syslog') ->level(LogLevel::ERROR) ; }; Now, if even one log entry has an ``LogLevel::ERROR`` level or higher, then *all* log entries for that request are saved to a file via the ``file_log`` handler. That means that your log file will contain *all* the details about the problematic request - making debugging much easier! .. tip:: The handler named "file_log" will not be included in the stack itself as it is used as a nested handler of the ``fingers_crossed`` handler. 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. For a full list, see `Monolog Configuration`_. 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, set the ``type`` option of your handler to ``rotating_file``: .. configuration-block:: .. code-block:: 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 .. code-block:: xml .. code-block:: php // config/packages/prod/monolog.php use Psr\Log\LogLevel; use Symfony\Config\MonologConfig; return static function (MonologConfig $monolog): void { $monolog->handler('main') ->type('rotating_file') ->path('%kernel.logs_dir%/%kernel.environment%.log') ->level(LogLevel::DEBUG) // max number of log files to keep // defaults to zero, which means infinite files ->maxFiles(10); }; Using a Logger inside a Service ------------------------------- If your application uses :ref:`service autoconfiguration `, any service whose class implements ``Psr\Log\LoggerAwareInterface`` will receive a call to its method ``setLogger()`` with the default logger service passed as a service. If you want to use in your own services a pre-configured logger which uses a specific channel (``app`` by default), you can either :ref:`autowire monolog channels ` or use the ``monolog.logger`` tag with the ``channel`` property as explained in the :ref:`Dependency Injection reference `. Adding extra Data to each Log (e.g. a unique request token) ----------------------------------------------------------- Monolog also supports *processors*: functions that can dynamically add extra information to your log entries. See :doc:`/logging/processors` for details. Handling Logs in Long Running Processes --------------------------------------- During long running processes, logs can be accumulated into Monolog and cause some buffer overflow, memory increase or even non logical logs. Monolog in-memory data can be cleared using the ``reset()`` method on a ``Monolog\Logger`` instance. This should typically be called between every job or task that a long running process is working through. Learn more ---------- .. toctree:: :maxdepth: 1 logging/monolog_email logging/channels_handlers logging/formatter logging/processors logging/handlers logging/monolog_exclude_http_codes logging/monolog_console .. _`the twelve-factor app methodology`: https://12factor.net/logs .. _`PSR-3`: https://www.php-fig.org/psr/psr-3/ .. _`stderr`: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) .. _`Monolog`: https://github.com/Seldaek/monolog .. _`LoggerInterface`: https://github.com/php-fig/log/blob/master/src/LoggerInterface.php .. _`logrotate`: https://github.com/logrotate/logrotate .. _`Monolog Configuration`: https://github.com/symfony/monolog-bundle/blob/master/DependencyInjection/Configuration.php#L25 .. _`STDERR PHP stream`: https://www.php.net/manual/en/features.commandline.io-streams.php