In application development or maintenance, logging is very important. It gives you the right amount of data that you need to help you monitor your application, logs, databases, code, and web services. Monolog is the existing standard logging library for PHP. It is most popular in PHP frameworks such as Laravel and Symfony, where it implements a common interface for logging libraries.
This article talks about the step-by-step process of using PHP Monolog in your application.
The basic way to use Monolog in PHP is to install the latest version of the Composer library
in your project using this command. You can download it here: https://getcomposer.org/.
sudo php composer.phar require monolog/monolog
composer require monolog/monolog
In case you are not using composer, download the PSR-3 source code and install to your application.
Make sure that you are using namespaces (to avoid a headache) so that you don’t require a lot of files.
First, when you create a logger, channel name should be included, so that you can distinguish your list of loggers.
$logger = new MonologLogger('channel-name'); $app->container->logger = $logger;
In the example above, ‘channel-name’ should be included in every log entry. This way you can easily look up and filter the entries; createanother channel for each component. Examples of these channels might include ‘database’, ‘security’, ’business’, and others.
Loggers by nature don’t know how to handle logs, but we also have what are called handlers that can handle logs.
require_once(DIR.'/vendor/autoload.php'); use MonologLogger; use MonologHandlerStreamHandler; $logger = new Logger('channel-name'); $logger->pushHandler(new StreamHandler(DIR.'/app.log', Logger::DEBUG)); $logger->info('This is a log! ^_^ '); $logger->warning('This is a log warning! ^_^ '); $logger->error('This is a log error! ^_^ ');
If you are using PHP 7.4 you will need to use this slightly modified code:
require __DIR__ . '/vendor/autoload.php';
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
$logger = new Logger('channel-name'); $logger->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::DEBUG));
Handlers can be pushed as many as you need to a Logger instance. These types of handler will decide the kind of logs to be handled. In the example provided above, StreamHandler, reports entries in the file system app.log.
An intensity level is added to each log entry, along with channel name to allow you to check and filter the entries.
As illustrated in RFC 5424 which describes the syslog protocol, the following levels of intensity are applied in Monolog.
An info log entry of a channel called ‘channel-name’, for example, looks like this:
[2018-07-05 11:10:35] channel-name.INFO: This is a log entry! [2018-07-09 02:58:27] channel-name.WARNING: This is a log warning! ^_^ [] [] [2018-07-09 02:58:27] channel-name.ERROR: This is a log error! ^_^ [] []
Monolog stores one add for each type of intensity level, likely, error, notice, warning, info, and so on.
The following code is the basic sample setup to log to a file and to firephp on the Debug level.
require_once(DIR.'/vendor/autoload.php'); use MonologLogger; use MonologHandlerStreamHandler; use MonologHandlerFirePHPHandler; $logger = new Logger('logger'); $logger->pushHandler(new StreamHandler(DIR.'/test_app.log', Logger::DEBUG)); $logger->pushHandler(new FirePHPHandler()); $logger->error('Logger is now Ready');
In the sample, it has two handlers in the stack to provide records in two different ways. FirePHPHandlers are described as it is joined on top of the stack. This permits you to tentatively append a logger with bubbling disabled, if you want to revoke other configured loggers.
When you add a log entry handler to the logger instance, it matters which order you push the handler. When a log entry is added, it will directly go to the handler stack. When a handler’s constructor is set to false, it stops traversing through the handler stack.
require_once(DIR.'/vendor/autoload.php'); use MonologLogger; use MonologHandlerStreamHandler; use MonologHandlerSwiftMailerHandler; // Create the Transport $transporter = new Swift_SmtpTransport('smtp.example.com', 465, 'ssl'); $transporter->setUsername('[email protected]'); $transporter->setPassword('123456'); // Create the Mailer using your created Transport $mailer = new Swift_Mailer($transporter); // Create a message $message = (new Swift_Message('A CRITICAL log was added')); $message->setFrom(['[email protected]' => 'Someone FROM']); $message->setTo(['[email protected]' => 'SomeoneTO']); $logger = new Logger('default'); $logger->pushHandler(new StreamHandler(DIR.'/test.log', Logger::INFO)); $logger->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::CRITICAL, false)); $logger->addCritical('Hey, a critical log entry!');
First, you need to install SwiftMailer (composer require swiftmailer/swiftmailer) to be able to use the SwiftMailerHandler. For you to be able to send emails, you need to use Swift_SmtpTransport, Swift_Message, and Swift_Mailer to join $mailer and $message to the SwiftMailerHandler instance.
When you examine the last parameter of SwiftMailer’s constructor, it returns false for the last parameter, called $bubble parameter. Technically, by default, it returns true, but when set to false it means that it stops traversing the handler stack. This is a technique in which making the StreamHandler that is located at the bottom of the stack; it never store logs in the file system if SwiftMailerHandler manages the entry.
Array passing is as simple as the following code:
$username = 'jmendez'; $logger->addInfo('User registered', ['username' => $username]);
This is a way to pass an array to the add method such as addDebug, addInfo, etc.
In Monolog, you can create your own processor even though it provides a lot of processors by default. Processors are used to include information in log entries such as client’s IP and browser, and others. Here’s a sample code snippet:
$logger = new Logger('default'); $logger->pushHandler(new StreamHandler(DIR.'/test.log', Logger::INFO)); $logger->pushProcessor(function ($entry) { $entry['extra']['data'] = 'Hello world!'; return $entry; }); $logger->addInfo('User registered', ['username'=>'jmendez']);
The sample above would simply store the log entry in this format
[2018-07-05 11:18:29] default.INFO: User registered {'username':'jmendez'} {"data":"Hello world!"}
You can also use the built-in processor that Monolog provides. You directly use the class and push an instance using the pushProcessor method. Here is sample code using pushProcessor.
use MonologLogger; use MonologHandlerStreamHandler; use MonologProcessorWebProcessor; $logger = new Logger('default'); $logger->pushHandler(new StreamHandler(DIR.'/test.log', Logger::INFO)); $logger->pushProcessor(new WebProcessor()); $logger->addInfo('User registered', ['username'=>'jmendez']);
In Monolog, there are a lot of formatters that can be used. You can find some built-in formatters here. You can also customize the format of the logs that are written in files, emails, databases, and other handlers.
$record['formatted']
is the most common way to use a handler with a value to be directly put into the log device. Formatters can be customized, so you can write your own. Here is a simple configuration for a built-in formatter class that formats the log sent by email, which can be understandable by everyone who will be using your system.
use MonologLogger; use MonologHandlerSwiftMailerHandler; use MonologFormatterHtmlFormatter; // Create the Transport $transporter = new Swift_SmtpTransport('smtp.gmail.com', 465, 'ssl'); $transporter->setUsername('[email protected]'); $transporter->setPassword(123456); // Create the Mailer using your created Transport $mailer = new Swift_Mailer($transporter); // Create a message $message = (new Swift_Message('A CRITICAL log was added')); $message->setFrom(['[email protected]' => 'From User']); $message->setTo(['[email protected]' => 'To User']); $message->setContentType("text/html"); $logger = new Logger('default'); $mailerHandler = new SwiftMailerHandler($mailer, $message, Logger::CRITICAL); $mailerHandler->setFormatter(new HtmlFormatter()); $logger->pushHandler($mailerHandler); $logger->addCritical('This is a critical message!');
In the example, I am using an HtmlFormatter object to the SwiftMailer and then define the $message and set content type to text/html. In the email, this would look like this:
Most of the third-party handlers, formatters and processors can be found in the wiki. You can always write your own if you want.! 😉
Logs are crucial part in every application, it gives you information about your entire application and check whatever blindspot in your application. One of the advantage in using Retrace in your application is that it can support centralized logging. Centralized logging gives you the complete view of your environment. In manual PHP application, you go through the process of finding and guessing some of the errors and logs in your system.
Below is an example of log monitoring work in Retrace.
See the following articles about PHP logging:
Monolog is integrated into most of the popular frameworks like Laravel, Symfony, Slim, and many others, as of today, Monolog is one of the excellent tools available for logging libraries. I hope this article guides you, and helps you in logging your PHP applications.
Retrace gives convenience in monitoring logs and troubleshoot problems quickly.
Sign up for a free trial of Retrace and try it today!
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]