Structured logging is the backbone of production observability. In the PHP ecosystem, three libraries dominate the conversation: Monolog (the de facto standard, 21,000+ stars on GitHub), KLogger (a minimal PSR-3 logger designed for simplicity), and Analog PHP (a micro-logging library with no dependencies). Each takes a fundamentally different approach to solving the same problem: getting log data from your application into a format you can query, alert on, and archive.

Comparison Table

FeatureMonologKLoggerAnalog PHP
GitHub Stars~21,400~550~340
PSR-3 CompliantYesYesYes (via wrapper)
Handlers/Drivers40+ built-inFile only (extensible)8+ (file, syslog, email, MongoDB, etc.)
Log FormatLineFormatter, JsonFormatter, etc.CustomizableLine-by-line (customizable)
PHP Version8.1+7.3+7.0+
Dependenciespsr/log + handler-specificpsr/log onlyNone (zero-dependency)
Async LoggingVia handlers (Redis, AMQP)NoNo
Log LevelsFull RFC 5424 (debug, info, notice, warning, error, critical, alert, emergency)Full PSR-3 (8 levels)Full PSR-3 (8 levels)
Install Size~2MB (with all handlers)~50KB~20KB

Getting Started: Installation and Basic Setup

Monolog

Monolog is the undisputed industry standard for PHP logging. It’s used by Symfony, Laravel, and virtually every major PHP framework:

1
composer require monolog/monolog
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Processor\UidProcessor;
use Monolog\Formatter\JsonFormatter;

$logger = new Logger('app');

// Add a rotating file handler — keeps last 30 days of logs
$handler = new RotatingFileHandler('/var/log/app/app.log', 30, Logger::DEBUG);
$handler->setFormatter(new JsonFormatter());
$logger->pushHandler($handler);

// Add a processor to tag each log entry with a unique request ID
$logger->pushProcessor(new UidProcessor());

// Add a separate handler for errors — writes to stderr
$logger->pushHandler(new StreamHandler('php://stderr', Logger::ERROR));

// Usage
$logger->info('User login successful', ['user_id' => 42, 'ip' => $_SERVER['REMOTE_ADDR']]);
$logger->error('Payment processing failed', [
    'order_id' => 12345,
    'error' => 'Insufficient funds',
    'gateway' => 'stripe'
]);

KLogger

KLogger takes a minimalist approach — one class, one handler (file-based), and a clean PSR-3 interface:

1
composer require katzgrau/klogger
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
require 'vendor/autoload.php';

use Katzgrau\KLogger\Logger;

$logger = new Logger('/var/log/app', Logger::DEBUG, [
    'filename' => 'app.log',
    'extension' => 'log',
    'prefix' => 'app_',
    'dateFormat' => 'Y-m-d G:i:s.u',
    'logFormat' => '[{date}] [{level}] {message} {context}',
]);

$logger->info('Cron job completed', ['job' => 'nightly-export', 'records' => 15420]);
$logger->warning('Rate limit approaching', [
    'endpoint' => '/api/v1/users',
    'current_rate' => 85,
    'limit' => 100
]);

Analog PHP

Analog is a zero-dependency micro-logger that emphasizes simplicity and speed. It ships with built-in handlers for file, syslog, email, MongoDB, and more:

1
composer require jbroadway/analog
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
require 'vendor/autoload.php';

use Analog\Analog;
use Analog\Handler\File;
use Analog\Handler\Multi;

// Initialize with a file handler
Analog::handler(File::init('/var/log/app/app.log'));

// Or use multiple handlers simultaneously
Analog::handler(Multi::init([
    File::init('/var/log/app/app.log'),
    Analog\Handler\Stderr::init(),
]));

// Simple logging with no instantiation required
Analog::log('Deployment version ' . $version, Analog::INFO);
Analog::log('Database connection pool exhausted', Analog::WARNING);
Analog::log('Unhandled exception: ' . $e->getMessage(), Analog::ERROR);

Deep Dive: Handler Architectures

Monolog’s handler system is its greatest strength. With over 40 built-in handlers, you can route logs to virtually any destination without changing your application code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Monolog multi-channel routing
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Handler\RedisHandler;
use Monolog\Handler\SlackWebhookHandler;

$logger = new Logger('app');

// Route ERROR+ to email
$logger->pushHandler(new NativeMailerHandler(
    'admin@example.com', 'Critical Alert', 'logger@example.com',
    Logger::ERROR
));

// Route WARNING+ to Slack
$logger->pushHandler(new SlackWebhookHandler(
    'https://hooks.slack.com/services/xxx',
    '#alerts', 'AppBot', true, null, Logger::WARNING
));

// Route ALL to Redis for real-time log aggregation
$redisClient = new \Predis\Client();
$logger->pushHandler(new RedisHandler($redisClient, 'logs', Logger::DEBUG));

KLogger and Analog take simpler approaches. KLogger writes to structured files (one per log level by default) with minimal configuration. Analog’s handler model is a single-function callback system, making it trivially extensible:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Custom Analog handler — log to a remote API
Analog::handler(function ($message, $level, $context) {
    $client = new GuzzleHttp\Client();
    $client->post('https://log-api.example.com/ingest', [
        'json' => [
            'message' => $message,
            'level' => $level,
            'context' => $context,
            'timestamp' => time(),
        ]
    ]);
});

Performance and Production Considerations

