Logging

While CakePHP core Configure Class settings can really help you seewhat’s happening under the hood, there are certain times thatyou’ll need to log data to the disk in order to find out what’sgoing on. With technologies like SOAP, AJAX, and REST APIs, debugging can berather difficult.

Logging can also be a way to find out what’s been going on in yourapplication over time. What search terms are being used? What sortsof errors are my users being shown? How often is a particular querybeing executed?

Logging data in CakePHP is easy - the log() function is provided by theLogTrait, which is the common ancestor for many CakePHP classes. Ifthe context is a CakePHP class (Controller, Component, View,…),you can log your data. You can also use Log::write() directly.See Writing to Logs.

Logging Configuration

Configuring Log should be done during your application’s bootstrap phase.The config/app.php file is intended for just this. You can defineas many or as few loggers as your application needs. Loggers should beconfigured using Cake\Log\Log. An example would be:

  1. use Cake\Log\Log;
  2.  
  3. // Short classname
  4. Log::setConfig('debug', [
  5. 'className' => 'File',
  6. 'path' => LOGS,
  7. 'levels' => ['notice', 'info', 'debug'],
  8. 'file' => 'debug',
  9. ]);
  10.  
  11. // Fully namespaced name.
  12. Log::setConfig('error', [
  13. 'className' => 'Cake\Log\Engine\FileLog',
  14. 'path' => LOGS,
  15. 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
  16. 'file' => 'error',
  17. ]);

The above creates two loggers. One named debug the other named error.Each is configured to handle different levels of messages. They also store theirlog messages in separate files, so it’s easy to separate debug/notice/info logsfrom more serious errors. See the section on Using Levels for moreinformation on the different levels and what they mean.

Once a configuration is created you cannot change it. Instead you should dropthe configuration and re-create it using Cake\Log\Log::drop() andCake\Log\Log::setConfig().

It is also possible to create loggers by providing a closure. This is usefulwhen you need full control over how the logger object is built. The closurehas to return the constructed logger instance. For example:

  1. Log::setConfig('special', function () {
  2. return new \Cake\Log\Engine\FileLog(['path' => LOGS, 'file' => 'log']);
  3. });

Configuration options can also be provided as a DSN string. This isuseful when working with environment variables or PaaS providers:

  1. Log::setConfig('error', [
  2. 'url' => 'file:///?levels[]=warning&levels[]=error&file=error',
  3. ]);

Note

Loggers are required to implement the Psr\Log\LoggerInterface interface.

Creating Log Adapters

Log adapters can be part of your application, or part ofplugins. If for example you had a database logger calledDatabaseLog. As part of your application it would be placed insrc/Log/Engine/DatabaseLog.php. As part of a plugin it would be placed inplugins/LoggingPack/src/Log/Engine/DatabaseLog.php. To configure logadapters you should use Cake\Log\Log::setConfig(). For exampleconfiguring our DatabaseLog would look like:

  1. // For src/Log
  2. Log::setConfig('otherFile', [
  3. 'className' => 'Database',
  4. 'model' => 'LogEntry',
  5. // ...
  6. ]);
  7.  
  8. // For plugin called LoggingPack
  9. Log::setConfig('otherFile', [
  10. 'className' => 'LoggingPack.Database',
  11. 'model' => 'LogEntry',
  12. // ...
  13. ]);

When configuring a log adapter the className parameter is used tolocate and load the log handler. All of the other configurationproperties are passed to the log adapter’s constructor as an array.

  1. namespace App\Log\Engine;
  2. use Cake\Log\Engine\BaseLog;
  3.  
  4. class DatabaseLog extends BaseLog
  5. {
  6. public function __construct($options = [])
  7. {
  8. parent::__construct($options);
  9. // ...
  10. }
  11.  
  12. public function log($level, $message, array $context = [])
  13. {
  14. // Write to the database.
  15. }
  16. }

CakePHP requires that all logging adapters implement Psr\Log\LoggerInterface.The class CakeLogEngineBaseLog is an easy way to satisfy theinterface as it only requires you to implement the log() method.

FileLog engine takes the following options:

  • size Used to implement basic log file rotation. If log file sizereaches specified size the existing file is renamed by appending timestampto filename and new log file is created. Can be integer bytes value orhuman readable string values like ‘10MB’, ‘100KB’ etc. Defaults to 10MB.
  • rotate Log files are rotated specified times before being removed.If value is 0, old versions are removed rather then rotated. Defaults to 10.
  • mask Set the file permissions for created files. If left empty the defaultpermissions are used.

