teamones/log
teamones/log is a Laravel/PHP logging package from TeamOne. Install it via Composer to add a dedicated logging component to Laravel applications. It is intended for projects that need a package-based approach to handling application logs; check compatibility and docs before use.
teamones/log is a Monolog handler for Laravel that ships log records to a remote HTTP collector using workerman/http-client instead of blocking on curl. The value proposition is narrow but real: log shipping stops adding latency to the request that produced the log. It is a 2021-era, zero-adoption package, so treat it as vendored source you own rather than a dependency you trust.
Before writing a line of app code, read four files inside vendor/teamones/log:
composer.json — the PHP and Laravel constraints, and which workerman/* packages it pulls in.ignore_exceptions, batching).config/logging.php.Install and wire it up:
composer require teamones/log
php artisan vendor:publish --provider="Teamones\Log\LogServiceProvider"
Then add a channel alongside your existing file channel rather than replacing it:
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'remote'],
],
'remote' => [
'driver' => 'teamones', // verify the driver name in the provider
'endpoint' => env('LOG_COLLECTOR_URL'),
'timeout' => 1,
'level' => env('LOG_LEVEL', 'info'),
],
],
Your first use case should be a local sink, not production. Run php -S 127.0.0.1:8080 with a tiny script that dumps php://input, point LOG_COLLECTOR_URL at it, and confirm the JSON envelope (message, level, context, channel, timestamp, host) matches what your collector expects. Doing this first answers the question that otherwise costs you an afternoon of debugging: is anything leaving the process at all?
Always stack with a durable handler. The remote channel is fire-and-forget and lossy by design. Keep daily (or stderr in containers) in the stack so a collector outage or a dropped async connection doesn't erase your audit trail. The remote channel is for aggregation and alerting, not for durability.
Gate it by environment. Point LOG_COLLECTOR_URL to a no-op locally and enable the remote channel in staging/prod only. A cheap pattern is to conditionally build the stack channel list:
'channels' => array_filter([
'daily',
app()->environment(['staging', 'production']) ? 'remote' : null,
]),
Inject correlation context once, not per call. A Monolog processor that pushes request_id, user_id, and route into extra turns the remote channel from noise into something queryable. Register it on the channel's handler rather than threading IDs through every Log::info() call.
Use structured context, keep payloads small. Log::channel('remote')->warning('payment declined', ['order_id' => $id, 'gateway' => 'x']) — the context array is what makes the collector useful. Do not dump request bodies or $exception->getTraceAsString() into the remote handler; traces are large, and async serialization of large payloads is where memory issues start.
Finger-crossed for volume control. If your app logs heavily at info, wrap the remote handler so records only ship when an error-level record occurs in the same request, or raise the channel's level to warning independently of the file channel.
Test with a stub, not the wire. In tests, set the remote channel to monolog/NullHandler, or use Log::spy() and assert on the facade. Never let a test suite attempt real HTTP to a collector; the async client plus a test process teardown is a reliable source of flakes.
Console and queue workers. The same channel works, but each queue job is a separate process — verify the event loop actually runs to completion in that runtime before relying on remote logs to debug a failing job.
The async client needs an event loop, and PHP-FPM doesn't have one. This is the make-or-break issue. workerman/http-client schedules work on an event loop; under FPM/Apache the process can terminate before the connection flushes. Unless you are running under a Workerman-based runtime (webman, or a custom Workerman server), you must either drain the loop explicitly before shutdown or accept dropped logs. Test this deliberately: log, hit the endpoint, and confirm arrival — don't assume it works because the code didn't throw.
It has been unmaintained since February 2021. Zero stars, zero dependents. Fork it into your own namespace, pin it, and audit for PHP 8 compatibility (dynamic properties, strlen(null), removed each(), Monolog 2→3 constructor signatures). If you're on Monolog 3, expect the handler's parent constructor call to need adjusting.
No retries, no backpressure, no delivery guarantee. A collector outage silently drops everything. If losing remote logs matters, put a queue between the app and the collector instead — dispatch a job with the record and let the queue's retry semantics do the work. For most Laravel apps that is a simpler and safer design than this package.
Exceptions are often swallowed by config. The handler typically exposes an ignore_exceptions flag. When it's on, a broken endpoint looks like silence. Flip it off in staging so failures surface, and back on in production so logging never takes down a request.
Config caching bites. If the handler reads env() at boot rather than from the config array, php artisan config:cache will bake in whatever was set at cache time. Keep every tunable inside config/logging.php and read env() only there.
Debugging checklist when nothing arrives: confirm the event loop is running; confirm the endpoint resolves and accepts the auth header; check the connect timeout (1s is common and easy to exceed cross-region); verify the level threshold isn't filtering you out; then add a temporary daily handler at the same level to prove the record exists at all.
Extension points are modest but sufficient: a custom formatter for the JSON envelope your collector wants, Monolog processors for context, and subclassing the handler's send method to swap transport. If you find yourself fighting the async loop, the honest exit is to replace the transport with a queued Http::post job — you keep the same channel abstraction and stop depending on an abandoned package.
How can I help you explore Laravel packages today?