flynsarmy/slim-monolog
Monolog integration for the Slim PHP framework. Add PSR-3 logging to your Slim app with minimal setup, route messages through Monolog handlers, and get configurable, structured logging for requests, errors, and custom events.
This package is a single adapter class, Flynsarmy\SlimMonolog\LogWriter, that implements Slim 2's \Slim\LogWriter interface and forwards write($message, $level) to a Monolog logger. That's the whole surface area — read src/Flynsarmy/SlimMonolog/LogWriter.php first (it's ~50 lines) and you'll know everything the package does.
Install and check the constraints immediately, because they pin you to the Slim 2 / Monolog 1.x era:
composer require flynsarmy/slim-monolog
composer show monolog/monolog slim/slim # confirm you're on 1.x / 2.x
Minimal wiring — register the writer as Slim's log writer before run():
require 'vendor/autoload.php';
$app = new \Slim\Slim([
'log.enabled' => true,
'log.level' => \Slim\Log::DEBUG,
'log.writer' => new \Flynsarmy\SlimMonolog\LogWriter(
__DIR__ . '/logs/app.log',
\Monolog\Logger::DEBUG
),
]);
$app->get('/orders/{id}', function ($id) use ($app) {
$app->log->info("Fetching order $id"); // -> lands in logs/app.log via Monolog
// ...
});
$app->run();
First real use case: replace Slim's default file writer with Monolog so you get handlers (rotation, stderr, Slack, FirePHP) and so framework-generated messages (errors, 404s) go through the same pipeline as your own.
Bootstrap-time single writer. The log.writer config key is the cleanest hook — one instance, one stream, applied globally. Alternatively, after construction:
$app->log->setWriter(new \Flynsarmy\SlimMonolog\LogWriter(__DIR__.'/logs/app.log'));
Use whichever happens first in your bootstrap; a later setWriter() silently replaces the previous one.
Rich handlers instead of the package's simple constructor. The constructor only gives you a filename and a level threshold. For anything else, build the Monolog instance yourself and hand Slim a writer:
$monolog = new \Monolog\Logger('app');
$monolog->pushHandler(new \Monolog\Handler\RotatingFileHandler(__DIR__.'/logs/app.log', 14));
$monolog->pushHandler(new \Monolog\Handler\StreamHandler('php://stderr', \Monolog\Logger::ERROR));
$app->log->setWriter(new class($monolog) implements \Slim\LogWriter {
public function __construct(private $logger) {}
public function write($message, $level = \Slim\Log::DEBUG) {
$this->logger->log($this->map($level), $message);
}
// map \Slim\Log constants -> \Monolog\Logger constants
});
Writing that anonymous class once is usually less work than fighting the package's limitations.
Request/response logging via middleware. Slim 2 middleware wraps the route, so instrument at the edges:
class RequestLog
{
public function call()
{
$app = \Slim\Slim::getInstance();
$start = microtime(true);
$this->next->call();
$app->log->info(sprintf(
'%s %s -> %d in %.1fms',
$app->request->getMethod(),
$app->request->getResourceUri(),
$app->response->getStatus(),
(microtime(true) - $start) * 1000
));
}
}
$app->add(new RequestLog());
Hooks for lifecycle breadcrumbs. slim.before, slim.before.router, slim.before.dispatch, slim.after.router are cheap places to emit debug lines without touching route code.
Error handling. Wire Slim's error handler so exceptions get logged with context:
$app->error(function (\Exception $e) use ($app) {
$app->log->error(get_class($e).': '.$e->getMessage()."\n".$e->getTraceAsString());
$app->response->setStatus(500);
echo 'Internal error';
});
Testing. Swap in Monolog\Handler\TestHandler and assert on records — this is the best argument for constructing Monolog yourself rather than trusting the constructor's defaults:
$this->assertTrue($handler->hasErrorThatContains('SQLSTATE'));
\Slim\LogWriter no longer exists in Slim 3/4; those use a PSR-3 logger in the container. Do not attempt to install this into a modern Slim app — you'll get a class-not-found at runtime, not at install time.monolog/monolog constraint will block a Monolog 2/3 upgrade. Treat it as a 20-line dependency you may need to inline and own; that's the realistic long-term move.\Slim\Log::DEBUG and \Monolog\Logger::DEBUG are numerically different; the adapter translates. Always use the fluent methods ($app->log->info($msg)) rather than passing raw integers, and use \Monolog\Logger::* constants only when constructing the writer.Log::info($msg, $context), this hurts: write() takes a message string only. Interpolate, or bypass Slim and call the Monolog instance directly when you need structured context.StreamHandler throws UnexpectedValueException if the parent directory is missing or unwritable. Pre-create logs/ and set permissions in your deploy script; a silent-looking logger is often just a discarded write to an unwritable stream.log.enabled defaults off in some setups. If nothing appears, check log.enabled, then log.level (anything above your threshold is dropped), then whether an exception handler is swallowing before the log line.RotatingFileHandler nor a custom channel — long-running or high-traffic apps will produce one unbounded file.new \Slim\Slim() but before $app->run(); writers set inside a route or late hook may miss earlier framework messages.FirePHPHandler / ChromePHPHandler add per-request overhead and leak internal detail; gate them on $app->config('debug').LogWriter is small and unprotected — extending it and overriding write() is a legitimate way to add context, channeling, or redaction without forking the package.How can I help you explore Laravel packages today?