Ultimate Guide to Logging

Your open-source resource for understanding, analyzing, and troubleshooting system logs

PHP Logging Libraries

Many PHP logging libraries offer the most common functionality that any application might need; however, some libraries have advantages over others and some have taken a unique approach to logging. To standardize the solution, the PHP-FIG (PHP Framework Interop Group) recommended a logging interface that frameworks can use as their logging API or build a custom logging system using it.

The framework you’ve chosen for your application (ala Laravel, Symfony, and others) likely has a logging library built-in. Have a look at PHP Framework Logging to learn more.

PSR-3 Logging Standard

The PSR-3 (PHP Standards Recommendation) logging standard defines a logging interface with eight methods (debug, info, notice, warning, error, critical, alert, emergency). The message passed to those functions should be a string or an object with a __toString method to cast it into a string. You can read more about the PSR-3 logging standard in the official documentation.

PSR-3 Logger Interface Specification

The PSR-3 standardizes the logging process through a common interface for logging. That way, you can use any PSR-3 compliant logger in your application and you maintain the flexibility to switch to a different logger in the future. You can read more about the full PSR-3 specification on the specification page on Github.

If you’re using Composer, you can include the ‘psr/log‘ package and integrate it with your project in one of the following ways:

  • Implementing the LoggerInterface and defining the eight methods
  • Inheriting the AbstractLogger class and defining the ‘log’ method
  • Using the LoggerTrait and defining the log method (Read more about traits in the documentation.)

Of course, instead of writing your own Logger classes, it’s much easier to use an existing package such as Monolog. Getting started with Monolog is easy, particularly if you’re already using Composer to manage libraries. Check out the documentation for basic usage.

Monolog

Monolog is PSR-3 compliant and has integration components for most popular frameworks like Laravel, Symfony, and others.

Monolog is one of, if not the best, logging library out there for PHP. It supports logging to different handlers like database, browser console, chat solutions like Slack, log management solutions like Loggly, etc. You can also specify what level each handler should log. For example, you may want to log error events to a Slack channel while you want to notify your developers team on fatal errors.

Monolog also supports formatters. If you want to format your data as JSON before sending them to a service like Loggly, you can use the JsonFormatter class to do the job. You can read more about Monolog formatters in the documentation.

Here is an example of how to set up Monolog to log to a file.

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

 

// create a log channel

$log = new Logger('name');

$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));

 

// add records to the log

$log->warning('Foo');

$log->error('Bar');

Monolog also provides ‘Preprocessors’ to add some data to the message before it’s logged. You have a list of preprocessors that can add specific details about your system like memory usage, processor, server details, etc.

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Handler\BrowserConsoleHandler;

use Monolog\Processor\WebProcessor;

 

$logger = new Logger('general');

$logger->pushProcessor(new WebProcessor); // pushing the web server preprocessor

$browserHandler = new BrowserConsoleHandler(Logger::INFO);

$logger->pushHandler($browserHandler);

 

$logger->info("message");

 

// Output to the browser console

Analog

The Analog package is a minimal logging package that doesn’t try to add all optional features like log formatters and processors. Through static access to the ‘Analog‘ class, you can configure the log handler (file, email, database, etc.). You can check the list of handlers in the documentation.

Text File

The file handler writes messages to a log file.

use Analog\Analog;

use Analog\Handler\File;

 

Analog::handler(File::init('/tmp/php.log'));

 

Analog::log('log message');

Email

The email handler uses the system’s mail command to send a log message in an email.

use Analog\Analog;

use Analog\Handler\Mail;

 

Analog::handler(Mail::init(

'developer@example.com',    // to

'Logging from dev machine', // subject

'logging@localhost.dev'     // from

));

 

Analog::warning('API is not responding.');

Custom Handler

The handler method also accepts an anonymous function (also called a closure) as a handler. The following example creates a handler to submit a simple log message to your Loggly account.

