liberusoftware/observability
Observability tools for Laravel apps, bundling logging, metrics, and tracing helpers to help you monitor performance, catch errors early, and understand how requests flow through your application.
Install and confirm what you actually got. The package is a single-digit-star module with no dependents, so treat the README as a hypothesis rather than documentation:
composer require liberusoftware/observability
php artisan vendor:publish --provider="Liberu\Observability\ObservabilityServiceProvider"
php artisan migrate
Before writing a line of integration code, open the vendor directory and read three things in this order:
config/observability.php (after publishing) — this is the real API surface. Everything you can turn on, redact, sample, or route lives here. Read it end to end; it is usually better documentation than the README.register()/boot() — tells you what is auto-registered (middleware? event listeners? a dashboard route?) and therefore what is already happening to your app the moment you install it. This matters, because a package that auto-hooks ExceptionHandler and queue events changes behaviour without you asking.metrics table with a payload JSON column and no index on created_at is a warning sign for both query speed and disk.First use case: request + exception monitoring on one route group. Don't enable everything at once. Pick a single high-traffic-but-not-critical area (an internal admin panel is ideal), attach the request middleware there, and open the dashboard. Verify rows appear, verify the dashboard renders, and verify you can answer "what did this request do?" before you widen the scope.
Check the dashboard's authorization immediately. The description advertises "authorized dashboards," which almost always means a gate or policy you must define yourself. If the gate is undefined, you either get a 403 everywhere (annoying) or, worse, an unguarded route exposing exception traces and request payloads to anyone who guesses the path. Register the gate in a service provider on day one:
Gate::define('viewObservability', fn ($user) => $user->hasRole('admin'));
Then confirm the package's config points at that ability name rather than a hardcoded one.
Sanity-check the baseline. Because this is a zero-star package, run a quick audit before it touches production: composer audit, check the license file is actually MIT, check composer.json for pinned PHP/Laravel constraints, and check whether last release: 2026-10-26 reflects real ongoing maintenance or a single tag. A release date in the future relative to your current date is itself a data-quality flag on the package metadata — verify the tag exists.
Layer the three capture surfaces separately. The package covers requests, jobs, and exceptions. Enable them in that order of risk:
report() or the exception handler. Confirm it doesn't swallow or re-throw differently, and confirm it still calls your existing handler chain.JobProcessing/JobProcessed/JobFailed. This is where the real day-to-day payoff is: failed-job payloads, retry counts, and duration per job class.Propagate a correlation ID across the async boundary. The pattern that makes this package worth keeping is a single trace ID that survives Request → dispatched Job → nested Job → queued Listener. Generate it in middleware, stash it in a request-scoped container binding or a Context-style store, and have your job middleware re-hydrate it from the job payload. Without this you get three disconnected log streams and the dashboard is decorative. If the package doesn't ship this, write ~20 lines of your own job middleware — it's the highest-leverage extension point available.
// Illustrative — confirm the actual facade/class names in the source
Observability::share(['trace_id' => $traceId]);
Redaction is a config contract, not a feature you enable. The description lists redaction, which means there is a list of keys or paths that get stripped before storage. Treat it as a testable contract:
// tests/Feature/RedactionTest.php
$this->post('/checkout', ['card_number' => '4242...', 'password' => 'secret']);
$this->assertDatabaseMissing('observability_requests', ['card_number' => '4242...']);
Cover password, password_confirmation, token, authorization, cookie, card_number, ssn, and any domain-specific identifier. Do this before enabling request capture in production. Structured payloads stored in a JSON column are exactly the kind of thing that ends up in a support ticket screenshot.
Use metrics as a small, boring API — not a metrics platform. "Metrics storage" in a package like this typically means counters/gauges persisted to a database table. That's fine for product-y questions ("how many invoices were generated today?") and wrong for high-cardinality latency histograms. Keep the call sites few and named:
Observability::increment('invoices.generated');
Observability::timing('reports.build', $ms);
Then resist the urge to instrument every loop. Database-backed metrics do not scale like Prometheus; a counter incremented per-row inside a 10k-row job will produce 10k inserts.
Wire the dashboard into your existing admin shell rather than exposing a new one. If the package publishes blade views, override them in resources/views/vendor/... so the dashboard inherits your layout, nav, and auth middleware. A second, visually distinct admin panel with its own session handling is a support burden.
Schedule the pruning command on day one. Whatever the retention command is called, register it immediately:
$schedule->command('observability:prune')->dailyAt('03:00');
Retention should be per-surface in config: keep exceptions for 30 days, requests for 7, metrics aggregates longer. If the package only offers a single global retention window, add your own command that deletes by table.
Integrate, don't duplicate. If you already run Sentry, Flarum-style log channels, or Laravel Telescope locally, decide explicitly which system owns which question. A common split: Telescope for local debugging, this package for production-lite request/job history, Sentry for alerting. Running all three capturing the same exception is pure cost.
Auto-registration is the main risk with any package this size. Verify whether the service provider is discovered, whether it pushes middleware onto the web group globally, and whether it registers a Route::get() for the dashboard without the auth middleware. Test by hitting a public route with php artisan route:list and inspecting the dashboard route's middleware stack.
The dashboard authorization gate is the single most likely security hole. "Authorized dashboards" describes an intention, not a guarantee. Try the dashboard URL as a logged-out user and as a non-admin user, in a local environment, before you deploy. If it's open, either wrap the route in your own middleware or disable it entirely and read the underlying tables directly.
Sampling and performance. Request middleware that serialises the full request (headers, body, session, user) on every hit will show up in your p95. Enable sampling in config — 10–20% on normal traffic, 100% on errors only. Measure before/after with a simple ab or k6 run against a representative endpoint; you want to know the overhead number, not guess it.
Queue context loss. JobProcessing fires in the worker process, which has no HTTP request. Trace IDs, authenticated user, and tenant context are all gone unless you deliberately re-hydrate them. Expect "unknown user" on every job record until you fix this; it's normal, not a bug, for packages in this category.
Redaction runs at write time, not read time. If you enabled request capture before configuring redaction, the unredacted rows are already in the table. Rotate any credentials that touched the endpoint and truncate the table — do not assume a config change retroactively cleans history.
JSON columns in MySQL mean no index on the keys you care about. Queries like "find all requests where payload->user_id = 5" will table-scan. If you need that, add a generated column with an index, or accept that the dashboard's filters are only fast at small table sizes.
Version pinning. At 0 stars with no dependents, there is no community signal that breaking changes will be announced. Pin an exact version ("liberusoftware/observability": "1.0.3", not ^1.0) and read the diff before every bump. Keep a thin wrapper class around the package's facade so that swapping it out later is one file, not fifty call sites.
Extension points to look for, in order of usefulness: the redactor (add your own key lists), the dashboard gate/ability name, the metrics store driver (swap DB for Redis if write volume bites), and the request context resolver (inject tenant/user). If those four are pluggable, the package is worth keeping; if they're hardcoded, be prepared to fork.
Debugging tip: when records simply don't appear, check in this order — config cached (php artisan config:clear), sampling set to 0, middleware not in the right group, dashboard querying a different connection than the migrations ran on, and queue worker not restarted after a deploy (php artisan queue:restart). Those five account for most "it's installed but silent" reports with packages of this shape.
Have an exit plan. Zero stars, zero dependents, and a young release date mean bus factor of approximately one. Decide up front what you'd do if the package goes unmaintained: the data is in your own database, so the dashboard can be replaced with a Blade page and the capture layer with a Laravel Context + log channel setup. Keeping the schema and your call sites thin is what makes that a day's work instead of a quarter's.
How can I help you explore Laravel packages today?