Logging
Logging in Concrete CMS
Logging is crucial in development. It provides insight into processes not easily observable, like command line or AJAX operations. Concrete features built-in logging for various purposes:
- Development Logging: Tracks values and states during code development to troubleshoot issues.
- Auditive Logging: Records events such as emails sent or pages deleted.
- Error Logging: Automatically logs serious system errors.
Access log entries via Dashboard > Reports > Logs
Error and email logging are automatic, but effective logging depends on its implementation in custom code or add-ons. Concrete supports channels and logging levels, allowing for differentiated handling of log entries based on severity or channel.
Basic Logging in Concrete CMS
Logging in Concrete CMS is straightforward. Here's how to log messages to the Dashboard:
\Log::addInfo('This is an informative message');
\Log::addWarning('Uh oh.');
\Log::addAlert('Red alert!');
Use the Log facade without the backslash by importing it:
use Log;
// ...
Log::addDebug('This is a debug level.');
Messages are logged with the specified severity level to the "Application" channel.
Severity Levels
Concrete uses the PSR-3 standard and Monolog library, supporting these severities:
- Debug
- Info
- Notice
- Warning
- Error
- Critical
- Alert
- Emergency
Use Log::add<Severity>('Message')
to log with a specific severity. Dashboard search includes severity filtering.
Logging Objects
To log objects, convert them to strings first. Here is an example with a Concrete\Core\User\UserInfo
object:
$ui = \UserInfo::getByID(1);
\Log::addInfo('Information about the admin user: ' . var_dump_safe($ui, false));
Use var_dump_safe
for Concrete objects, setting the $echo
argument to false to avoid crashing PHP with large object sizes.
Logger Usage in Classes
To use the default logger in classes with dependency injection:
namespace MyVendor\Something;
use Concrete\Core\Logging\Logger;
class MyClass {
protected $logger;
public function __construct(Logger $logger) {
$this->logger = $logger;
}
}
Create the class via the application container:
$myClass = $this->app->make('MyVendor\Something\MyClass');
This gives MyClass
a configured logger for the application channel.
Outside a controller, create an instance of the container:
$app = Facade::getFacadeApplication();
Or use:
$myClass = \Core::make('MyVendor\Something\MyClass');
Using a Different Logging Channel
To use a non-default channel when injecting the Logger:
class MyClass {
protected $logger;
public function __construct(Logger $logger) {
$logger = $logger->withName('shopping_cart');
$this->logger = $logger;
}
}
Or subclass the core logger:
class ShoppingCartLogger extends Concrete\Core\Logging\Logger {
public function __construct() {
parent::__construct('shopping_cart');
}
}
class MyClass {
protected $logger;
public function __construct(ShoppingCartLogger $logger) {
$this->logger = $logger;
}
}
Custom Handlers in Concrete CMS Logging
Concrete CMS's logging system supports custom handlers, allowing different handling of log messages. By default, Concrete uses a database handler for all logs, saving messages from debug level upwards to the Dashboard logs report. You can create custom handlers for specific needs.
For instance, to route alert severity messages to a file, add this in application/bootstrap/app.php
or in a package's on_start()
method:
$logger = Core::make('log');
$logger->pushHandler(
new \Monolog\Handler\RotatingFileHandler(
'/path/to/file.log',
0,
\Monolog\Logger::ALERT,
false)
);
Messages with alert severity or higher will go into /path/to/file-date.log
and skip the database. To log to both the file and database, omit the false
in the RotatingFileHandler
constructor.
Concrete's Logger, being a Monolog subclass, supports various handlers, including those for MongoDB, Slack, Sendgrid, and Hipchat. For more, see Monolog documentation.
Advanced Logging Configuration
In Advanced Mode (System > Environment > Logging Settings), you can use a custom configuration array in Monolog Cascade format. Unless specific loggers are defined in this array, simple configuration applies.
Example configuration to log all core channels to a file at DEBUG
level and the email channel to the database at INFO
level:
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'
]
]
]
]
]
]
];
This configuration details how core and custom channels can be managed differently for logging purposes.