Log Management and Analytics

Explore the full capabilities of Log Management and Analytics powered by SolarWinds Loggly

View Product Info

FEATURES

Infrastructure Monitoring Powered by SolarWinds AppOptics

Instant visibility into servers, virtual hosts, and containerized environments

View Infrastructure Monitoring Info

Application Performance Monitoring Powered by SolarWinds AppOptics

Comprehensive, full-stack visibility, and troubleshooting

View Application Performance Monitoring Info

Digital Experience Monitoring Powered by SolarWinds Pingdom

Make your websites faster and more reliable with easy-to-use web performance and digital experience monitoring

View Digital Experience Monitoring Info

PHP Logging Libraries

Ultimate Guide to Logging - Your open-source resource for understanding, analyzing, and troubleshooting system logs

PHP Logging Libraries

Many PHP logging libraries already contain the most common functionality an 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) recommends 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 (such as Laravel, Symfony, or others) likely has a built-in logging library. Have a look at PHP Framework Logging to learn more.

PSR-3 Logging Standard

The PHP Standards Recommendation (PSR)-3 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. This way, you can use any PSR-3 compliant logger in your application and 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 LoggerInterfaceand defining the eight methods
  • Inheriting the AbstractLoggerclass and defining the log method
  • Using the LoggerTraitand 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 often 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 popular frameworks like Laravel, Symfony, and others.

Monolog is one of the more popular logging libraries out there for PHP. It supports logging to different handlers like a database, the browser console, chat solutions like Slack, and log management solutions like Loggly®. You can also specify which level each handler should log. For example, you may want to log error events to a Slack channel while you notify your developer team of fatal errors.

Monolog also supports formatters. If you want to format your data as JSON before sending it 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’s 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 for adding 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 (such as a file, an email, or a database). 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 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 isn’t PSR-3 compatible, but Analog provides a compatible class called Logger 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 the Analog 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.

📝 Note: Log4PHP != Log4j

With all the hype around CVE-2021-44228 (“Log4Shell”) and the fact that Log4j is also an Apache product, it’s worth noting that Log4PHP is NOT directly affected by CVE-2021-44228. A CVE-2021-44228 exploit depends on the Java Naming and Directory Interface (JNDI) that other non-Java languages like PHP don’t use.

However, Log4PHP isn’t PSR-3 compliant, isn’t 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 log formatting for the 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 creating 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);


Last updated: November 2022