PHPackages                             skeylup/owlogs-agent - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. skeylup/owlogs-agent

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

skeylup/owlogs-agent
====================

Owlogs agent — ship Laravel logs to an Owlogs server for aggregation and viewing.

v1.0.18(2w ago)0254↓16.7%MITPHPPHP ^8.1CI failing

Since Apr 19Pushed 1w agoCompare

[ Source](https://github.com/skeylup/owlogs-agent)[ Packagist](https://packagist.org/packages/skeylup/owlogs-agent)[ Docs](https://www.owlogs.com)[ RSS](/packages/skeylup-owlogs-agent/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (11)Versions (20)Used By (0)

Owlogs Agent
============

[](#owlogs-agent)

Ship Laravel logs — with rich context, tracing IDs, caller location, queue job metadata, and sanitized request body — asynchronously to [**Owlogs**](https://www.owlogs.com).

Drop-in, zero-config, Octane-safe. Works with Laravel 8.65 through 13.

---

Requirements
------------

[](#requirements)

- **PHP 8.1+** and **Laravel 8.65+**.
- Full feature parity from Laravel 11. On Laravel 8/9/10, cross-job context propagation (trace\_id / breadcrumbs inheriting from the dispatching request) falls back to a per-process polyfill — same-request and single-CLI correlation still work, only the HTTP-request → queued-job hand-off is best-effort.
- A queue driver (redis, database, sqs, etc.) — **required**: the ship job runs on the queue, no HTTP call blocks a request.
- A persistent cache store (redis, file, database, memcached) — the debounce marker uses `Cache::add`; the `array` driver prevents de-duplication across requests.
- Redis **or** a writable filesystem — for the cross-process log buffer store (see *How it works*).
- An **Owlogs account** (free sign-up at [owlogs.com](https://www.owlogs.com)) to get a workspace API key.

### Tested Laravel × PHP matrix

[](#tested-laravel--php-matrix)

LaravelPHPMonolog8.65+8.12.x9.x8.12.x10.x8.1+3.x11.x8.2+3.x12.x8.2+3.x13.x8.3+3.x> **EOL notice** — Laravel 8 and 9 are upstream EOL (no more security patches). The agent runs on them today; we cannot promise to chase Laravel regressions there.

---

Features
--------

[](#features)

- **Automatic context enrichment** on every log entry: `trace_id`, `span_id`, `origin`, `user_id`, `app_name`, `app_env`, `app_url`, `git_sha`, `uri`, `route_name`, `route_action`, `ip`, `user_agent`, `duration_ms`, etc.
- **Distributed tracing**: the same `trace_id` flows from an HTTP request into every queue job it dispatches, so you can reconstruct the full lifetime of a request.
- **Caller location**: each log entry carries the `file:line` and `Class@method` where `Log::*()` was actually called — not the framework frame.
- **Queue job metadata**: job class, attempt, queue, connection, plus the public (scalar) properties of the job payload.
- **Artisan command metadata**: command name and arguments for CLI origin logs.
- **Sanitized request input**: POST / PUT / PATCH bodies are captured with `password`, `secret`, `token`, `authorization`, `cookie`, `credit_card` values redacted.
- **Exception stacktraces** including up to 3 levels of chained exceptions.
- **Performance spans** via the `Measure` helper and optional automatic DB query tracking with N+1 detection.
- **Opt-in lifecycle auto-logging** for route matched, auth events, job lifecycle, mail, cache, slow queries, scheduled tasks, model changes, DB transactions, Livewire calls, and more. Replaces the old breadcrumb pattern: each captured event becomes a standalone log line tagged with the shared `trace_id` — the timeline correlates the same way without the per-row payload overhead.
- **Async delivery** via a debounced queue job: N flushes (HTTP request + queue jobs it dispatches) in the same window collapse to a single `ShipBufferedLogsJob` — no cascade, no blocking.
- **Runtime-aware buffering**: non-Octane accumulates in RAM and flushes once per request/job/command boundary; Octane batches across requests with a 2 s / 20-log window so workers ship fewer, bigger payloads.
- **Octane-safe**: no container / request injection into singletons, all state is reset between requests.

---

Quickstart
----------

[](#quickstart)

```
composer require skeylup/owlogs-agent
```

Add your workspace API key to `.env` — grab it from your workspace's **API keys** page on [owlogs.com](https://www.owlogs.com):

```
OWLOGS_API_KEY=owl_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

That's it. With `LOG_CHANNEL=stack` (Laravel's default), any `Log::info(...)`, `Log::error(...)`, etc. is now shipped to Owlogs asynchronously via a queue job — the agent registers the `owlogs` channel on boot and appends it to your `stack` channel for you. No edits to `config/logging.php` or `LOG_STACK` required.

Prefer remote-only shipping? Set `LOG_CHANNEL=owlogs`.

If `OWLOGS_API_KEY` is empty, the queue job is still dispatched but returns silently without hitting the network — handy for local development.

If the project uses [Laravel Boost](https://github.com/laravel/boost), run `php artisan boost:update` to pick up the bundled AI instrumentation skill — your AI assistant will then know how to add workflow-level logging across the app automatically.

### Customising the config (optional)

[](#customising-the-config-optional)

Only publish the config file when you actually want to tweak something beyond env vars:

```
php artisan vendor:publish --tag=owlogs-agent-config
```

### Opt out of auto-registration

[](#opt-out-of-auto-registration)

If you'd rather wire things manually (custom stack logic, explicit `LOG_STACK`, or an alternate channel definition), set:

```
OWLOGS_AUTO_REGISTER_STACK=false
```

Then declare the channel yourself in `config/logging.php`:

```
'owlogs' => [
    'driver' => 'custom',
    'via'    => \Skeylup\OwlogsAgent\Handlers\RemoteLogChannel::class,
    'level'  => env('LOG_LEVEL', 'debug'),
    'tap'    => [\Skeylup\OwlogsAgent\LogContextTap::class],
],
```

…and add `owlogs` to your `LOG_STACK` (e.g. `LOG_STACK=single,owlogs`).

### Upgrading from an earlier version

[](#upgrading-from-an-earlier-version)

Earlier versions required you to declare the `owlogs` channel in `config/logging.php` and list it in `LOG_STACK` by hand. Both are now done at boot. After upgrading you can safely:

- Remove the manual `'owlogs' => [...]` entry from `config/logging.php`.
- Drop `owlogs` from `LOG_STACK` in `.env` (e.g. `LOG_STACK=single`).

Either is also fine to keep — a pre-existing channel definition is never overwritten, and the stack injection skips `owlogs` if it's already present.

---

How it works
------------

[](#how-it-works)

```
┌──────────────┐   ┌───────────────┐   ┌─────────────────┐   ┌─────────────────┐   ┌───────────────────────┐
│  Log::info() │ → │  LogContext   │ → │ RemoteHandler   │ → │ LogBufferStore  │ → │ ShipBufferedLogsJob   │
│              │   │     Tap       │   │ (RAM, policy)   │   │ (Redis / file)  │   │ (queued, debounced)   │
└──────────────┘   └───────────────┘   └─────────────────┘   └─────────────────┘   └──────────┬────────────┘
                                              ▲                      ▲                        │
                                              │                      │                        ▼
                                          flush on                Cache::add          POST /api/owlogs/ingest
                                          boundary                  debounce          X-Api-Key: owl_…
                                       (or Octane                  (once per
                                         window)                 10 s window)

```

The pipeline has two independent stages — a per-process RAM buffer and a cross-process persistent buffer — both with their own bounded-size / bounded-time guarantees. Together they collapse what used to be *one queue job per flush* into *one ship job per debounce window*.

1. **Context enrichment.** Global middleware `AddLogContext` populates Laravel's `Context` facade on every HTTP request with tracing, routing, user and timing fields. The same context flows into queue jobs (`Context::hydrated`) and artisan commands (`CommandStarting`).
2. **Caller trace + formatter.** `LogContextTap` attaches a Monolog processor that resolves the real `file:line` / `Class@method` of the caller (skipping framework frames) and sets a JSON formatter.
3. **RAM buffer, runtime-aware.** `RemoteHandler` accumulates records in memory. When to drain RAM → store is chosen by the active `FlushPolicy`:
    - **`EndOfRequestPolicy`** (non-Octane: PHP-FPM / Herd, artisan one-shot, `queue:work`): no mid-request flush. Drain on `app()->terminating()`, `Queue::after`, `CommandFinished`, plus a `register_shutdown_function` safety net.
    - **`OctaneWindowPolicy`** (Swoole / RoadRunner / FrankenPHP): drain when the RAM buffer reaches `octane.batch_count` (20) **or** `octane.window_ms` (2 000) have elapsed since the first buffered record. Under Swoole, `Octane::tick` enforces the window even during idle periods; RR / FrankenPHP check on each `RequestTerminated` + `WorkerStopping`.
    - Runtime is detected from `$_SERVER['LARAVEL_OCTANE']`. `OWLOGS_FLUSH_STRATEGY=octane|end_of_request` forces one or the other (tests / edge cases).
    - An unconditional hard ceiling at `2 × batch_size` / `2 × max_payload_bytes` forces a flush no matter the policy — protects against a runaway caller looping on `Log::*`.
4. **Cross-process store.** On flush, rows are appended to a `LogBufferStore`:
    - `redis` (default) — single Redis list, atomic Lua-scripted `LRANGE` + `LTRIM` drain. Multiple concurrent processes append safely; a drain always returns distinct rows. Requires `OWLOGS_BUFFER_REDIS_CONNECTION` (default `default`).
    - `file` — JSONL under `OWLOGS_BUFFER_FILE_PATH` (default `storage/app/owlogs/buffer.jsonl`) with advisory `flock(LOCK_EX)`. Zero dependencies beyond a writable filesystem.
    - `memory` — in-process, testing only.
5. **Debounced dispatch.** After appending, `RemoteHandler::flush()` tries to dispatch **one** `ShipBufferedLogsJob` guarded by `Cache::add('owlogs:ship:pending', ttl)`. Any flush fired within the debounce window (`ship.debounce_ms`, default 10 000) sees the marker and appends without dispatching — so an HTTP request that fans out to 15 queue jobs still queues a single ship job.
6. **Ship job.** `ShipBufferedLogsJob::handle()` releases the marker, drains up to `ship.batch_count` (256) rows from the store, splits the payload by `max_payload_bytes` (each split becomes one HTTPS POST, gzipped, to `https://www.owlogs.com/api/owlogs/ingest`), and self-re-dispatches (no delay) while `store.size() > 0`. `RemoteHandler::$suppressed` is held `true` for the duration so nothing the job does can feed back into the buffer.
7. **Retries.** Each POST retries 3 times with backoff `[5, 30, 120]` seconds on 5xx / network errors. 4xx client errors (bad key, invalid payload), `403` (no active subscription) and `429` (quota exhausted) abandon without retry. The rows already drained from the store are serialized into the job payload, so a retry re-ships the same batch (at-least-once, never lost on transient 5xx).

---

Enriching logs from your models
-------------------------------

[](#enriching-logs-from-your-models)

Implement `HasLogContext` on any Eloquent model to expose safe, curated metadata in logs — instead of dumping the whole model.

```
use Skeylup\OwlogsAgent\Contracts\HasLogContext;

class User extends Authenticatable implements HasLogContext
{
    public function toLogContext(): array
    {
        return [
            'email' => $this->email,
            'role'  => $this->role,
            'plan'  => $this->subscription?->plan_name,
        ];
    }

    public function getLogContextLabel(): string
    {
        return trim("{$this->first_name} {$this->last_name}");
    }
}
```

When the authenticated user implements this interface, the data is attached to every log entry under `extra.user` and `extra.user_label`. When passed explicitly as context (`Log::info('...', ['user' => $user])`), the same transformation happens — no more accidental serialization of sensitive fields.

---

Performance measurement
-----------------------

[](#performance-measurement)

```
use Skeylup\OwlogsAgent\Measure;

// Manual span
Measure::start('generate_invoice', ['invoice_id' => 42]);
// ... work ...
Measure::stop('generate_invoice');

// Closure wrapper
$result = Measure::track('stripe.charge', fn () => Stripe::charge($cents));

// Instant checkpoint
Measure::checkpoint('cache.hit', ['key' => 'user:42']);
```

Spans are stored on Laravel's `Context` and shipped with the last log entry of the flushed batch.

Enable automatic DB query tracking with:

```
OWLOGS_MEASURE_DB=true
OWLOGS_N_PLUS_ONE_THRESHOLD=5
```

Every query is recorded as a `db` span, and when the same normalized SQL runs more than N times an `n+1` marker is added to the measures array.

---

Breadcrumbs *(deprecated)*
--------------------------

[](#breadcrumbs-deprecated)

The `Skeylup\OwlogsAgent\Breadcrumb` helper still ships but every method is a **no-op** — existing call sites keep compiling without throwing. The feature is retired in favour of richer **auto-logs** (see next section): each framework event becomes a standalone log line tagged with the shared `trace_id`. Filter by `trace_id` in the Owlogs UI to get the chronological narrative the old breadcrumb chain used to produce, without the per-record payload duplication and the extra quota cost.

For custom business steps that no framework event captures, emit a regular log line:

```
Log::info('[checkout.kyc_step_3_passed]', ['user_id' => $user->id]);
```

Same trace correlation, less ceremony.

---

Auto-logging lifecycle events
-----------------------------

[](#auto-logging-lifecycle-events)

Most lifecycle events are captured automatically out of the box. Flip any switch to `false` in `.env` to disable a category.

Env varDefaultEvent`OWLOGS_AUTO_ROUTE_MATCHED``true`Router resolves the route — first line of every HTTP trace`OWLOGS_AUTO_JOB_DISPATCHED``true`A job is queued`OWLOGS_AUTO_JOB_STARTED``true`A worker picks up a job`OWLOGS_AUTO_JOB_COMPLETED``true`A job completes successfully`OWLOGS_AUTO_JOB_FAILED``true`A job fails (exception + attempt)`OWLOGS_AUTO_JOB_RETRYING``true`A retry is requested`OWLOGS_AUTO_AUTH_LOGIN``true`User logs in (email, IP, UA)`OWLOGS_AUTO_AUTH_FAILED``true`Failed login attempt`OWLOGS_AUTO_AUTH_LOGOUT``true`User logs out`OWLOGS_AUTO_AUTH_PASSWORD_RESET``true`Password reset completed`OWLOGS_AUTO_AUTH_VERIFIED``true`Email verified`OWLOGS_AUTO_MAIL_SENT``true`Mail sending / sent`OWLOGS_AUTO_NOTIFICATION_SENT``true`Notification dispatched`OWLOGS_AUTO_NOTIFICATION_FAILED``true`Notification failed`OWLOGS_AUTO_SLOW_QUERY``true`Queries slower than the threshold below`OWLOGS_AUTO_SLOW_QUERY_MS``500`Slow-query threshold in ms`OWLOGS_AUTO_MIGRATION``false`Migration ran (opt-in — noisy on deploys)`OWLOGS_AUTO_DB_TRANSACTION``false`DB transaction committed / rolled back (opt-in — useful to debug silent rollbacks)`OWLOGS_AUTO_CACHE_HIT` / `OWLOGS_AUTO_CACHE_MISS``false`Cache events (opt-in — very high volume)`OWLOGS_AUTO_HTTP_CLIENT``true`Outgoing HTTP client errors (&gt;= 4xx)`OWLOGS_AUTO_SCHEDULE``false`Scheduled task failed (opt-in)`OWLOGS_AUTO_LIVEWIRE_CALL``true`Livewire `Component::method` calls as standalone timeline rows (no-op when livewire/livewire is not installed; context enrichment also runs regardless)`OWLOGS_AUTO_MODEL_CHANGES``true`Eloquent created / updated / deleted (scoped via `model_changes_models`)`OWLOGS_AUTO_EVENT_DISPATCH``true`App-level events (excluding framework internals)---

Full configuration reference
----------------------------

[](#full-configuration-reference)

All of the following can be overridden in `config/owlogs.php` after publishing.

### Core

[](#core)

Env varDefaultDescription`OWLOGS_ENABLED``true`Master kill-switch`OWLOGS_API_KEY`—Workspace API key (sent as `X-Api-Key`)`OWLOGS_AUTO_REGISTER_STACK``true`Auto-define the `owlogs` channel and append it to `stack` on boot`OWLOGS_JSON``true`Use JsonFormatter (vs. LineFormatter)### Transport (HTTP POST to Owlogs)

[](#transport-http-post-to-owlogs)

Env varDefaultDescription`OWLOGS_INGEST_URL``https://www.owlogs.com/api/owlogs/ingest`Ingest endpoint`OWLOGS_COMPRESSION``true`Gzip the request body before POSTing`OWLOGS_QUEUE``default`Queue name for `ShipBufferedLogsJob``OWLOGS_QUEUE_CONNECTION`—Queue connection (null = app default)`OWLOGS_TIMEOUT``30`HTTP timeout in seconds`OWLOGS_MAX_PAYLOAD_BYTES``524288` (512 KB)Hard cap used by the ship job's chunker: any batch larger than this is split into multiple POSTs### Ship debounce (queue → Owlogs)

[](#ship-debounce-queue--owlogs)

Multiple flushes within the debounce window collapse to a single `ShipBufferedLogsJob`. The job drains up to `batch_count` rows per run, self-re-dispatches while the store still has rows.

Env varDefaultDescription`OWLOGS_SHIP_DEBOUNCE_MS``10000`Delay applied to each ship dispatch; also the effective de-duplication window`OWLOGS_SHIP_BATCH_COUNT``256`Max rows drained (and shipped) per ship-job run### Buffer store (cross-process queue of pending rows)

[](#buffer-store-cross-process-queue-of-pending-rows)

Env varDefaultDescription`OWLOGS_BUFFER_STORE``redis``redis`, `file`, or `memory``OWLOGS_BUFFER_REDIS_CONNECTION``default`Redis connection name (from `config/database.php`)`OWLOGS_BUFFER_REDIS_KEY``owlogs:buffer`List key used in Redis`OWLOGS_BUFFER_FILE_PATH``storage/app/owlogs/buffer.jsonl`JSONL file path when `buffer_store=file`### RAM flush policy (per-process)

[](#ram-flush-policy-per-process)

Env varDefaultDescription`OWLOGS_OCTANE_WINDOW_MS``2000`Octane only: flush RAM when this many ms elapsed since the first buffered record`OWLOGS_OCTANE_BATCH_COUNT``20`Octane only: flush RAM when this many records are buffered`OWLOGS_FLUSH_STRATEGY`—`octane`, `end_of_request`, or unset (auto-detect)`OWLOGS_BATCH_SIZE``50`Hard ceiling: RAM buffer is force-drained when it hits `batch_size * 2` rows (runaway-loop protection)`OWLOGS_MIN_FLUSH_INTERVAL_MS``500`**Deprecated.** No longer read; kept to avoid breaking older `.env` files### Measurement

[](#measurement)

Env varDefaultDescription`OWLOGS_MEASURE_DB``false`Auto-instrument DB queries`OWLOGS_MEASURE_MEMORY``true`Attach peak memory to each batch`OWLOGS_N_PLUS_ONE_THRESHOLD``5`Identical-SQL count to flag as N+1Individual context fields can be toggled under `config('owlogs.fields')` if you want to opt out of e.g. `ip` or `user_agent`.

### Ignoring noisy URIs

[](#ignoring-noisy-uris)

`config('owlogs.ignored_uris')` is a list of path patterns (matched with `Str::is`, so `*` is supported) whose logs are dropped before hitting the transport.

Laravel's `broadcasting/auth` endpoint fires on every websocket handshake and usually has no signal — it is ignored by default. Set `OWLOGS_IGNORE_BROADCASTING=false` to forward it again.

Add your own after publishing the config:

```
'ignored_uris' => [
    'broadcasting/auth',
    'horizon/*',
    'health',
],
```

Env varDefaultDescription`OWLOGS_IGNORE_BROADCASTING``true`Append `broadcasting/auth` to `ignored_uris`### Ignoring noisy events

[](#ignoring-noisy-events)

The `event_dispatch` auto-log forwards every app-level event to Owlogs as an `event.dispatched: ` entry. Third-party packages that fire an event on every request (e.g. `Spatie\LaravelSettings\Events\SettingsLoaded`) can drown out the signal.

`config('owlogs.ignored_events')` is a list of event class names whose dispatched log is dropped before hitting the transport. It uses the same `Str::is` matcher as `ignored_uris`, so wildcards (`*`) are supported and a single entry can silence a whole package namespace at once.

```
'ignored_events' => [
    // Exact FQN
    \Spatie\LaravelSettings\Events\SettingsLoaded::class,

    // Whole-namespace wildcard
    'Spatie\\LaravelSettings\\Events\\*',
    'App\\Events\\Internal\\*',
],
```

Notes:

- Only the `auto_log.event_dispatch` listener is filtered. Explicit `Log::*('event.dispatched: ...')` calls go through unchanged.
- Framework / Laravel-package events (`Illuminate\…`, `Laravel\…`, `Livewire\…`, `Filament\…`, `Stancl\…`, etc.) are already filtered internally, no need to list them.
- Per-environment exclusions are just plain PHP — e.g. `'ignored_events' => app()->isProduction() ? [...] : []`.

---

Payload format
--------------

[](#payload-format)

For transparency, here's what each flush POSTs to Owlogs:

```
{
  "logs": [
    {
      "trace_id": "01JKXZ4…",
      "span_id": "01JKXZ4…",
      "origin": "http",
      "level_name": "ERROR",
      "level": 400,
      "channel": "owlogs",
      "message": "Payment declined",
      "stacktrace": "Stripe\\Exception\\CardException: …",
      "caller_file": "app/Http/Controllers/BillingController.php",
      "caller_line": 87,
      "caller_method": "BillingController@charge",
      "uri": "POST https://app.example.com/billing/charge",
      "route_name": "billing.charge",
      "route_action": "App\\Http\\Controllers\\BillingController@charge",
      "ip": "10.0.0.1",
      "user_agent": "Mozilla/5.0 …",
      "request_input": "{\"amount\":1000,\"currency\":\"EUR\"}",
      "user_id": 42,
      "app_name": "Example App",
      "app_env": "production",
      "app_url": "https://app.example.com",
      "git_sha": "a1b2c3d4",
      "job_class": null,
      "job_attempt": null,
      "queue_name": null,
      "connection_name": null,
      "duration_ms": 147,
      "context": null,
      "breadcrumbs": "[\"CreateOrderAction\",\"ChargeCard\"]",
      "job_props": null,
      "measures": "[{\"label\":\"stripe.charge\",\"duration_ms\":132.1}]",
      "memory_peak_mb": 38,
      "extra": "{\"user\":{\"email\":\"…\"}}",
      "logged_at": "2026-04-16 10:11:12.345"
    }
  ]
}
```

Expected responses:

- `2xx` → the agent moves on (server replies `{"accepted": }`)
- `403` (no active subscription) or `429` (quota exhausted) → the job fails immediately, no retry
- Any other `4xx` → not retried (fix the key / payload)
- `5xx` or network error → retried up to 3 times with backoff `[5, 30, 120]` seconds

---

Troubleshooting
---------------

[](#troubleshooting)

**Jobs pile up in the `failed_jobs` table.** Check the exception: if it's `401` / `403`, your `OWLOGS_API_KEY` is wrong or the key was rotated — regenerate it from your workspace and update `.env`.

**Logs never arrive.** Run `php artisan queue:work` — without a worker, the dispatched `ShipBufferedLogsJob` will never execute and rows pile up in the store. Also verify `OWLOGS_API_KEY` is set (empty key = silent no-op), and that `LOG_CHANNEL=stack` (or `LOG_CHANNEL=owlogs`) — if `LOG_CHANNEL` points to a non-stack channel (e.g. `single`), the auto-registered `owlogs` entry in `stack` is bypassed.

**Too many `ShipBufferedLogsJob` in the queue.** The debounce marker (`Cache::add`) needs a shared persistent cache. Check `CACHE_STORE` (Laravel 11+) / `CACHE_DRIVER` — if it's `array`, each PHP request has its own marker and no de-duplication happens. Switch to `redis`, `file`, `database`, or `memcached`.

**Rows stuck in the buffer.** Inspect the store directly:

```
# Redis
redis-cli LLEN owlogs:buffer
redis-cli LRANGE owlogs:buffer 0 0

# File
wc -l storage/app/owlogs/buffer.jsonl
```

A non-zero `LLEN` with no ship job in the queue usually means the cache marker is stale (e.g. the previous ship job crashed before releasing it). Run `php artisan cache:forget owlogs:ship:pending` to clear it — the next flush will re-arm dispatch.

**Octane complains about bindings.** The agent does not use container / request / config injection in singletons. If you see such warnings, they come from elsewhere in your app.

**Caller location is wrong.** If your logs go through a custom wrapper class, add its path to `config('owlogs.caller.ignore_paths')` so the frame-walker skips over it.

---

Security
--------

[](#security)

- **Redaction** is automatic for request body keys matching `password`, `password_confirmation`, `current_password`, `secret`, `token`, `key`, `authorization`, `cookie`, `credit_card`. Extend the list in `Middleware/AddLogContext.php` if you need more.
- **HTTPS**: traffic is sent over TLS to `https://www.owlogs.com` with Laravel's default HTTP client verification.
- **Authentication**: every request carries the `X-Api-Key` header. Rotate the key from your workspace and update `OWLOGS_API_KEY` to invalidate.
- **No global state**: all tracing IDs live in Laravel's `Context` which is reset between requests / jobs.

---

License
-------

[](#license)

[MIT](LICENSE) © [Skeylup](https://www.owlogs.com)

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance97

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

19

Last Release

18d ago

PHP version history (2 changes)v1.0.0PHP ^8.2

v1.0.18PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/11789833?v=4)[Skeylup](/maintainers/skeylup)[@skeylup](https://github.com/skeylup)

---

Top Contributors

[![skeylup](https://avatars.githubusercontent.com/u/11789833?v=4)](https://github.com/skeylup "skeylup (38 commits)")

---

Tags

laravellogginglogsapmtracingoctanemonologobservabilityowlogs

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/skeylup-owlogs-agent/health.svg)

```
[![Health](https://phpackages.com/badges/skeylup-owlogs-agent/health.svg)](https://phpackages.com/packages/skeylup-owlogs-agent)
```

###  Alternatives

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9732.3M121](/packages/roots-acorn)[shaffe/laravel-mail-log-channel

A package to support logging via email in Laravel

1392.4k](/packages/shaffe-laravel-mail-log-channel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
