Manual:Structured logging

MediaWiki version:
1.25

Structured logging is operational (debug) logging that includes structured data for easier post-processing. It is distinct from Manual:Logging to Special:Log which is logging for the benefit of wiki editors; structured logging is for the benefit of developers.

Since MediaWiki 1.25 the PSR-3 logging standard has been available for use by MediaWiki core and extensions to replace the outdated wfDebug and wfDebugLog debug logging calls. The PSR-3 standard allows attaching an array of context data to each log message to provide structured key-value pairs.

Get a logger from the LoggerFactory

use MediaWiki\Logger\LoggerFactory;

$logger = LoggerFactory::getInstance( 'MyCoolLoggingChannel' );

The parameter passed to LoggerFactory::getInstance (here MyCoolLoggingChannel) is the log channel name. How the channel name is used depends on the logging configuration (see below) but typically separate log channels can be directed to separate files or such. You should use something unique (typically the name of your extension) as the channel name to make it easier to separate unrelated log entries.

Send log messages

Once you have a logger object, you can use its method corresponding to PSR-3 severity levels to log messages:

$logger->debug( 'Entered ' . __METHOD__ )
These are messages that are useful for local development and are generally too "spammy" to output on a production wiki. This would typically include anything currently being logged via wfDebug.
$logger->info( 'Restoring page' )
Valuable state change information. This level is a great place to record information that would be useful in a production environment when tracing the path of a request that eventually had an error. This is currently the level automatically associated with wfDebugLog calls when mapped to PSR-3.
$logger->warning( 'Page is protected' )
A soft error condition such as a recoverable error or another condition that typically should not happen but isn't halting for the operation in process.
$logger->error( 'Page not found' )
A hard error such as a caught exception with no recovery path.

The PSR-3 standard includes other severity levels, however they are not recommended for usage in MediaWiki.

Add structured data to logging context

All the logging methods take an optional context array, for example:

$logger->warning( 'page not found', [ 'user' => $user->getName(), 'title' => $page->getPrefixedText() ] );

You should add useful structured information to your log messages in this context object that others can use to find related messages or relevant database records, and trace the cause of the error. This is especially important and useful for warning and error level messages where the wiki operator may not be familiar with the code path and needs to be able to file a good bug report.

  • If you pass an Exception object in the context parameter, it MUST be in the 'exception' key (e.g. $logger->error( 'got exception', [ 'exception' => $e ] )) [1]
    • You can also generate an exception object on the fly, which is a good way of attaching a stack trace e.g. when logging an error in a utility function that's invoked in many places: $logger->warning( 'called with a user which does not exist', [ 'user' => $user->getName(), 'exception' => new RuntimeException() ] )
  • Attach parameters or other interesting state to messages. It's best to make sure the items in the context array are strings. (Other types might be allowed, depending on the logging service the wiki uses, but the behavior for non-string values is often unintuitive.) Many MediaWiki core classes have a __toString method which generates some debugging-friendly description of the object so you can just use things like (string)$titleValue.
    • If you do pass non-string values, try to use reasonably unique field names. This is mainly needed for Wikimedia production which sends the context data to Logstash, which requires all context data using same key to be of the same type (globally, across all log channels).
  • Standard parameters (wiki name, server name etc) will be added automatically. Details depend on what logging service you use, but you'll probably end up using MediaWiki\Logger\Monolog\WikiProcessor.
  • Replace faux structure such as tab-separated items, label=value/label:value pairs, or json serialization.

Many log aggregators try to deduplicate logs by message, so try to keep mutable details out of the message and move them into the context. The logger will replace any tokens inside curly braces with the corresponding value from the context. For example, the code

$logger->error( 'Caught exception while trying to create user {user}: {exception}', [
    'user' => $user->getName(),
    'exception' => $e,
] );

will result in something like

Caught exception while trying to create user Foo: exception DatabaseException with message 'Unique constraint violation' in /srv/mediawiki/includes/Database.php:123
#0 /srv/mediawiki/includes/Database.php(456): Database::query()
...

For maximum compatibility with various logging backends, do not use these keys in your context data:

  • message
  • channel
  • host
  • level
  • type
  • @timestamp
  • @version

Use PSR-3 compatible objects

Throw exceptions which implement INormalizedException (or, if you don't need a custom exception class, use NormalizedException) so that related exception messages can be grouped together in the logs.

You can convert Status and StatusValue objects into PSR-3 logging with:

$statusFormatter = MediaWikiServices::getInstance()->getFormatterFactory()->getStatusFormatter( RequestContext::getMain() );
[ $message, $context ] = $statusFormatter->getPsr3MessageAndContext( $status );
$logger->error( $message, $context );

This is a best effort - Status has a messy internal structure so sometimes this will just end up with something like [ '{message}', 'message' => '...the error message in the Status object' ]. But for statuses holding a single message, it results in nice message strings that are easy to group.

Configuring your wiki for structured logging

Warning Warning: The default legacy logging implementation in MediaWiki drops most context information!

For backwards compatibility, if you are using the default MediaWiki configuration and have configured basic logging, then whether you supply a context object to these logger methods or to MediaWiki's global functions such as wfDebugLog( 'myChannel', $someMessage, 'private', $someContext ), the information in the context object does not appear in the log files you've configured. You should implement a better logger, such as monolog, as the logger "service provider interface". See $wgMWLoggerDefaultSpi and Manual:MonologSpi .

See also