When logging at scale, the overhead of log writing can become a bottleneck. Here are real-world benchmarks for 100,000 log writes on PHP 8.3:

LibraryTime (100K writes)Memory PeakNotes
Monolog (StreamHandler)2.8s4.2MBWith JsonFormatter
Monolog (RotatingFile)3.4s4.8MBWith file rotation overhead
KLogger1.9s1.1MBMinimal formatting
Analog PHP (File)1.7s0.8MBNo dependency overhead

KLogger and Analog win on raw speed and memory, but Monolog’s handler routing lets you offload the I/O cost to an external service (Redis, AMQP, syslog) — a net win for high-traffic applications.

When to Use Each Library

Choose Monolog when:

  • You’re building a framework or large application that needs multiple log destinations
  • You need structured logging (JSON format) for log aggregation tools like Elasticsearch, Grafana Loki, or Datadog
  • You want async logging via queue handlers (Redis, AMQP, Kafka)
  • You’re integrating with cloud services (Slack, Loggly, New Relic, Sentry)

Choose KLogger when:

  • You need a drop-in PSR-3 logger for a small to medium project
  • You want clean, date-stamped file logs with zero configuration
  • Your deployment environment is straightforward (single server, file-based logs)
  • You value code simplicity over feature breadth

Choose Analog when:

  • You’re building a microservice or CLI tool with minimal dependencies
  • You need multiple output handlers with almost no configuration
  • You want the smallest possible footprint (20KB installed)
  • You’re working on a legacy PHP 7.0+ codebase

Log Processing Pipelines at Scale

When your application generates millions of log lines per day, the logging library is just the first link in a processing pipeline. Monolog’s rich handler ecosystem makes it the natural choice for feeding into centralized log aggregators. A typical production pipeline routes structured JSON logs through a Redis queue, where they are consumed by Logstash or Vector and indexed into Elasticsearch or Grafana Loki:

1
2
3
4
5
6
7
8
// Monolog pipeline: app -> Redis -> Logstash -> Elasticsearch
$redisHandler = new RedisHandler(
    new \Predis\Client('tcp://redis:6379'),
    'logstash',  // Redis key
    Logger::DEBUG
);
$redisHandler->setFormatter(new JsonFormatter());
$logger->pushHandler($redisHandler);

KLogger and Analog, being file-first loggers, can feed into the same pipeline using filebeat or fluent-bit sidecars that tail log files and forward them to a central aggregator. The choice between app-level shipping (Monolog handlers) and infrastructure-level shipping (filebeat sidecar) depends on your deployment model: monolithic apps benefit from direct handler shipping, while containerized microservices are better served by sidecar-based file tailing.

For teams running PHP in Docker or Kubernetes, structured logging to stdout/stderr with JSON formatting is the modern best practice — the container runtime captures stdout and the log aggregator picks it up without requiring a Redis intermediary. All three libraries support this with their StreamHandler (Monolog) or file handler pointed at php://stdout.

Why Self-Host Your PHP Logging Infrastructure?

Centralized logging is one of the highest-ROI observability investments you can make. By running open-source log aggregation tools like Grafana Loki or the ELK stack alongside structured logging libraries, you gain full visibility into your application without vendor lock-in. Unlike commercial APM solutions that charge per-gigabyte or per-host, self-hosted logging scales with your hardware budget alone.

For a complete observability pipeline, see our self-hosted log integrity guide with audit logging. If you’re evaluating logging infrastructure at the container level, check our Kubernetes logging operators comparison.

FAQ

What is PSR-3 and why does it matter for PHP logging?

PSR-3 is the PHP-FIG standard recommendation for a common logging interface. It defines the LoggerInterface with eight RFC 5424 log levels (emergency, alert, critical, error, warning, notice, info, debug) and a standardized log($level, $message, $context) method. PSR-3 compliance means you can swap logging libraries without changing your application code — just change the concrete implementation in your service container. All three libraries support PSR-3.

Can I use Monolog without Composer?

Monolog requires Composer for autoloading and dependency management, like virtually all modern PHP libraries. However, Analog PHP is truly zero-dependency and can be used by including a single Analog.php file — making it suitable for legacy applications or environments where Composer is not available.

How do I handle log rotation in production?

Monolog’s RotatingFileHandler handles log rotation natively — it keeps a configurable number of daily log files and deletes the oldest. KLogger writes to date-prefixed files automatically, so you can use Linux logrotate for cleanup. Analog does not include rotation logic; pair it with logrotate or a cron job. For production deployments, structured JSON logs shipped to a centralized aggregator (Grafana Loki, ELK) are generally preferred over file-based rotation.

Which library should I use with Laravel or Symfony?

Both Laravel and Symfony use Monolog as their default logging backend. If you’re building on these frameworks, Monolog is the natural choice — you get deep framework integration, automatic channel configuration, and access to environment-specific handlers through the framework’s logging configuration. KLogger and Analog can still be used as additional loggers for specific subsystems if needed.

Is there a performance difference between JSON and line formats?

Yes. JSON formatting in Monolog adds approximately 15-20% overhead compared to line formatting because of the encoding step, but it provides structured data that log aggregators can index and query efficiently. If you’re shipping logs to Elasticsearch or Grafana Loki, the JSON overhead is worth it. For simple file-based logs where you primarily use grep and tail, line formatting is faster and produces smaller files.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com