Warning

Engines have the suffix Log. You should avoid class names like SomeLogLogwhich include the suffix twice at the end.

Note

You should configure loggers during bootstrapping. config/app.php is theconventional place to configure log adapters.

In debug mode missing directories will be automatically created to avoid unnecessaryerrors thrown when using the FileEngine.

Error and Exception Logging

Errors and Exceptions can also be logged. By configuring the correspondingvalues in your app.php file. Errors will be displayed when debug is trueand logged when debug is false. To log uncaught exceptions, set the logoption to true. See Configuration for more information.

Interacting with Log Streams

You can introspect the configured streams withCake\Log\Log::configured(). The return of configured() is anarray of all the currently configured streams. You can removestreams using Cake\Log\Log::drop(). Once a log stream has beendropped it will no longer receive messages.

Using the FileLog Adapter

As its name implies FileLog writes log messages to files. The level of logmessage being written determines the name of the file the message is stored in.If a level is not supplied, LOG_ERR is used which writes to theerror log. The default log location is logs/$level.log:

  1. // Executing this inside a CakePHP class
  2. $this->log("Something didn't work!");
  3.  
  4. // Results in this being appended to logs/error.log
  5. // 2007-11-02 10:22:02 Error: Something didn't work!

The configured directory must be writable by the web server user inorder for logging to work correctly.

You can configure additional/alternate FileLog locations when configuringa logger. FileLog accepts a path which allows forcustom paths to be used:

  1. Log::setConfig('custom_path', [
  2. 'className' => 'File',
  3. 'path' => '/path/to/custom/place/'
  4. ]);

Warning

If you do not configure a logging adapter, log messages will not be stored.

Logging to Syslog

In production environments it is highly recommended that you setup your system touse syslog instead of the files logger. This will perform much better as anywrites will be done in a (almost) non-blocking fashion and your operating systemlogger can be configured separately to rotate files, pre-process writes or usea completely different storage for your logs.

Using syslog is pretty much like using the default FileLog engine, you just needto specify Syslog as the engine to be used for logging. The followingconfiguration snippet will replace the default logger with syslog, this shouldbe done in the bootstrap.php file:

  1. Log::setConfig('default', [
  2. 'engine' => 'Syslog'
  3. ]);

The configuration array accepted for the Syslog logging engine understands thefollowing keys:

  • format: An sprintf template string with two placeholders, the first onefor the error level, and the second for the message itself. This key isuseful to add additional information about the server or process in thelogged message. For example: %s - Web Server 1 - %s will look likeerror - Web Server 1 - An error occurred in this request afterreplacing the placeholders.
  • prefix: An string that will be prefixed to every logged message.
  • flag: An integer flag to be used for opening the connection to thelogger, by default LOG_ODELAY will be used. See openlog documentationfor more options
  • facility: The logging slot to use in syslog. By default LOG_USER isused. See syslog documentation for more options

Writing to Logs

Writing to the log files can be done in 2 different ways. The firstis to use the static Cake\Log\Log::write() method:

  1. Log::write('debug', 'Something did not work');

The second is to use the log() shortcut function available on anyclass using the LogTrait. Calling log() will internally callLog::write():

  1. // Executing this inside a class using LogTrait
  2. $this->log("Something did not work!", 'debug');

All configured log streams are written to sequentially each timeCake\Log\Log::write() is called. If you have not configured anylogging adapters log() will return false and no log messages will bewritten.

Using Levels

CakePHP supports the standard POSIX set of logging levels. Each level representsan increasing level of severity:

  • Emergency: system is unusable
  • Alert: action must be taken immediately
  • Critical: critical conditions
  • Error: error conditions
  • Warning: warning conditions
  • Notice: normal but significant condition
  • Info: informational messages
  • Debug: debug-level messages

You can refer to these levels by name when configuring loggers, and when writinglog messages. Alternatively, you can use convenience methods likeCake\Log\Log::error() to clearly indicate the logginglevel. Using a level that is not in the above levels will result in anexception.

Note

When levels is set to an empty value in a logger’s configuration, itwill take messages of any level.

Logging Scopes

Often times you’ll want to configure different logging behavior for differentsubsystems or parts of your application. Take for example an e-commerce shop.You’ll probably want to handle logging for orders and payments differently thanyou do other less critical logs.