Analog::handler (function ($message) {

$headers = array('Content-Type: application/json');

$url = "https://logs-01.loggly.com/inputs/<LOGGLY_TOKEN>";

 

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_POST, true);

curl_setopt($ch, CURLOPT_POSTFIELDS, $message);

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

curl_exec($ch);

});

The Analog class is not PSR-3 compatible, but Analog provides a compatible class called Logger that you can use if you want to keep a good level of abstraction. It simply implements the Psr\Log\LoggerInterface and calls the Analog class as shown above. For more information, see Analog’s documentation.

KLogger

KLogger allows you to log to a specific file and format the output to fit your requirements. Here’s a simple example.

$logger = new Katzgrau\KLogger\Logger('/var/log/my_app', Psr\Log\LogLevel::INFO, array(

'logFormat' => '{date} - {level} - {message}',

'filename'  => 'error_log'

));

 

$logger->info('message');

 

// Output

2015-09-20 10:52:13.808123 INFO message

KLogger is PSR-3 compliant and supports a list of options for formatting. Be sure to check the documentation for more details.

Log4PHP

Log4PHP is an Apache Foundation package. It provides most of the functionality mentioned earlier like formatting, logging to different destinations, and more. It uses a configuration file and can attach one or more handlers (called “appenders” in Log4PHP) to the Logger class. However, Log4PHP isn’t PSR-3 compliant, is not actively developed, and doesn’t use namespacing for its classes, making it harder to integrate into large projects. For new projects, you should use a more active package like Monolog. Nevertheless, let’s start exploring it using a simple example.

$logger = Logger::getLogger('general'); // 'general' is our logging channel name

$logger->info("INFO message");

To configure the logger, you have the option to use XML, INI or PHP.  We’ll be using PHP to configure the logger in these examples, but you can read more about using XML and INI files in the documentation.

class AppLogConfigurator implements LoggerConfigurator

{

public function configure(LoggerHierarchy $hierarchy, $input = null)

{

$consoleAppender = new LoggerAppenderConsole(); // create a new console appender

 

$consoleAppender->activateOptions(); // activate the appender

 

$rootLogger = $hierarchy->getRootLogger(); // holds multiple loggers if needed

$rootLogger->addAppender($consoleAppender); // add our appenders

 

}

}

 

Logger::configure([], new AppLogConfigurator); // use the specified configuration

$logger = Logger::getLogger('general'); // create a new logger

 

$logger->info("INFO message");

$logger->error("ERROR message");

The above code will log both the info and error messages.

You can use layouts to configure logs formatting for appender. The following example will format the console appender using the ‘LoggerLayoutPattern’ class to include the date, file, line number, and log message. You can read more about layouts in the documentation.

class AppLogConfigurator implements LoggerConfigurator

{

public function configure(LoggerHierarchy $hierarchy, $input = null)

{

// Note that %n inserts a newline.

$layout = new LoggerLayoutPattern();

$layout->setConversionPattern("%date{Y-m-d h:i:s} - %file:%line - %msg%n");

$layout->activateOptions();

 

$consoleAppender = new LoggerAppenderConsole();

$consoleAppender->setLayout($layout);

$consoleAppender->activateOptions();

 

$rootLogger = $hierarchy->getRootLogger();

$rootLogger->addAppender($consoleAppender);

}

}

 

Logger::configure([], new AppLogConfigurator);

$logger = Logger::getLogger('general');

 

$logger->info("INFO message");

$logger->error("ERROR message");

 

// Output

 

2015-09-21 10:58:58 - /Users/admin/Desktop/www/loggly/index.php:25 - INFO message

2015-09-21 10:58:58 - /Users/admin/Desktop/www/loggly/index.php:26 - ERROR message

If you try to log an object, it will be logged using the PHP ‘var_dump‘ function. You may alter this behavior by using a predefined renderer or create your own. See  the official documentation for more examples.

class Person {

public $firstName;

public $lastName;

public $age;

}

 

$person = new Person();

$person->firstName = 'John';

$person->lastName = 'Doe';

$person->age = 37;

$logger = Logger::getLogger('main');

$logger->info($person);