|
1 |
| -Logging |
2 |
| -======= |
| 1 | +Logging with Monolog |
| 2 | +==================== |
| 3 | + |
| 4 | +Symfony comes with an outside library - called Monolog_ - that allows you to create |
| 5 | +logs that can be stored in a variety of different places. |
| 6 | + |
| 7 | +Logging a Message |
| 8 | +----------------- |
| 9 | + |
| 10 | +To log a message, fetch the ``logger`` service from the container in |
| 11 | +your controller:: |
| 12 | + |
| 13 | + public function indexAction() |
| 14 | + { |
| 15 | + $logger = $this->get('logger'); |
| 16 | + $logger->info('I just got the logger'); |
| 17 | + $logger->error('An error occurred'); |
| 18 | + |
| 19 | + $logger->critical('I left the oven on!', array( |
| 20 | + // include extra "context" info in your logs |
| 21 | + 'cause' => 'in_hurry', |
| 22 | + )); |
| 23 | + |
| 24 | + // ... |
| 25 | + } |
| 26 | + |
| 27 | +The ``logger`` service has different methods for different logging levels/priorities. |
| 28 | +You can configure the logger to do different things based on the *level* of a message |
| 29 | +(e.g. :doc:`send an email when an error occurs </logging/monolog_email>`). |
| 30 | + |
| 31 | +See LoggerInterface_ for a list of all of the methods on the logger. |
| 32 | + |
| 33 | +Where Logs are Stored |
| 34 | +--------------------- |
| 35 | + |
| 36 | +The configuration for *where* logs are stored lives in the specific |
| 37 | +:doc:`environment </configuration/environments>` configuration files: ``config_dev.yml`` |
| 38 | +and ``config_prod.yml``. |
| 39 | + |
| 40 | +By default, log entries are written to the ``app/logs/dev.log`` file when you're in |
| 41 | +the ``dev`` environment. In the ``prod`` environment, logs are written to ``app/logs/prod.log``, |
| 42 | +but *only* during a request where an error or high-priority log entry was made |
| 43 | +(i.e. ``error()`` , ``critical()``, ``alert()`` or ``emergency()``). |
| 44 | + |
| 45 | +To control this, you'll configure different *handlers* that handle log entries, sometimes |
| 46 | +modify them, and ultimately store them. |
| 47 | + |
| 48 | +Handlers: Writing Logs to different Locations |
| 49 | +--------------------------------------------- |
| 50 | + |
| 51 | +The logger has a stack of *handlers*, and each can be used to write the log entries |
| 52 | +to different locations (e.g. files, database, Slack, etc). |
| 53 | + |
| 54 | +.. tip:: |
| 55 | + |
| 56 | + You can *also* configure logging "channels", which are like categories. Each |
| 57 | + channel can have its *own* handlers, which means you can store different log |
| 58 | + messages in different places. See :doc:`/logging/channels_handlers`. |
| 59 | + |
| 60 | +Symfony pre-configures some basic handlers in the ``config_dev.yml`` and ``config_prod.yml`` |
| 61 | +files. Check these out for some real-world examples. |
| 62 | + |
| 63 | +This example uses *two* handlers: ``stream`` (to write to a file) and ``syslog`` |
| 64 | +to write logs using the :phpfunction:`syslog` function: |
| 65 | + |
| 66 | +.. configuration-block:: |
| 67 | + |
| 68 | + .. code-block:: yaml |
| 69 | +
|
| 70 | + # app/config/config.yml |
| 71 | + monolog: |
| 72 | + handlers: |
| 73 | + # this "file_log" key could be anything |
| 74 | + file_log: |
| 75 | + type: stream |
| 76 | + # log to app/logs/(environment).log |
| 77 | + path: "%kernel.logs_dir%/%kernel.environment%.log" |
| 78 | + # log *all* messages (debug is lowest level) |
| 79 | + level: debug |
| 80 | +
|
| 81 | + syslog_handler: |
| 82 | + type: syslog |
| 83 | + # log error-level messages and higher |
| 84 | + level: error |
| 85 | +
|
| 86 | + .. code-block:: xml |
| 87 | +
|
| 88 | + <!-- app/config/config.xml --> |
| 89 | + <?xml version="1.0" encoding="UTF-8" ?> |
| 90 | + <container xmlns="http://symfony.com/schema/dic/services" |
| 91 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 92 | + xmlns:monolog="http://symfony.com/schema/dic/monolog" |
| 93 | + xsi:schemaLocation="http://symfony.com/schema/dic/services |
| 94 | + http://symfony.com/schema/dic/services/services-1.0.xsd |
| 95 | + http://symfony.com/schema/dic/monolog |
| 96 | + http://symfony.com/schema/dic/monolog/monolog-1.0.xsd"> |
| 97 | +
|
| 98 | + <monolog:config> |
| 99 | + <monolog:handler |
| 100 | + name="file_log" |
| 101 | + type="stream" |
| 102 | + path="%kernel.logs_dir%/%kernel.environment%.log" |
| 103 | + level="debug" |
| 104 | + /> |
| 105 | + <monolog:handler |
| 106 | + name="syslog_handler" |
| 107 | + type="syslog" |
| 108 | + level="error" |
| 109 | + /> |
| 110 | + </monolog:config> |
| 111 | + </container> |
| 112 | +
|
| 113 | + .. code-block:: php |
| 114 | +
|
| 115 | + // app/config/config.php |
| 116 | + $container->loadFromExtension('monolog', array( |
| 117 | + 'handlers' => array( |
| 118 | + 'file_log' => array( |
| 119 | + 'type' => 'stream', |
| 120 | + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', |
| 121 | + 'level' => 'debug', |
| 122 | + ), |
| 123 | + 'syslog_handler' => array( |
| 124 | + 'type' => 'syslog', |
| 125 | + 'level' => 'error', |
| 126 | + ), |
| 127 | + ), |
| 128 | + )); |
| 129 | +
|
| 130 | +This defines a *stack* of handlers and each handler is called in the order that it's |
| 131 | +defined. |
| 132 | + |
| 133 | +Handlers that Modify Log Entries |
| 134 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 135 | + |
| 136 | +Instead of writing log files somewhere, *some* handlers are used to filter or modify |
| 137 | +log entries before sending them to *other* handlers. One powerful, built-in handler |
| 138 | +called ``fingers_crossed`` is used in the ``prod`` environment by default. It stores |
| 139 | +*all* log messages during a request but *only* passes them to a second handler if |
| 140 | +one of the messages reaches an ``action_level``. Take this example: |
| 141 | + |
| 142 | +.. configuration-block:: |
| 143 | + |
| 144 | + .. code-block:: yaml |
| 145 | +
|
| 146 | + # app/config/config.yml |
| 147 | + monolog: |
| 148 | + handlers: |
| 149 | + filter_for_errors: |
| 150 | + type: fingers_crossed |
| 151 | + # if *one* log is error or higher, pass *all* to file_log |
| 152 | + action_level: error |
| 153 | + handler: file_log |
| 154 | +
|
| 155 | + # now passed *all* logs, but only if one log is error or higher |
| 156 | + file_log: |
| 157 | + type: stream |
| 158 | + path: "%kernel.logs_dir%/%kernel.environment%.log" |
| 159 | +
|
| 160 | + # still passed *all* logs, and still only logs error or higher |
| 161 | + syslog_handler: |
| 162 | + type: syslog |
| 163 | + level: error |
| 164 | +
|
| 165 | + .. code-block:: xml |
| 166 | +
|
| 167 | + <!-- app/config/config.xml --> |
| 168 | + <?xml version="1.0" encoding="UTF-8" ?> |
| 169 | + <container xmlns="http://symfony.com/schema/dic/services" |
| 170 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 171 | + xmlns:monolog="http://symfony.com/schema/dic/monolog" |
| 172 | + xsi:schemaLocation="http://symfony.com/schema/dic/services |
| 173 | + http://symfony.com/schema/dic/services/services-1.0.xsd |
| 174 | + http://symfony.com/schema/dic/monolog |
| 175 | + http://symfony.com/schema/dic/monolog/monolog-1.0.xsd"> |
| 176 | +
|
| 177 | + <monolog:config> |
| 178 | + <monolog:handler |
| 179 | + name="filter_for_errors" |
| 180 | + type="fingers_crossed" |
| 181 | + action-level="error" |
| 182 | + handler="file_log" |
| 183 | + /> |
| 184 | + <monolog:handler |
| 185 | + name="file_log" |
| 186 | + type="stream" |
| 187 | + path="%kernel.logs_dir%/%kernel.environment%.log" |
| 188 | + level="debug" |
| 189 | + /> |
| 190 | + <monolog:handler |
| 191 | + name="syslog_handler" |
| 192 | + type="syslog" |
| 193 | + level="error" |
| 194 | + /> |
| 195 | + </monolog:config> |
| 196 | + </container> |
| 197 | +
|
| 198 | + .. code-block:: php |
| 199 | +
|
| 200 | + // app/config/config.php |
| 201 | + $container->loadFromExtension('monolog', array( |
| 202 | + 'handlers' => array( |
| 203 | + 'filter_for_errors' => array( |
| 204 | + 'type' => 'fingers_crossed', |
| 205 | + 'action_level' => 'error', |
| 206 | + 'handler' => 'file_log', |
| 207 | + ), |
| 208 | + 'file_log' => array( |
| 209 | + 'type' => 'stream', |
| 210 | + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', |
| 211 | + 'level' => 'debug', |
| 212 | + ), |
| 213 | + 'syslog_handler' => array( |
| 214 | + 'type' => 'syslog', |
| 215 | + 'level' => 'error', |
| 216 | + ), |
| 217 | + ), |
| 218 | + )); |
| 219 | +
|
| 220 | +Now, if even one log entry has an ``error`` level or higher, then *all* log entries |
| 221 | +for that request are saved to a file via the ``file_log`` handler. That means that |
| 222 | +your log file will contain *all* the details about the problematic request - making |
| 223 | +debugging much easier! |
| 224 | + |
| 225 | +.. tip:: |
| 226 | + |
| 227 | + The handler named "file_log" will not be included in the stack itself as |
| 228 | + it is used as a nested handler of the ``fingers_crossed`` handler. |
| 229 | + |
| 230 | +.. note:: |
| 231 | + |
| 232 | + If you want to override the ``monolog`` configuration via another config |
| 233 | + file, you will need to redefine the entire ``handlers`` stack. The configuration |
| 234 | + from the two files cannot be merged because the order matters and a merge does |
| 235 | + not allow to control the order. |
| 236 | + |
| 237 | +All Built-in Handlers |
| 238 | +--------------------- |
| 239 | + |
| 240 | +Monolog comes with *many* built-in handlers for emailing logs, sending them to Loggly, |
| 241 | +or notifying you in Slack. These are documented inside of MonologBundle itself. For |
| 242 | +a full list, see `Monolog Configuration`_. |
| 243 | + |
| 244 | +How to Rotate your Log Files |
| 245 | +---------------------------- |
| 246 | + |
| 247 | +Over time, log files can grow to be *huge*, both while developing and on |
| 248 | +production. One best-practice solution is to use a tool like the `logrotate`_ |
| 249 | +Linux command to rotate log files before they become too large. |
| 250 | + |
| 251 | +Another option is to have Monolog rotate the files for you by using the |
| 252 | +``rotating_file`` handler. This handler creates a new log file every day |
| 253 | +and can also remove old files automatically. To use it, just set the ``type`` |
| 254 | +option of your handler to ``rotating_file``: |
| 255 | + |
| 256 | +.. configuration-block:: |
| 257 | + |
| 258 | + .. code-block:: yaml |
| 259 | +
|
| 260 | + # app/config/config_dev.yml |
| 261 | + monolog: |
| 262 | + handlers: |
| 263 | + main: |
| 264 | + type: rotating_file |
| 265 | + path: '%kernel.logs_dir%/%kernel.environment%.log' |
| 266 | + level: debug |
| 267 | + # max number of log files to keep |
| 268 | + # defaults to zero, which means infinite files |
| 269 | + max_files: 10 |
| 270 | +
|
| 271 | + .. code-block:: xml |
| 272 | +
|
| 273 | + <!-- app/config/config_dev.xml --> |
| 274 | + <?xml version="1.0" encoding="UTF-8" ?> |
| 275 | + <container xmlns="http://symfony.com/schema/dic/services" |
| 276 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 277 | + xmlns:monolog="http://symfony.com/schema/dic/monolog" |
| 278 | + xsi:schemaLocation="http://symfony.com/schema/dic/services |
| 279 | + http://symfony.com/schema/dic/services/services-1.0.xsd |
| 280 | + http://symfony.com/schema/dic/monolog |
| 281 | + http://symfony.com/schema/dic/monolog/monolog-1.0.xsd"> |
| 282 | +
|
| 283 | + <monolog:config> |
| 284 | + <!-- "max_files": max number of log files to keep |
| 285 | + defaults to zero, which means infinite files --> |
| 286 | + <monolog:handler name="main" |
| 287 | + type="rotating_file" |
| 288 | + path="%kernel.logs_dir%/%kernel.environment%.log" |
| 289 | + level="debug" |
| 290 | + max_files="10" |
| 291 | + /> |
| 292 | + </monolog:config> |
| 293 | + </container> |
| 294 | +
|
| 295 | + .. code-block:: php |
| 296 | +
|
| 297 | + // app/config/config_dev.php |
| 298 | + $container->loadFromExtension('monolog', array( |
| 299 | + 'handlers' => array( |
| 300 | + 'main' => array( |
| 301 | + 'type' => 'rotating_file', |
| 302 | + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', |
| 303 | + 'level' => 'debug', |
| 304 | + // max number of log files to keep |
| 305 | + // defaults to zero, which means infinite files |
| 306 | + 'max_files' => 10, |
| 307 | + ), |
| 308 | + ), |
| 309 | + )); |
| 310 | +
|
| 311 | +Adding extra Data to each Log (e.g. a unique request token) |
| 312 | +----------------------------------------------------------- |
| 313 | + |
| 314 | +Monolog also supports *processors*: functions that can dynamically add extra |
| 315 | +information to your log entries. |
| 316 | + |
| 317 | +See :doc:`/logging/processors` for details. |
3 | 318 |
|
4 | 319 | .. toctree::
|
5 | 320 | :maxdepth: 1
|
6 | 321 | :glob:
|
7 | 322 |
|
8 | 323 | logging/*
|
| 324 | + |
| 325 | +.. _Monolog: https://github.com/Seldaek/monolog |
| 326 | +.. _LoggerInterface: https://github.com/php-fig/log/blob/master/Psr/Log/LoggerInterface.php |
| 327 | +.. _`logrotate`: https://fedorahosted.org/logrotate/ |
| 328 | +.. _`Monolog Configuration`: https://github.com/symfony/monolog-bundle/blob/master/DependencyInjection/Configuration.php#L25 |
0 commit comments