CakePHP exposes this concept as logging scopes. When log messages are writtenyou can include a scope name. If there is a configured logger for that scope,the log messages will be directed to those loggers. For example:

  1. // Configure logs/shops.log to receive all levels, but only
  2. // those with `orders` and `payments` scope.
  3. Log::setConfig('shops', [
  4. 'className' => 'File',
  5. 'path' => LOGS,
  6. 'levels' => [],
  7. 'scopes' => ['orders', 'payments'],
  8. 'file' => 'shops.log',
  9. ]);
  10.  
  11. // Configure logs/payments.log to receive all levels, but only
  12. // those with `payments` scope.
  13. Log::setConfig('payments', [
  14. 'className' => 'File',
  15. 'path' => LOGS,
  16. 'levels' => [],
  17. 'scopes' => ['payments'],
  18. 'file' => 'payments.log',
  19. ]);
  20.  
  21. Log::warning('this gets written only to shops.log', ['scope' => ['orders']]);
  22. Log::warning('this gets written to both shops.log and payments.log', ['scope' => ['payments']]);

Scopes can also be passed as a single string or a numerically indexed array.Note that using this form will limit the ability to pass more data as context:

  1. Log::warning('This is a warning', ['orders']);
  2. Log::warning('This is a warning', 'payments');

Note

When scopes is set to an empty array or null in a logger’sconfiguration, it will take messages of any scope. Setting it to falsewill only match messages without scope.

Log API

  • class Cake\Log\Log
  • A simple class for writing to logs.
  • static Cake\Log\Log::setConfig($key, $config)

Parameters:

  • $name (string) – Name for the logger being connected, usedto drop a logger later on.
  • $config (array) – Array of configuration information andconstructor arguments for the logger.

Get or set the configuration for a Logger. See Logging Configuration formore information.

  • static Cake\Log\Log::configured

Returns:An array of configured loggers.

Get the names of the configured loggers.

  • static Cake\Log\Log::drop($name)

Parameters:

  • $name (string) – Name of the logger you wish to no longer receivemessages.
  • static Cake\Log\Log::write($level, $message, $scope = [])
  • Write a message into all the configured loggers.$level indicates the level of log message being created.$message is the message of the log entry being written to.$scope is the scope(s) a log message is being created in.
  • static Cake\Log\Log::levels

Call this method without arguments, eg: Log::levels() to obtain currentlevel configuration.

Convenience Methods

The following convenience methods were added to log $message with theappropriate log level.

  • static Cake\Log\Log::emergency($message, $scope = [])
  • static Cake\Log\Log::alert($message, $scope = [])
  • static Cake\Log\Log::critical($message, $scope = [])
  • static Cake\Log\Log::error($message, $scope = [])
  • static Cake\Log\Log::warning($message, $scope = [])
  • static Cake\Log\Log::notice($message, $scope = [])
  • static Cake\Log\Log::info($message, $scope = [])
  • static Cake\Log\Log::debug($message, $scope = [])

Logging Trait

  • trait Cake\Log\LogTrait
  • A trait that provides shortcut methods for logging
  • Cake\Log\LogTrait::log($msg, $level = LOG_ERR)
  • Log a message to the logs. By default messages are logged asERROR messages. If $msg isn’t a string it will be converted withprint_r before being logged.

Using Monolog

Monolog is a popular logger for PHP. Since it implements the same interfaces asthe CakePHP loggers, it is easy to use in your application as the defaultlogger.

After installing Monolog using composer, configure the logger using theLog::setConfig() method:

  1. // config/bootstrap.php
  2.  
  3. use Monolog\Logger;
  4. use Monolog\Handler\StreamHandler;
  5.  
  6. Log::setConfig('default', function () {
  7. $log = new Logger('app');
  8. $log->pushHandler(new StreamHandler('path/to/your/combined.log'));
  9. return $log;
  10. });
  11.  
  12. // Optionally stop using the now redundant default loggers
  13. Log::drop('debug');
  14. Log::drop('error');

Use similar methods if you want to configure a different logger for your console:

  1. // config/bootstrap_cli.php
  2.  
  3. use Monolog\Logger;
  4. use Monolog\Handler\StreamHandler;
  5.  
  6. Log::setConfig('default', function () {
  7. $log = new Logger('cli');
  8. $log->pushHandler(new StreamHandler('path/to/your/combined-cli.log'));
  9. return $log;
  10. });
  11.  
  12. // Optionally stop using the now redundant default CLI loggers
  13. Configure::delete('Log.debug');
  14. Configure::delete('Log.error');

Note

When using a console specific logger, make sure to conditionally configureyour application logger. This will prevent duplicate log entries.