minhyung / monolog-openobserve
Monolog handler for OpenObserve
Requires
- php: >=8.3
- ext-curl: *
- monolog/monolog: ^3.0
- psr/log: ^2.0 || ^3.0
Requires (Dev)
- fakerphp/faker: ^1.24
- phpunit/phpunit: ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Monolog handler for OpenObserve
Requirements
- PHP 8.3 or later
- the
curlextension - Monolog 3
Installation
composer require minhyung/monolog-openobserve
Usage
use Minhyung\Monolog\OpenObserveHandler; use Monolog\Logger; $handler = new OpenObserveHandler( host: 'http://localhost:5080', organizationId: 'default', streamName: 'monolog', token: 'your-ingest-token', ); $logger = new Logger('app'); $logger->pushHandler($handler); $logger->info('Hello OpenObserve!', ['foo' => 'bar']);
The token is the value after Basic in OpenObserve's ingestion Authorization
header. Pass it without that prefix. The handler sends it unchanged, without
base64-encoding it again.
Builder and username/password authentication
Use the builder to authenticate with a username and password:
$handler = OpenObserveHandler::builder('http://localhost:5080', 'default', 'monolog') ->withCredentials('admin@yourdomain.com', 'yourpassword') ->withLevel(\Monolog\Level::Info) ->withTimeout(5.0) ->build();
Tokens are also supported by the builder:
$handler = OpenObserveHandler::builder('http://localhost:5080', 'default', 'monolog') ->withToken('your-ingest-token') ->withIgnoreFailure(true) ->build();
The builder is mutable: each with...() call returns the same builder, and the
last withToken() or withCredentials() call determines authentication.
Each build() creates an independent handler. Authentication must be configured
before building, otherwise build() throws LogicException. Empty tokens and
tokens containing CR or LF cause InvalidArgumentException at construction or
build time.
All constructor options have matching builder methods: withIgnoreFailure(),
withLevel(), withBubble(), withFallbackLogger(), withConnectTimeout(), and
withTimeout(). Defaults are identical to the constructor: failures throw,
level is Debug, bubbling is enabled, there is no fallback logger, and connection
and request timeouts are 2 and 5 seconds.
Migration: The constructor's former username and password arguments have
been replaced by a single token argument. Replace existing credentials-based
constructor calls with OpenObserveHandler::builder($host, $organizationId, $streamName)->withCredentials($username, $password)->build(), transferring
optional arguments to their matching builder methods.
Handling failures
By default the handler throws when OpenObserve cannot be reached or rejects the
request, including on an authentication failure such as 401 or when an HTTP
success response reports rejected records in status[].failed. Set
ignoreFailure: true to swallow those errors so that a logging problem never
breaks the application:
$handler = new OpenObserveHandler( host: 'http://localhost:5080', organizationId: 'default', streamName: 'monolog', token: 'your-ingest-token', ignoreFailure: true, );
Swallowed failures disappear silently. To keep a record of them, pass a
fallbackLogger — any PSR-3 logger — and every ignored failure is reported to it
at error level, with the original exception under the exception context key:
use Monolog\Handler\ErrorLogHandler; $handler = new OpenObserveHandler( host: 'http://localhost:5080', organizationId: 'default', streamName: 'monolog', token: 'your-ingest-token', ignoreFailure: true, fallbackLogger: new Logger('openobserve', [new ErrorLogHandler()]), );
The fallback logger must not write back to this handler, as that would recurse.
It is only consulted while ignoreFailure is on; when the handler rethrows, the
caller already sees the exception.
Timeouts
Because a stalled or unreachable OpenObserve would otherwise block the
application on every log call, the request is bounded by two timeouts: 2 seconds
to connect and 5 seconds for the whole request. Adjust them — or set the request
timeout to 0 to disable it — via the connectTimeout and timeout arguments:
$handler = new OpenObserveHandler( host: 'http://localhost:5080', organizationId: 'default', streamName: 'monolog', token: 'your-ingest-token', connectTimeout: 2.0, timeout: 5.0, );
Each log or batch is sent once, without automatic retries, so retries do not multiply the configured timeout. A timeout does not guarantee that the server has not already ingested the records; retrying may create duplicates.