Technical Resources
Educational Resources
APM Integrated Experience
Connect with Us
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.
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.
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:
LoggerInterfaceand
defining the eight methodsAbstractLoggerclass
and defining the log
methodLoggerTraitand
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 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
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.
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');
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.');
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 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 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