PHPackages                             abdian/loglens - 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. [Database &amp; ORM](/categories/database)
4. /
5. abdian/loglens

ActiveLibrary[Database &amp; ORM](/categories/database)

abdian/loglens
==============

Production-scale log viewer for Laravel: persistent on-disk index, instant multi-GB opening, full-text search, browser live tail, error grouping, RTL. Laravel 8–13, PHP 8.0+, zero required extensions.

v1.0.0(today)00MITPHPPHP ^8.0CI passing

Since Jun 20Pushed todayCompare

[ Source](https://github.com/abdian/loglens)[ Packagist](https://packagist.org/packages/abdian/loglens)[ Docs](https://github.com/abdian/loglens)[ RSS](/packages/abdian-loglens/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (3)Versions (2)Used By (0)

🔍 LogLens
=========

[](#-loglens)

**A production-scale log viewer for Laravel.**

Open a 5 GB `laravel.log` in under 100 ms · search it in under 200 ms · never hang the UI on a cache miss again.

[![Packagist Version](https://camo.githubusercontent.com/aab8f252348dc59f6860abf1b8b315fef78384b152acc58ebbb9a3393e446663/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f61626469616e2f6c6f676c656e733f7374796c653d666c61742d73717561726526636f6c6f723d346634366535)](https://packagist.org/packages/abdian/loglens)[![Tests](https://camo.githubusercontent.com/917476076dc3e0a4d482c54ba8f6bb4548fbc916fd76285b31a3ef435cffd9b9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f61626469616e2f6c6f676c656e732f63692e796d6c3f6272616e63683d6d61696e267374796c653d666c61742d737175617265266c6162656c3d7465737473)](https://github.com/abdian/loglens/actions/workflows/ci.yml)[![PHP Version](https://camo.githubusercontent.com/d972d7d5e1ac4736ab991926d87d178668353916083088b4bb19d606632e4b7c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f61626469616e2f6c6f676c656e732f7068703f7374796c653d666c61742d737175617265266c6f676f3d706870266c6f676f436f6c6f723d776869746526636f6c6f723d373737626234)](https://packagist.org/packages/abdian/loglens)[![Laravel](https://camo.githubusercontent.com/b04ca0659ffa10b172996f4936c99473afae121baa6e034300b0130293da2e50/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d3825323025453225383025393325323031332d6666326432303f7374796c653d666c61742d737175617265266c6f676f3d6c61726176656c266c6f676f436f6c6f723d7768697465)](https://laravel.com)[![Downloads](https://camo.githubusercontent.com/c72e2cdece0769b65960e47f38d91d02a52b49683e8dab14a2a52d03189ef59d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f61626469616e2f6c6f676c656e733f7374796c653d666c61742d73717561726526636f6c6f723d323263353565)](https://packagist.org/packages/abdian/loglens)[![License](https://camo.githubusercontent.com/3111d5336f0af5e6acf124695c6b0776452fbac3416a9267b495af710cb9482a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f61626469616e2f6c6f676c656e733f7374796c653d666c61742d737175617265)](LICENSE)

[Installation](docs/installation.md) · [Configuration](docs/configuration.md) · [Query language](docs/query-language.md) · [Security](docs/security.md) · [CLI](docs/cli.md) · [JSON API](docs/api.md)

[![LogLens — three-pane log viewer: facet sidebar, entry list with volume histogram, and live tail](art/screenshot-overview.webp)](art/screenshot-overview.webp)

```
composer require abdian/loglens
```

Then visit `/loglens` — no publish step, no npm, no migrations, no configuration required.

---

The one architectural decision that matters
-------------------------------------------

[](#the-one-architectural-decision-that-matters)

Most Laravel log viewers keep their index in the **Laravel cache**. That choice is the root of two failure modes you eventually hit at scale: the UI hangs while a large file is (re-)scanned, and searches return zero results after the cache is evicted or cleared. Because the cache is load-bearing, neither can be solved without changing the storage model.

LogLens stores a **persistent on-disk SQLite sidecar index** per log file under `storage/loglens/`. That index survives cache clears, deploys, load-balancer key skew, and Octane restarts. The entire bug class is structurally impossible.

---

Headline differentiators
------------------------

[](#headline-differentiators)

The column on the right describes the common design of a cache-backed Laravel log viewer — the pattern LogLens was built to move past. It is a description of an architecture, not a benchmark of any one package.

LogLensTypical cache-backed log viewerIndex storagePer-file SQLite sidecar on local diskLaravel cache (evictable)Multi-GB file openInstant tail-first read (3.6 ms on 500 MB, no index needed)Full-file scan on every open; degrades on large filesSearchFTS5 + trigram — index once, search manyRe-scans the raw file on every queryQuery language`level:error after:-1h "payment failed" -channel:horizon`Raw regex onlyLive tail in the browserSSE + polling fallback, works on Windows and shared hostingManual refreshJSON/NDJSON logsAuto-detected, no class requiredOften needs a custom log class`.gz` readingTransparent, indexedUsually unsupportedError groupingSentry-style Issues view — deterministic fingerprints, sparklinesTypically noneRTL / Farsi localeSupported via CSS logical propertiesRarely supportedPHP/Laravel supportLaravel 8–13, PHP 8.0–8.5, every combination CI-testedVariesRequired extensionsNone (pdo\_sqlite + zlib used opportunistically)VariesInstall`composer require` → visit `/loglens`Often requires a config publish---

Feature overview
----------------

[](#feature-overview)

### Persistent index engine

[](#persistent-index-engine)

One SQLite file per log file lives under `storage/loglens/`. It records byte offsets, timestamps, levels, fingerprint hashes, and pre-aggregated per-hour/level counts. Indexing is incremental — only appended bytes are re-scanned. Rotation and truncation are detected via file size, inode, and a fingerprint of the first 4 KB.

When `pdo_sqlite` is unavailable, LogLens falls back to a packed binary sidecar that supports all browsing operations except full-text search.

### Instant first paint

[](#instant-first-paint)

Opening any file immediately serves the newest page via backward chunked reads — no index is required. The response carries `indexState: none|building|ready` so the UI can progressively enable index-backed features (full-text search, histogram, jump-to-date) as they become available. You see logs in under 100 ms on a 5 GB file.

### Format support

[](#format-support)

Built-in parsers: Laravel/Monolog LineFormatter (multi-line stack traces, dual JSON context tails from Laravel 11+), NDJSON/JsonFormatter (auto-detected), Horizon (both formats), Apache/Nginx access logs, Apache/Nginx error logs, PHP-FPM, PostgreSQL, Redis, and Supervisor.

Custom formats can be declared in `config/loglens.php` with a named-capture PCRE — no PHP class required. A class-based API and an adapter for existing opcodesio `Log` subclasses are also available.

Compressed `.gz` files are read transparently through zlib stream wrappers.

### FTS5 search with a query language

[](#fts5-search-with-a-query-language)

```
level:error after:-1h "payment failed" -channel:horizon
level:>=warning after:2026-06-01 before:2026-06-30
context.user_id:42 OR context.user_id:43
/TimeoutException|ConnectionException/i

```

The query language supports bare terms (implicit AND), quoted phrases, `field:value` filters, level comparisons, absolute and relative time filters, negation, OR with parentheses, prefix wildcards (`error*`), and opt-in regex. The same grammar drives the web UI, the JSON API, `loglens:search`, `loglens:tail`, and live-tail server-side filtering.

Search executes against the SQLite FTS5 index. A capability ladder degrades gracefully: FTS5+trigram → FTS5 unicode61 → SQL LIKE → index-assisted streamed PCRE scan. The active tier is visible in the diagnostics panel.

&lt;img src="art/screenshot-search.webp" alt="Search results for level:error after:-24h "payment" with matched terms highlighted and the active search tier shown" width="900"&gt;

### Live tail in the browser

[](#live-tail-in-the-browser)

Server-Sent Events over a raw `StreamedResponse` (compatible with Laravel 8+). Short 45-second windows with automatic reconnect ensure compatibility with common proxy read timeouts. A client-side watchdog switches transparently to offset-based polling when a buffering middlebox is detected, or when running under Octane/Swoole where streaming responses buffer by design.

Multi-file tailing multiplexes over a single connection using per-file SSE event names.

### Error grouping (Issues view)

[](#error-grouping-issues-view)

Every entry is fingerprinted at index time. Exception entries get two deterministic hashes: one on `exception_class|top_app_frame_file|top_app_frame_line` and one on `exception_class|throw_site`. Non-exception entries are normalized by masking UUIDs, dates, IPs, numbers, and SQL bindings, producing a stable group title.

The Issues view shows each group with its occurrence count, first/last seen, level, and a sparkline — collapsing thousands of identical errors into one actionable row. A "new since" diff reports fingerprints that first appeared after a given timestamp (useful after a deploy).

[![Issues view — log entries grouped by fingerprint with occurrence counts](art/screenshot-issues.webp)](art/screenshot-issues.webp)

### Analytics

[](#analytics)

A level-stacked volume histogram above the entry list is answered entirely from pre-aggregated statistics — no entry scans, under 100 ms on any indexed file. Dragging a range on the histogram zooms the entry list to that window.

### File management done right

[](#file-management-done-right)

- **Clear** uses `ftruncate` with an exclusive lock, preserving the inode so long-running writers (queue workers, Horizon) continue appending to the same file without interruption.
- **Delete** removes the file and writes a tombstone so a same-path replacement starts a fresh index.
- **Delete a single entry** soft-deletes one row in the index (the raw file is never rewritten); it disappears from listings, search, stats, and groups, and the deletion survives a full re-index.
- **Download** uses short-TTL signed URLs bound to the authenticated user. Streaming zip downloads never materialize the full archive on disk. Partial downloads ("last 50 MB") are supported for large files.
- **Prune** (`loglens:prune`) automates retention by age and total size, with optional compression to `.gz` and a `--dry-run` mode.

### Web UI

[](#web-ui)

Vue 3 + Pinia + Tailwind SPA served from the vendor directory — no publish step, no npm for users. Three-pane IDE layout: facet sidebar, virtualized entry list with level/time histogram, and a detail drawer with a Flare-style stack trace pane including editor deep links (PhpStorm, VS Code, Cursor, and more) with configurable remote-to-local path mapping.

Keyboard-first: `j`/`k` row navigation, `/` focuses search, `Cmd+K` opens the command palette, `e`/`E` jumps between errors. Dark mode by default. Full RTL support for Persian (`fa`) locale via CSS logical properties.

[![Detail drawer — Flare-style stack trace with app frames highlighted, vendor frames collapsed, and editor deep links](art/screenshot-stacktrace.webp)](art/screenshot-stacktrace.webp)

### JSON API and `api_only` mode

[](#json-api-and-api_only-mode)

Every viewer operation is available through a versioned JSON API under `{prefix}/api`. Setting `api_only = true` disables UI and asset routes while keeping the full API operational — useful for headless dashboards or custom frontends.

### CLI companion

[](#cli-companion)

```
php artisan loglens:index          # build/update all indexes
php artisan loglens:index --watch  # continuous incremental indexing
php artisan loglens:search "level:error after:-24h" --json
php artisan loglens:tail --query="level:>=warning"
php artisan loglens:stats --json
php artisan loglens:prune --days=30 --max-total-size=10G --dry-run
```

All commands share the same index and query language as the web UI.

---

Security
--------

[](#security)

LogLens is **production default-deny**. In any non-local environment, all routes return 403 until you define the `viewLogLens` gate in your application. The identical authorization middleware stack protects web, API, and SSE routes.

Display-time secret redaction is enabled by default: Authorization headers, APP\_KEY, AWS keys, Stripe keys, JWTs, and password fields are replaced with `[redacted]` before any content reaches the browser.

See [docs/security.md](docs/security.md) for the full hardening guide.

---

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

[](#requirements)

- PHP 8.0–8.5
- Laravel 8–13
- No required PHP extensions (pdo\_sqlite and zlib are used opportunistically with graceful fallbacks)

---

Quick start
-----------

[](#quick-start)

```
composer require abdian/loglens
```

Visit `https://your-app.test/loglens`. In a local environment the UI is immediately accessible. In production, define the `viewLogLens` gate first — see [docs/security.md](docs/security.md).

To publish the config file:

```
php artisan vendor:publish --tag=loglens-config
```

---

Documentation
-------------

[](#documentation)

- [Installation](docs/installation.md)
- [Configuration reference](docs/configuration.md)
- [Query language](docs/query-language.md)
- [Security hardening](docs/security.md)
- [CLI commands](docs/cli.md)
- [JSON API](docs/api.md)
- [Deployment guide](docs/deployment.md)
- [Migrating from opcodesio/log-viewer](docs/migration-from-opcodesio.md)

---

License
-------

[](#license)

MIT

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 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

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25894542?v=4)[Ali Abdian](/maintainers/abdian)[@abdian](https://github.com/abdian)

---

Top Contributors

[![abdian](https://avatars.githubusercontent.com/u/25894542?v=4)](https://github.com/abdian "abdian (1 commits)")

---

Tags

fts5laravellaravel-packagelog-viewerlogsmonologobservabilityphpsqlitetaillaravelsqlitetaillogslog viewermonologobservabilityfts5

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/abdian-loglens/health.svg)

```
[![Health](https://phpackages.com/badges/abdian-loglens/health.svg)](https://phpackages.com/packages/abdian-loglens)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k54.1M11.1k](/packages/illuminate-database)[laravel/pail

Easily delve into your Laravel application's log files directly from the command line.

91555.7M861](/packages/laravel-pail)[watson/validating

Eloquent model validating trait.

9743.4M53](/packages/watson-validating)[spatie/laravel-model-flags

Add flags to Eloquent models

4471.2M4](/packages/spatie-laravel-model-flags)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

436834.4k1](/packages/clickbar-laravel-magellan)[wnx/laravel-backup-restore

A package to restore database backups made with spatie/laravel-backup.

213389.8k2](/packages/wnx-laravel-backup-restore)

PHPackages © 2026

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