Monolog

repository·main·Indexed 12 days ago

https://github.com/seldaek/monolog

A comprehensive logging library for PHP that implements the PSR-3 interface. Monolog allows sending logs to various destinations including files, sockets, databases, and web services via a system of loggers, handlers, and formatters. It supports RFC 5424 severity levels and provides extensive integrations with frameworks like Symfony and Laravel. Version ^3.0 requires PHP 8.1+, while ^2.5 requires PHP 7.2+.

Tokens
16.7K
Snippets
49
Records
76
Agent score
97%

What's inside Monolog

  1. Differentiate between context and extra data

    main

    While both context and extra carry arbitrary data related to a log message, they serve different purposes:

    • context: Supplied in "user land". This is the third parameter passed to Psr\Log\LoggerInterface methods. It is intended for data the developer explicitly wants to include with a specific log entry.
    • extra: Internal to Monolog. This array is populated by processors. Processors write to extra instead of context to ensure they do not accidentally overwrite or interfere with the data provided by the user in the context array.
  2. Understand the Monolog LogRecord structure

    main

    In Monolog, log messages are encapsulated in Monolog\LogRecord objects. These objects are passed to processors and handlers and contain the following properties:

    PropertyTypeDescription
    messagestringThe log message. If PsrLogMessageProcessor is used, placeholders like {username} are replaced by values from the context array.
    levelMonolog\LevelThe severity level of the log message.
    contextarrayArbitrary data provided by the user during the log call (e.g., user IDs, IP addresses).
    channelstringThe name of the channel the logger was created with (e.g., new Logger('auth')).
    datetimeMonolog\JsonSerializableDateTimeImmutableThe timestamp of the log event. Extends \DateTimeImmutable.
    extraarrayData added by Monolog processors. This is used to avoid overwriting user-provided context data.

    All properties except extra are read-only.

  3. Activate FingersCrossedHandler using activation strategies

    main

    Monolog provides activation strategies to control when a FingersCrossedHandler should start passing records through to its child handler. This is typically used to buffer logs and only flush them when a specific event (like an error) occurs.

    • ErrorLevelActivationStrategy: Activates the handler when a log record reaches a specific minimum log level.
    • ChannelLevelActivationStrategy: Activates the handler when a log record reaches a specific log level, but scoped to specific channels.
  4. How Loggers, Handlers, and Channels work together

    main

    Monolog is built around three main components:

    1. Logger: An instance representing a specific channel (e.g., db, auth, request). It acts as the entry point for log messages.
    2. Handlers: A stack of handlers attached to a Logger. When a log record is added, it traverses the stack. Each handler decides if it should handle the record.
      • Bubbling: Handlers have a $bubble property. If set to false, the handler stops the record from propagating further down the stack.
    3. Formatters: Attached to Handlers, these normalize and format the log record into a specific output (e.g., a string for a file or a JSON object for an API).

    Multiple Loggers can share the same Handlers, but they will be distinguished by their unique channel names in the logs.

    // A logger with a specific channel and a stack of handlers
    $logger = new Logger('my_channel');
    $logger->pushHandler(new StreamHandler(__DIR__.'/app.log', Level::Debug));
  5. Use Monolog\Registry for global logger access

    main
    The Monolog\Registry class allows you to configure global loggers that can be accessed statically from anywhere in your application. While not considered a best practice in modern dependency injection-based architectures, it is useful for legacy codebases or for quick access to loggers without passing instances through multiple layers.
  6. Understand log records for extension

    main

    When extending Monolog via Handlers, Formatters, or Processors, you must understand the structure of a Monolog\LogRecord. All extension points interact with these records to transform, enrich, or transport log data.

    Key properties of a LogRecord used in handlers include:

    • $record->channel: The name of the channel.
    • $record->level: The log level.
    • $record->message: The raw log message.
    • $record->formatted: The message after it has been processed by a Formatter.
    • $record->datetime: A DateTimeImmutable object representing the log time.
  7. How handler wrappers work

    main

    Monolog provides several special 'wrapper' handlers that modify the behavior of the handler they wrap. These allow for advanced logging strategies:

    Conditional and Buffered Logging

    • FingersCrossedHandler: Accumulates all log records (including low-severity ones) but only delivers them to the wrapped handler if a record reaches a specific severity level. This ensures you have full context (debug/info) only when an actual error occurs.
    • BufferHandler: Buffers all log records and calls handleBatch() on the wrapped handler once when close() is called. Useful for sending a single email containing all logs instead of one email per log entry.
    • DeduplicationHandler: Accumulates records and delivers them to the wrapped handler only if they are unique over a given time period (default 60s). This prevents notification storms during critical failures.
    • OverflowHandler: Buffers messages until a configured threshold of a certain level is reached, then passes all messages to the wrapped handler. Useful for batch processing where you only care about significant failures.

    Error Handling and Grouping

    • GroupHandler: Sends every received record to all configured child handlers.
    • WhatFailureGroupHandler: Extends GroupHandler by ignoring any exceptions raised by child handlers. This prevents a failing remote connection from crashing your application.
    • FallbackGroupHandler: Extends GroupHandler by ignoring exceptions from child handlers until one handler succeeds without throwing. This allows attempting alternative logging destinations if the primary one fails.

    Filtering and Testing

    • FilterHandler: Only allows records of specific levels to pass through to the wrapped handler.
    • SamplingHandler: Lets a sample of records through to the wrapped handler.
    • NoopHandler: Does nothing but does not stop the rest of the handler stack from processing.
    • NullHandler: Throws away any record it handles. Can be used to temporarily disable a handler by placing it on top of the stack.
    • PsrHandler: Forwards log records to an existing PSR-3 logger.
    • TestHandler: Used for unit testing; it records everything sent to it and provides accessors to inspect the logged information.
    • HandlerWrapper: A base class you can inherit from to create your own custom handler wrappers.
  8. Migrate to Monolog 3.x LogRecord objects

    main

    In Monolog 3.0.0, log records were converted from arrays to Monolog\LogRecord objects. This object has public (and mostly readonly) properties.

    Key changes:

    • Accessing data: Instead of using array syntax like $record['context'], use object property syntax: $record->context.
    • Backwards Compatibility: If you are writing a Formatter or Handler that requires an array, you can call $record->toArray() to get a Monolog 1/2 style array. This array will contain enum values instead of enum cases for level and level_name to maintain compatibility.
    • Interface Changes: FormatterInterface, HandlerInterface, and ProcessorInterface now use LogRecord $record as the parameter type instead of array $record.

    Compatibility Tip: To support multiple Monolog versions (e.g., 2 and 3), type-hint the record as array|LogRecord (requires PHP 8.0+). Because LogRecord implements ArrayAccess, you can still use array syntax on the object for backward compatibility.

    // Monolog 3.x style
    $context = $record->context;
    $level = $record->level;
    
    // To get a Monolog 1/2 style array
    $arrayRecord = $record->toArray();
  9. Add extra data using Context or Processors

    main

    There are two ways to include additional metadata in your logs:

    1. Logging Context

    Pass an associative array as the second argument to any logging method. This is best for data specific to a single log event.

    $logger->info('Adding a new user', ['username' => 'Seldaek']);

    2. Processors

    Use a processor to add data to every log record automatically. Processors are callables that receive the $record and must return it after modifying the $record->extra array.

    You can register a processor on the Logger (applies to all handlers) or on a specific Handler (applies only to that handler).

    $logger->pushProcessor(function ($record) {
        $record->extra['dummy'] = 'Hello world!';
        return $record;
    });
  10. Use the SocketHandler to write logs to sockets

    main

    The SocketHandler allows you to send log messages to a socket using fsockopen or pfsockopen.

    By default, it uses TCP. However, you can specify different protocols using prefixes in the connection string:

    • Use unix:// to connect to Unix domain sockets.
    • Use udp:// to open UDP sockets.

    In web environments, it is recommended to enable persistent connections to improve performance by avoiding the overhead of opening and closing connections between every request.

    <?php
    
    use Monolog
    ame
    ame; // Note: The example uses Monolog\Logger and Monolog\Handler\SocketHandler
    use Monolog
    use Monolog
    ame
    ame;
    
    // Create the logger
    $logger = new Logger('my_logger');
    
    // Create the handler
    $handler = new SocketHandler('unix:///var/log/httpd_app_log.socket');
    $handler->setPersistent(true);
    
    // Now add the handler
    $logger->pushHandler($handler, Level::Debug);
    
    // You can now use your logger
    $logger->info('My logger is now ready');
  11. Update custom Handlers and Formatters for Monolog 3.x

    main

    If you have extended Monolog classes or implemented its interfaces, several changes in 3.0.0 may require updates:

    General Changes:

    • Type Hinting: All properties have had types added. If you extended a Monolog class and declared the same property, you must add the corresponding type hint.
    • ResettableInterface: The reset() method now requires a void return type.

    Specific Handler/Formatter Overrides: If you previously customized behavior by redefining $logLevels properties, you must now override specific methods:

    • HtmlFormatter: Override getLevelColor() instead of $logLevels.
    • AbstractSyslogHandler: Override toSyslogPriority() instead of $logLevels.
    • RollbarHandler: Override toRollbarLevel() instead of $logLevels.
    • ZendMonitorHandler: Override toZendMonitorLevel() instead of $levelMap.

    NormalizerFormatter: A new normalizeRecord method is available as an extension point. This is called specifically when converting a LogRecord to an array. Use this if you previously overrode format, as parent::format now requires a LogRecord object.