Back to Blog
Core Components

Logging with dot-log in Zend Expressive and Dotkernel

Adding the config provider

  • Open config/config.php.
  • If there's no entry for the dot-log config provider, add \Dot\Log\ConfigProvider::class.
  • Make sure it's added before application-specific components, e.g. Frontend\App\ConfigProvider::class, Admin\App\ConfigProvider::class, MyProject\ConfigProvider::class.
  • Inside Dot\Log\ConfigProvider, the dependencies section registers an abstract factory, LoggerAbstractServiceFactory::class. This class responds to "selectors" instead of class names - instead of requesting Zend\Log\Logger::class from the container, you request dot-log.my_logger (or just my_logger if using zend-log).
  • Create a log.global.php file within /config/autoload, returning an empty array to start.

Configuring the logger

For this tutorial, the created logger is named my_logger (the name is the developer's choice and should reflect its purpose, e.g. db_error_logger).

In log.global.php:

  1. Add a top-level key dot-log (or log if using zend-log) with an array value.
  2. Inside it, add a loggers key.
  3. Inside that, add the logger name key (my_logger) with an empty array.

For the logger to actually log somewhere, a writer is required - without one, log calls are received but there's nowhere to write the message.

Configuring the writer(s)

Loggers must have at least one writer. A writer is an object that inherits from Zend\Log\Writer\AbstractWriter, responsible for recording log data to a storage backend (see the zend-log writer documentation).

It's possible to separate logs into multiple files using writers and filters (e.g. warnings.log, errors.log, all_messages.log). In the simplest example, all log messages are written to one file, e.g. /data/logs/dk.log, under a writers key inside my_logger.

Notes on writer configuration:

  • The writer key name (e.g. FileWriter) is optional - otherwise the writers array would be enumerative instead of associative.
  • The writer's name key is a developer-provided name for that writer and is mandatory.
  • The writer's priority key doesn't affect which errors get written - it's only a way to organize writers (e.g. 1 - FILE, 2 - SQL, 3 - E-mail), reflecting that writing to a file is the most reliable since SQL or e-mail servers can be external and offline. The priority key is optional.
  • To write to a file, the stream key must be present in the writer's options array (required only when writing to streams/files).

More writer examples: Streams, Databases, FirePHP, ChromePHP, Mail, MongoDB, Syslog, Zend Monitor.

(Optional) Configuring the filters

A filter prevents a message from being written to the log (see the zend-log filters documentation).

Per PSR-3, the log levels, in order of priority/importance, are:

Level Priority number
emergency 0
alert 1
critical 2
error 3
warn 4
notice 5
info 6
debug 7

Although the plain Logger in Zend Log is not fully PSR-3 compatible, it provides a way to log all of these message types. The developer can optionally use keys to name filters. Important: the operator for "more important" messages is <=, because a smaller number represents a more important message.

More on filters: zend-log filters documentation.

(Optional) Configuring the formatter

The logged value isn't limited to a string - arrays can be logged too, and for readability they can be serialized. Zend Log provides String, XML, JSON and FirePHP formatting.

The formatter config accepts:

  • name - the formatter class (must implement Zend\Log\Formatter\FormatterInterface)
  • options - options passed to the formatter constructor, if required

More on formatters: Simple, JSON, XML, FirePHP.

Full example (described)

A complete configuration, as described in the article, does the following:

  • Uses the log through dot-log
  • Names the logger my_logger
  • Writes to file: data/logs/dk.log
  • Explicitly allows all messages to be written
  • Formats the message as JSON

Usage

Basic usage of the logger:

use Zend\Log\Logger;
$logger = $container->get('dot-log.my_logger');

/** @var Logger $logger */
$logger->emerg('0 EMERG');
$logger->alert('1 ALERT');
$logger->crit('2 CRITICAL');
$logger->err('3 ERR');
$logger->warn('4 WARN');
$logger->notice('5 NOTICE');
$logger->info('6 INF');
$logger->debug('7 debug');
$logger->log(Logger::NOTICE, 'NOTICE from log()');

Frequently Asked Questions

How do I register dot-log's config provider? +

In config/config.php, add `\Dot\Log\ConfigProvider::class` if it's not already there, making sure it is added before application-specific components such as Frontend\App\ConfigProvider or Admin\App\ConfigProvider.

How is a logger retrieved from the container instead of using the plain class name? +

Dot\Log\ConfigProvider registers an abstract factory, LoggerAbstractServiceFactory, that responds to "selectors" instead of class names. Instead of requesting Zend\Log\Logger::class from the container, you request dot-log.my_logger (or just my_logger if using zend-log).

What is a writer, and how many does a logger need? +

A writer is an object that inherits from Zend\Log\Writer\AbstractWriter and is responsible for recording log data to a storage backend. Loggers must have at least one writer, and the writer's "name" key is mandatory while its "priority" key is optional and only used to organize writers, not to affect which errors get written.

What does a filter do, and how are log levels ordered? +

A filter prevents a message from being written to the log. Per PSR-3, the log levels in order of priority/importance are emergency (0), alert (1), critical (2), error (3), warn (4), notice (5), info (6), and debug (7) - the operator for "more important" messages is `<=` because a smaller number represents a more important message.

What does the formatter configuration control? +

The formatter accepts a "name" (a class implementing Zend\Log\Formatter\FormatterInterface) and "options" to pass to that formatter's constructor. Zend Log provides String, XML, JSON and FirePHP formatting, and arrays can be serialized this way for better readability.

How do you actually write log messages once the logger is configured? +

Fetch the logger from the container, e.g. `$logger = $container->get('dot-log.my_logger');`, then call methods such as emerg(), alert(), crit(), err(), warn(), notice(), info(), debug(), or the generic log(Logger::NOTICE, 'message').