# Monolog: PSR-3 logging for PHP, from a single file to a fan-out pipeline

> Monolog is the PHP logging library that most frameworks embed. It implements PSR-3, dispatches records through a handler stack, and can write the same event to a file, a socket, a database or a web service.

**Seldaek/monolog** — Sends your logs to files, sockets, inboxes, databases and various web services

- Repository: https://github.com/Seldaek/monolog
- Website: https://seldaek.github.io/monolog/
- Stars: 21,404 · Forks: 1,911
- Language: PHP
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/seldaek-monolog

## The PHP logging gap Monolog fills

PHP ships error_log() and the error_log ini directive. Both write text to one destination. Neither gives you levels, structured context, or a way to send the same event to two places. Monolog exists to close that gap: it is a logger with a level scheme, a record object, and a list of destinations you assemble yourself.

The intended audience is twofold. Application developers who want warnings in a file and errors in an inbox, without sprinkling conditional code at every call site. And library authors, who the README addresses directly: because Monolog implements PSR-3, you can type-hint against the interface "in your own libraries to keep a maximum of interoperability". That matters more than it sounds. A library that depends on a concrete logger forces every consumer onto that logger. A library that depends on Psr\Log\LoggerInterface does not.

One detail worth knowing before you read the source: Monolog's public APIs accept PSR-3 log levels, but internally it "still uses its own level scheme since it predates PSR-3". The README says this plainly. It is the kind of historical seam that shows up when you compare a level constant in your code against what a handler filters on.

## How a log record travels through handlers and formatters

The mechanism is a stack, not a configuration file. You construct a Logger with a channel name, then push handlers onto it. When you call a log method, the record walks the stack in the order you pushed, and each handler decides independently whether to act on it.

That last point is the design decision that shapes everything else. Handlers are not mutually exclusive. A single error call can hit a StreamHandler writing to disk and a handler that ships the record to a web service, because both were pushed. The README describes this as the reason "special handlers" exist: they let you build advanced strategies, such as filtering by level, buffering, or grouping, without changing the calling code.

Formatters sit between the record and the destination. A handler owns a formatter, and the formatter turns the record into the bytes the destination receives. This separation is why the same log statement can produce a line-oriented text file in development and a structured payload for a remote collector in production. The README's documentation index splits these concerns across doc/02-handlers-formatters-processors.md, with processors as a third stage that mutates the record before it reaches a handler.

The channel name is the other piece of state. It is set at construction, and it travels with every record, which is how a downstream system distinguishes the component that logged from the message itself.

## Installing Monolog and logging your first record

Installation is a single Composer command. Monolog 3 requires PHP 8.1 or above, so check that first; the README's requirements section gives the full matrix.

```bash
composer require monolog/monolog
```

The README's basic usage example is short enough to reproduce in full. It creates a channel, attaches one handler, and writes two records:

```php
<?php

use Monolog\Level;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Level::Warning));

// add records to the log
$log->warning('Foo');
$log->error('Bar');
```

What you should see: the file at the path you passed to StreamHandler receives the warning and the error. The level argument, Level::Warning in this example, is the minimum the handler will accept, so a call to $log->info('...') on the same logger produces nothing on disk. That filter is per handler, not per logger, which is the detail people miss when they add a second handler and wonder why it is quiet.

The README points to doc/01-usage.md for the fuller usage instructions, and doc/02-handlers-formatters-processors.md for the handler catalogue. Neither is reproduced in the README itself, so plan to read the doc/ directory rather than guessing at class names.

## Where Monolog stops and something else has to start

Monolog writes records. It does not store them in a way you can query, and it does not manage retention. If your requirement is "show me every 500 response in the last hour, grouped by route", Monolog is the wrong layer. It is the part that emits; the searching and visualizing happens in whatever receives the output, which is why the README's sponsored section points at a log centralization service rather than at a Monolog feature.

The second limitation is version fragmentation. The README supports three lines at once: Monolog 3 for PHP 8.1 and above, Monolog 2.5 for PHP 7.2 and above, and Monolog 1.25 for PHP 5.3 up to 8.1. Version 1 is explicitly described as "not very maintained anymore" and will not receive PHP support fixes, with 1.x support "somewhat limited at this point" and only important fixes being made. If you are on an old PHP runtime, the library you can install is not the library the 3.x README documents, and the class names and level handling differ between the lines.

