Skip to main content

Architecture

Monorepo layout

log-lens/
├── packages/
│ ├── core/ cliqthemes/log-lens-core - standalone engine (PHP, PDO SQLite, no framework)
│ │ ├── src/ LogLens\ (Config, Database, Kernel, Http, Services, Repositories, Parsing, Connectors, Domain)
│ │ ├── public/ front controller + built UI (public/ui)
│ │ ├── bin/ import.php, sync.php
│ │ ├── tests/ run.php (assertion suite)
│ │ └── config.php
│ └── laravel/ cliqthemes/log-lens - Laravel adapter (thin)
│ ├── src/ LogLens\Laravel\ (ServiceProvider, LogLens gate, Http, Console)
│ ├── config/ log-lens.php
│ └── resources/dist built UI (base /vendor/log-lens/)
├── frontend/ shared React app to builds to both targets
├── skills/ claude / codex agent skills
├── docs/ this documentation (plain Markdown, incl. reference/)
├── docs-site/ Docusaurus renderer for docs/
└── site/ marketing / landing page (Astro)

Layers (core)

Framework-agnostic, PSR-4 LogLens\:

  • Parsing - streaming parsers (LaravelLogParser, NginxAccessLogParser, GenericConsoleLogParser) selected by ParserRegistry; yield LogEvents without loading whole files.
  • Domain - LogEvent, EventAnalysis, RemoteLogFile.
  • Services - ingestion, connectors/sync, tags, modules, workflow, retention, maintenance, application registry, path repair.
  • Repositories - IssueRepository (persist/fingerprint), IssueQueryRepository (search/detail/summary).
  • Http - the transport-agnostic dispatcher (below).

Request/response kernel (the key to being embeddable)

The HTTP layer never touches PHP superglobals or emits output directly:

  • LogLensRequest - DTO (method, query, body, headers) with a fromGlobals() factory for the standalone transport.
  • LogLensResponse - DTO (data + status).
  • ApiController::handle(LogLensRequest): LogLensResponse and ApplicationApiController - return responses; never exit.
  • RequestGuard::check() - pure; returns a 401/403 response or null.
  • Kernel::handle(LogLensRequest): LogLensResponse - resolves the application, opens its Database, repairs moved paths, and dispatches.

This is why two front-ends can share one engine:

standalone public/index.php to LogLensRequest::fromGlobals() to Kernel to JsonResponse::emit()
laravel LogLensController to Illuminate Request to LogLensRequest to Kernel to response()->json()

Everything below Kernel is untouched by which framework calls it.

Storage

SQLite per application (WAL, busy_timeout, migration gated by PRAGMA user_version). Occurrences store byte ranges into archived files rather than duplicating bodies; an indexed generated column occurred_day powers date filters/trends. Full schema and indexes: reference/database-schema.md.

Frontend

One React/Vite app builds to two bases via env (LOG_LENS_BASE, LOG_LENS_OUTDIR): /ui/ into packages/core/public/ui for standalone, and /vendor/log-lens/ into packages/laravel/resources/dist for the adapter. API calls are prefix-relative, so the SPA works at / or under /log-lens.