mirror of
https://github.com/symfony/symfony-docs.git
synced 2026-03-23 16:22:10 +01:00
484 lines
18 KiB
ReStructuredText
484 lines
18 KiB
ReStructuredText
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 <service-psr4-loader>`.
|
|
|
|
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 </logging/monolog_email>`).
|
|
|
|
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
|
|
|
|
<!-- 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>
|
|
<!-- this "file_log" key could be anything -->
|
|
<monolog:handler name="file_log"
|
|
type="stream"
|
|
path="%kernel.logs_dir%/%kernel.environment%.log"
|
|
level="debug"/><!-- log *all* messages (debug is lowest level) -->
|
|
|
|
<monolog:handler name="syslog_handler"
|
|
type="syslog"
|
|
level="error"/><!-- log error-level messages and higher -->
|
|
</monolog:config>
|
|
</container>
|
|
|
|
.. 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. Each handler can define a ``priority``
|
|
(default ``0``) to control its position in the stack. Handlers with a higher
|
|
priority are called first, while those with the same priority keep the order in
|
|
which they are defined:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# config/packages/prod/monolog.yaml
|
|
monolog:
|
|
handlers:
|
|
file_log:
|
|
type: stream
|
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
|
|
|
syslog_handler:
|
|
type: syslog
|
|
priority: 10 # called first
|
|
|
|
.. code-block:: 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"
|
|
/>
|
|
|
|
<!-- called first -->
|
|
<monolog:handler name="syslog_handler"
|
|
type="syslog"
|
|
priority="10"
|
|
/>
|
|
</monolog:config>
|
|
</container>
|
|
|
|
.. code-block:: php
|
|
|
|
// config/packages/prod/monolog.php
|
|
use Psr\Log\LogLevel;
|
|
use Symfony\Config\MonologConfig;
|
|
|
|
return static function (MonologConfig $monolog): void {
|
|
$monolog->handler('file_log')
|
|
->type('stream')
|
|
->path('%kernel.logs_dir%/%kernel.environment%.log')
|
|
;
|
|
|
|
$monolog->handler('syslog_handler')
|
|
->type('syslog')
|
|
->priority(10) // called first
|
|
;
|
|
};
|
|
|
|
.. note::
|
|
|
|
When adding handlers in other configuration files, it's recommended to set
|
|
an explicit priority to ensure they are ordered as expected.
|
|
|
|
.. _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
|
|
|
|
<!-- 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>
|
|
<!-- if *one* log is error or higher, pass *all* to file_log -->
|
|
<monolog:handler name="filter_for_errors"
|
|
type="fingers_crossed"
|
|
action-level="error"
|
|
handler="file_log"
|
|
/>
|
|
|
|
<!-- now passed *all* logs, but only if one log is error or higher -->
|
|
<monolog:handler name="file_log"
|
|
type="stream"
|
|
path="%kernel.logs_dir%/%kernel.environment%.log"
|
|
level="debug"
|
|
/>
|
|
|
|
<!-- still passed *all* logs, and still only logs error or higher -->
|
|
<monolog:handler name="syslog_handler"
|
|
type="syslog"
|
|
level="error"
|
|
/>
|
|
</monolog:config>
|
|
</container>
|
|
|
|
.. 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
|
|
|
|
<!-- 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>
|
|
|
|
.. 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 <services-autoconfigure>`,
|
|
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 <monolog-autowire-channels>`
|
|
or use the ``monolog.logger`` tag with the ``channel`` property as explained in the
|
|
:ref:`Dependency Injection reference <dic_tags-monolog>`.
|
|
|
|
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/3.x/src/DependencyInjection/Configuration.php
|
|
.. _`STDERR PHP stream`: https://www.php.net/manual/en/features.commandline.io-streams.php
|