How to Configure Monolog to Email Errors

How to Configure Monolog to Email Errors

Caution

This feature is compatible with the new Symfony mailer starting from Symfony 5.1. All previous versions require using SwiftMailer.

Monolog can be configured to send an email when an error occurs within an application. The configuration for this requires a few nested handlers in order to avoid receiving too many emails. This configuration looks complicated at first but each handler is fairly straightforward when it is broken down.

  • YAML

    1. # config/packages/prod/monolog.yaml
    2. monolog:
    3. handlers:
    4. main:
    5. type: fingers_crossed
    6. # 500 errors are logged at the critical level
    7. action_level: critical
    8. # to also log 400 level errors (but not 404's):
    9. # action_level: error
    10. # excluded_http_codes: [404]
    11. handler: deduplicated
    12. deduplicated:
    13. type: deduplication
    14. handler: swift
    15. swift:
    16. type: swift_mailer
    17. from_email: '[email protected]'
    18. to_email: '[email protected]'
    19. # or list of recipients
    20. # to_email: ['[email protected]', '[email protected]', ...]
    21. subject: 'An Error Occurred! %%message%%'
    22. level: debug
    23. formatter: monolog.formatter.html
    24. content_type: text/html
  • XML

    1. <!-- config/packages/prod/monolog.xml -->
    2. <?xml version="1.0" encoding="UTF-8" ?>
    3. <container xmlns="http://symfony.com/schema/dic/services"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xmlns:monolog="http://symfony.com/schema/dic/monolog"
    6. xsi:schemaLocation="http://symfony.com/schema/dic/services
    7. https://symfony.com/schema/dic/services/services-1.0.xsd
    8. http://symfony.com/schema/dic/monolog https://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
    9. <monolog:config>
    10. <!--
    11. 500 errors are logged at the critical level,
    12. to also log 400 level errors (but not 404's):
    13. action-level="error"
    14. And add this child inside this monolog:handler
    15. <monolog:excluded-http-code code="404"/>
    16. -->
    17. <monolog:handler
    18. name="main"
    19. type="fingers_crossed"
    20. action-level="critical"
    21. handler="deduplicated"
    22. />
    23. <monolog:handler
    24. name="deduplicated"
    25. type="deduplication"
    26. handler="swift"
    27. />
    28. <monolog:handler
    29. name="swift"
    30. type="swift_mailer"
    31. from-email="[email protected]"
    32. subject="An Error Occurred! %%message%%"
    33. level="debug"
    34. formatter="monolog.formatter.html"
    35. content-type="text/html">
    36. <monolog:to-email>[email protected]</monolog:to-email>
    37. <!-- or list of recipients -->
    38. <!--
    39. <monolog:to-email>[email protected]</monolog:to-email>
    40. <monolog:to-email>[email protected]</monolog:to-email>
    41. ...
    42. -->
    43. </monolog:handler>
    44. </monolog:config>
    45. </container>
  • PHP

    1. // config/packages/prod/monolog.php
    2. $container->loadFromExtension('monolog', [
    3. 'handlers' => [
    4. 'main' => [
    5. 'type' => 'fingers_crossed',
    6. // 500 errors are logged at the critical level
    7. 'action_level' => 'critical',
    8. // to also log 400 level errors (but not 404's):
    9. // 'action_level' => 'error',
    10. // 'excluded_http_codes' => [404],
    11. 'handler' => 'deduplicated',
    12. ],
    13. 'deduplicated' => [
    14. 'type' => 'deduplication',
    15. 'handler' => 'swift',
    16. ],
    17. 'swift' => [
    18. 'type' => 'swift_mailer',
    19. 'from_email' => '[email protected]',
    20. 'to_email' => '[email protected]',
    21. // or a list of recipients
    22. // 'to_email' => ['[email protected]', '[email protected]', ...],
    23. 'subject' => 'An Error Occurred! %%message%%',
    24. 'level' => 'debug',
    25. 'formatter' => 'monolog.formatter.html',
    26. 'content_type' => 'text/html',
    27. ],
    28. ],
    29. ]);

The main handler is a fingers_crossed handler which means that it is only triggered when the action level, in this case critical is reached. The critical level is only triggered for 5xx HTTP code errors. If this level is reached once, the fingers_crossed handler will log all messages regardless of their level. The handler setting means that the output is then passed onto the deduplicated handler.

Tip

If you want both 400 level and 500 level errors to trigger an email, set the action_level to error instead of critical. See the code above for an example.

The deduplicated handler keeps all the messages for a request and then passes them onto the nested handler in one go, but only if the records are unique over a given period of time (60 seconds by default). If the records are duplicated they are discarded. Adding this handler reduces the amount of notifications to a manageable level, specially in critical failure scenarios. You can adjust the time period using the time option:

  • YAML

    1. # config/packages/prod/monolog.yaml
    2. monolog:
    3. handlers:
    4. # ...
    5. deduplicated:
    6. type: deduplication
    7. # the time in seconds during which duplicate entries are discarded (default: 60)
    8. time: 10
    9. handler: swift
  • XML

    1. <!-- config/packages/prod/monolog.xml -->
    2. <!-- time: the time in seconds during which duplicate entries are discarded (default: 60) -->
    3. <monolog:handler name="deduplicated"
    4. type="deduplication"
    5. time="10"
    6. handler="swift"/>
  • PHP

    1. // config/packages/prod/monolog.php
    2. $container->loadFromExtension('monolog', [
    3. 'handlers' => [
    4. // ...
    5. 'deduplicated' => [
    6. 'type' => 'deduplication',
    7. // the time in seconds during which duplicate entries are discarded (default: 60)
    8. 'time' => 10,
    9. 'handler' => 'swift',
    10. ],
    11. ],
    12. ]);

The messages are then passed to the swift handler. This is the handler that actually deals with emailing you the error. The settings for this are straightforward, the to and from addresses, the formatter, the content type and the subject.

You can combine these handlers with other handlers so that the errors still get logged on the server as well as the emails being sent:

  • YAML

    1. # config/packages/prod/monolog.yaml
    2. monolog:
    3. handlers:
    4. main:
    5. type: fingers_crossed
    6. action_level: critical
    7. handler: grouped
    8. grouped:
    9. type: group
    10. members: [streamed, deduplicated]
    11. streamed:
    12. type: stream
    13. path: '%kernel.logs_dir%/%kernel.environment%.log'
    14. level: debug
    15. deduplicated:
    16. type: deduplication
    17. handler: swift
    18. swift:
    19. type: swift_mailer
    20. from_email: '[email protected]'
    21. to_email: '[email protected]'
    22. subject: 'An Error Occurred! %%message%%'
    23. level: debug
    24. formatter: monolog.formatter.html
    25. content_type: text/html
  • XML

    1. <!-- config/packages/prod/monolog.xml -->
    2. <container xmlns="http://symfony.com/schema/dic/services"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:monolog="http://symfony.com/schema/dic/monolog"
    5. xsi:schemaLocation="http://symfony.com/schema/dic/services
    6. https://symfony.com/schema/dic/services/services-1.0.xsd
    7. http://symfony.com/schema/dic/monolog https://symfony.com/schema/dic/monolog/monolog-1.0.xsd">
    8. <monolog:config>
    9. <monolog:handler
    10. name="main"
    11. type="fingers_crossed"
    12. action_level="critical"
    13. handler="grouped"
    14. />
    15. <monolog:handler
    16. name="grouped"
    17. type="group"
    18. >
    19. <member type="stream"/>
    20. <member type="deduplicated"/>
    21. </monolog:handler>
    22. <monolog:handler
    23. name="stream"
    24. path="%kernel.logs_dir%/%kernel.environment%.log"
    25. level="debug"
    26. />
    27. <monolog:handler
    28. name="deduplicated"
    29. type="deduplication"
    30. handler="swift"
    31. />
    32. <monolog:handler
    33. name="swift"
    34. type="swift_mailer"
    35. from-email="[email protected]"
    36. subject="An Error Occurred! %%message%%"
    37. level="debug"
    38. formatter="monolog.formatter.html"
    39. content-type="text/html">
    40. <monolog:to-email>[email protected]</monolog:to-email>
    41. <!-- or list of recipients -->
    42. <!--
    43. <monolog:to-email>[email protected]</monolog:to-email>
    44. <monolog:to-email>[email protected]</monolog:to-email>
    45. ...
    46. -->
    47. </monolog:handler>
    48. </monolog:config>
    49. </container>
  • PHP

    1. // config/packages/prod/monolog.php
    2. $container->loadFromExtension('monolog', [
    3. 'handlers' => [
    4. 'main' => [
    5. 'type' => 'fingers_crossed',
    6. 'action_level' => 'critical',
    7. 'handler' => 'grouped',
    8. ],
    9. 'grouped' => [
    10. 'type' => 'group',
    11. 'members' => ['streamed', 'deduplicated'],
    12. ],
    13. 'streamed' => [
    14. 'type' => 'stream',
    15. 'path' => '%kernel.logs_dir%/%kernel.environment%.log',
    16. 'level' => 'debug',
    17. ],
    18. 'deduplicated' => [
    19. 'type' => 'deduplication',
    20. 'handler' => 'swift',
    21. ],
    22. 'swift' => [
    23. 'type' => 'swift_mailer',
    24. 'from_email' => '[email protected]',
    25. 'to_email' => '[email protected]',
    26. // or a list of recipients
    27. // 'to_email' => ['[email protected]', '[email protected]', ...],
    28. 'subject' => 'An Error Occurred! %%message%%',
    29. 'level' => 'debug',
    30. 'formatter' => 'monolog.formatter.html',
    31. 'content_type' => 'text/html',
    32. ],
    33. ],
    34. ]);

This uses the group handler to send the messages to the two group members, the deduplicated and the stream handlers. The messages will now be both written to the log file and emailed.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.