techforce / openlogs-monolog
Framework-agnostic Monolog handler that ships logs to an OpenLogs server in batches.
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.5
- monolog/monolog: ^3.0
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
- techforce/openlogs-laravel: First-class Laravel log channel with queued delivery on top of this handler.
README
A framework-agnostic Monolog handler that
ships logs to an OpenLogs server. It
buffers records and delivers them in batches to OpenLogs'
POST /api/ingest/batch endpoint, normalizes every record so a single bad entry
can't sink a batch, and never throws — on failure it replays records to a
fallback handler.
Works anywhere Monolog runs. Using Laravel? Prefer the
techforce/openlogs-laravel
bridge, which adds a first-class log channel and queued delivery on top of this
handler.
Install
composer require techforce/openlogs-monolog
Requires PHP 8.1+, monolog/monolog ^3, guzzlehttp/guzzle ^7.
Configuration
The LoggerFactory accepts an array with these keys:
| Key | Required | Default | Description |
|---|---|---|---|
url |
yes¹ | — | OpenLogs base URL (e.g. https://logs.example.com) |
api_key |
yes¹ | — | Project API key (sent as X-API-Key) |
channel |
no | openlogs |
Logger/channel name (becomes the log channel) |
level |
no | Debug |
Minimum level (Monolog Level, name, or int) |
buffer_limit |
no | 500 |
Records buffered before an automatic flush |
timeout |
no | 5.0 |
HTTP timeout in seconds |
fallback |
no | none | A Monolog HandlerInterface used when delivery fails |
client |
no | new Guzzle | A Guzzle ClientInterface (for reuse/testing) |
deliverer |
no | Guzzle sync | A custom BatchDeliverer (overrides the default) |
¹ Required unless you supply your own deliverer.
Records buffer in memory and flush when buffer_limit is reached and on process
shutdown. Keep buffer_limit under the server's 1000-entry cap.
Plain PHP
use Monolog\Level; use Monolog\Handler\StreamHandler; use TechForce\OpenLogs\LoggerFactory; $logger = (new LoggerFactory())([ 'url' => getenv('OPENLOGS_URL'), 'api_key' => getenv('OPENLOGS_API_KEY'), 'channel' => 'my-app', 'fallback' => new StreamHandler('php://stderr', Level::Warning), ]); $logger->info('User signed up', ['user_id' => 42]); // buffered — delivered as a batch on shutdown (or when the buffer fills)
Symfony
Register the logger as a service and reference the handler from your Monolog
config. In config/services.yaml:
services: TechForce\OpenLogs\LoggerFactory: ~ openlogs.logger: class: Monolog\Logger factory: ['@TechForce\OpenLogs\LoggerFactory', '__invoke'] arguments: - url: '%env(OPENLOGS_URL)%' api_key: '%env(OPENLOGS_API_KEY)%' channel: '%kernel.environment%'
Then route a Monolog channel/handler to it, or pull the OpenLogs handler off that
logger and reference it from monolog.yaml. A StreamHandler to var/log makes
a good fallback.
Laravel
Use the techforce/openlogs-laravel
bridge — it registers an openlogs log channel, publishes config, and adds
opt-in queued delivery on a dedicated queue.
How it works
Log call → BufferHandler → OpenLogsHandler::handleBatch()
│ normalize records → wire entries
▼
BatchDeliverer::deliver(entries, records)
│
SyncGuzzleDeliverer → POST /api/ingest/batch
│ on failure (network / non-201):
▼ replay original records → fallback handler
The BatchDeliverer interface is the extension seam: implement it to change how
batches are delivered (for example, the Laravel bridge's queued deliverer).
License
MIT