The third is the handler stack itself. Every handler you push runs on every record that reaches it. Push a network handler without a level filter or a buffer and you have added a network round trip to your request path. The README does not present a default configuration that avoids this; assembling the stack is your job.

## Monolog against the built-in error_log and framework loggers

The honest alternative for a small script is error_log() plus the error_log ini directive. It has no dependency, no Composer step, and no learning curve. The difference in approach is that error_log() has one destination and no levels: you get a string in a file or a stream, and any routing logic lives in your own code. Monolog replaces that routing logic with a handler list and gives you levels and context as first-class fields on the record. For a CLI tool that runs for two seconds, error_log() is the correct answer. For a long-lived application with several components writing to several destinations, it is not.

The other comparison is against the logger your framework already configures. Symfony, Laravel, Lumen and Magento are listed in the README as shipping with Monolog out of the box, and CakePHP, Nette, Yii 2, Drupal and Aimeos reach it through plugins or extensions. In those stacks you are not choosing Monolog; you are configuring it. The practical difference between "adopt Monolog" and "use the framework's Monolog" is where the handler stack is defined: in a framework config file, or in your own bootstrap code. Getting that wrong means two stacks, and duplicate log lines.

## Licence, upgrade cost and the 1.x to 3.x spread

Monolog is MIT licensed, per the README and the LICENSE file at the repository root. MIT is permissive: it allows commercial and closed-source use, modification and redistribution provided the copyright notice and permission notice are retained. That is a description of the licence text, not legal advice; if your organisation has a policy on third-party licences, the LICENSE file is the document to hand to whoever reviews it.

Upgrade cost is the part to budget for. The repository carries an UPGRADE.md alongside the README, which is where the project documents what changes between major versions. Because the README maintains separate documentation branches for 3.x, 2.x and 1.x, the migration path is not a single document but a set of them. Monolog 1.x is the sharp edge: the README states it is not very maintained and will not receive PHP support fixes, so staying on it means staying on a PHP version the library no longer tracks.

On the maintenance side, the most recent release listed for the repository is 3.12.0, dated 2026-09-09, with 3.11.0 on 2026-09-02 and a 2.11.1 backport the same day. The 2.x line is still receiving releases, which matters if you are pinned there.

## Conclusion

Adopt Monolog if you are writing PHP 8.1 or above and want a PSR-3 logger whose output targets you can change without touching call sites, or if you are building a library and want to type-hint against an interface rather than a concrete logger. Do not adopt it if you need a log viewer, a query UI or retention policies; Monolog writes records, it does not store or index them for you. Before committing, verify three things: that your runtime satisfies the version matrix (Monolog 3 needs PHP 8.1, Monolog 2.5 needs PHP 7.2), that your framework does not already configure a Monolog stack you would duplicate, and which handler you actually need, because the README points to doc/02-handlers-formatters-processors.md for the full list rather than enumerating it.

## FAQ

### What is Monolog and what does it do?

Monolog is a PHP logging library that sends logs to files, sockets, inboxes, databases and various web services. It implements the PSR-3 interface, so libraries can type-hint against the interface rather than a concrete logger.

### Is it monolog or monologue?

They are different things. Monolog is the name of the PHP logging library; a monologue is a long speech by one person. The library's name is spelled with one o at the end.

### What is an example of using Monolog?

The README shows a Logger constructed with a channel name, one StreamHandler pushed onto it with a minimum level of Level::Warning, and then calls to warning() and error() on the logger. Only records at or above the handler's level reach that file.

### What does monolog mean?

In this context Monolog is the name of a PHP logging library that sends logs to files, sockets, inboxes, databases and various web services. The README does not define the word itself beyond using it as the project name.

### What does monologo mean?

The README does not use the word monologo. The project documented here is Monolog, a PSR-3 logging library for PHP installed with composer require monolog/monolog.

### What is the difference between monolog and monologue?

Monolog is the PHP logging library described in this README, which sends logs to files, sockets, inboxes, databases and web services. Monologue is an unrelated English word, and the README does not discuss it.

## Sources

- [License: MIT](https://github.com/Seldaek/monolog/blob/main/LICENSE)
- [Project website](https://seldaek.github.io/monolog/)
- [README](https://github.com/Seldaek/monolog/blob/main/README.md)
- [Releases](https://github.com/Seldaek/monolog/releases)
- [Seldaek/monolog on GitHub](https://github.com/Seldaek/monolog)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/seldaek-monolog
