Custom Handlers and Advanced Functionality

Concrete CMS's logging system also contains support for handlers. Handlers are the ways that logged messages make their way to people interested in their contents. By default, Concrete adds a database handler to every Logger, and uses this handler for debug messages and up. That ensures that every logged message makes its way into the Dashboard logs report. However, if you want more control over certain types of log messages, you can use custom handlers.

By default, all messages are logged to Concrete's database adapter. However, let's say we want to take messages that have the severity of alert or higher, and pipe those to a log file (which we consume in some other way.) We just make sure this code exists in application/bootstrap/app.php, or in the on_start() method of a package that's installed:

// This gets the logger singleton that's used throughout Concrete.
$logger = Core::make('log');    
$logger->pushHandler(
    new \Monolog\Handler\RotatingFileHandler(
    '/path/to/file.log', 
    0, 
    \Monolog\Logger::ALERT, 
    false)
);

Then, when a message is logged – whether it comes from our custom code or from Concrete's core code – if it's an alert or more severe, it will skip the database log and go directly into /path/to/file-date.log. If we want to have this message logged to both places, simply omit the last false argument in the RotatingFileHandler constructor. This is the $bubble parameter.

Since Concrete's Logger class subclasses Monolog's Logger class, it supports all the handlers that Monolog supports. That's a long list. You can connect Concrete's logging system to systems like MongoDB, Slack, Sendgrid, Hipchat, and others. For information on handlers, and other Monolog functionality like log processors and line formatters, check out the Monolog documentation.

Advanced Mode

If Logging Settings are set to Advanced Mode in the dashboard (System > Environment > Logging Settings), a custom configuration array from concrete.log.configuration.advanced.configuration will be passed to Monolog Cascade. The array should be in the Monolog Cascade format.

Note: unless you specify loggers within your advanced configuration array, simple configuration will be used.

Here is an example where all core channels (Channels::META_CHANNEL_ALL) are logged to file at the DEBUG level, but the email core channel (Channels::CHANNEL_EMAIL) is logged to the database at the INFO level. If a custom channel is created, you would need to specify that as a key in the loggers array. If logging one the core channels, it can be specified by Channels::CHANNEL_<channel_name> where <channel_name> is one of: EMAIL, EXCEPTIONS, PACKAGES, SECURITY, AUTHENTICATION, PERMISSIONS, SPAM, SITE_ORGANIZATION, NETWORK, USERS, OPERATIONS, API.

<?php
// file: application/config/concrete.php
use Concrete\Core\Logging\Channels;

return [
    "log" => [
        "configuration" => [
            "advanced" => [
                "configuration" => [
                    'version' => 1,
                    'formatters' => [
                        'basic_database' => [
                            'format' => "%message%"
                        ],
                    ],
                    'loggers' => [
                        Channels::META_CHANNEL_ALL => [
                            'handlers' => [
                                'file'
                            ]
                        ],
                        Channels::CHANNEL_EMAIL => [
                            'handlers' => [
                                'database'
                            ]
                        ]
                    ],
                    'handlers' => [
                        'file' => [
                            'class' => 'Monolog\Handler\StreamHandler',
                            'level' => 'DEBUG',
                            'stream' => '/var/log/all.log'
                        ],
                        'database' => [
                            'class' => 'Concrete\Core\Logging\Handler\DatabaseHandler',
                            'level' => 'INFO',
                            'formatter' => 'basic_database'
                        ]
                    ]
                ]
            ]
        ]
    ]
];