Looking to hire Laravel developers? Try LaraJobs

laravel-ai-log-search maintained by hassanjrao

Description
AI-assisted, natural-language log file search for Laravel. Streams huge log files without loading them into memory and turns plain-language questions into structured filters via Gemini or any OpenAI-compatible API.
Author
Last update
2026/07/14 13:57 (dev-main)
License
Links
Downloads
0

Comments
comments powered by Disqus

Laravel AI Log Search

AI-assisted, natural-language search over your Laravel log files — built for very large logs (hundreds of MB) that normal log viewers choke on.

Ask in plain language:

"errors that happened today" · "entries mentioning PaymentJob after 2 pm" · "critical entries related to user 4521"

The AI turns your request into a structured filter (levels, keywords, date range) — it never reads your log file. A deterministic streaming scanner then applies that filter to the file:

  • Flat memory — the file is read backwards in 64 KB chunks; a 2 GB log uses the same memory as a 2 KB one.
  • Newest first — results stream from the end of the file.
  • Bounded requests — each request stops at a match limit and a byte-scan budget (default 50 MB), returning a byte offset so "Load older" resumes exactly where it left off.
  • Multi-line aware — stack traces and context lines are grouped with their entry.
  • Redaction — emails, bearer tokens, obvious secrets, and long digit runs are masked before entries reach the browser.
  • Works without AI — with driver=none (the default) the prompt is used as a plain keyword search. No API key needed to try it.

The UI is a single self-contained page (vanilla JS, no build step, no frontend framework required).

Demo

🎥 Watch a quick demo on Loom

Requirements

  • PHP 7.2.5+ or 8.x
  • Laravel 7 – 12

Installation

composer require hassanjrao/laravel-ai-log-search

The service provider is auto-discovered. Visit /ai-log-search (in the local environment it works immediately).

Authorization (required outside local)

Like Telescope, the page is open in the local environment only. Anywhere else you must define a viewAiLogSearch gate (e.g. in AuthServiceProvider::boot()):

use Illuminate\Support\Facades\Gate;

Gate::define('viewAiLogSearch', function ($user) {
    return in_array($user->email, ['admin@example.com']);
    // or: return $user->hasPermissionTo('view-log-search');
});

Enable AI interpretation (optional)

Pick a driver in .env:

# Google Gemini
AI_LOG_SEARCH_DRIVER=gemini
AI_LOG_SEARCH_GEMINI_KEY=your-key
AI_LOG_SEARCH_GEMINI_MODEL=gemini-2.5-flash

# ...or any OpenAI-compatible API (OpenAI, Azure, Groq, Ollama, LM Studio, ...)
AI_LOG_SEARCH_DRIVER=openai
AI_LOG_SEARCH_OPENAI_KEY=your-key
AI_LOG_SEARCH_OPENAI_MODEL=gpt-4o-mini
AI_LOG_SEARCH_OPENAI_BASE_URL=https://api.openai.com/v1

If the AI call fails or returns garbage, the package silently falls back to searching the raw prompt as a keyword — the page keeps working.

Custom AI provider

Bind your own implementation of Hassanjrao\AiLogSearch\Contracts\AiClient in a service provider:

$this->app->bind(\Hassanjrao\AiLogSearch\Contracts\AiClient::class, MyAnthropicClient::class);

completeJson(string $instruction): ?array receives the interpretation prompt and must return the decoded JSON filter (or null).

Configuration

php artisan vendor:publish --tag=ai-log-search-config

Key options in config/ai-log-search.php:

Option Default Description
route_prefix ai-log-search URL prefix for the page and its API.
middleware ['web'] Middleware stack (the authorize middleware is always appended).
log_dir storage/logs Directory scanned for *.log files.
driver none gemini, openai, or none (keyword-only).
limits.entries_per_page 200 Max matching entries per request.
limits.byte_budget 50 MB Max bytes scanned per request.
redact true Mask likely PII/secrets in returned entries.

To customize the page itself:

php artisan vendor:publish --tag=ai-log-search-views

HTTP API

All endpoints live under the configured prefix and honor the same authorization.

Method Endpoint Body Returns
GET /files {files: [{name, size, size_human, modified}]}
POST /query {file, prompt} interpreted filter, explanation, plus the first result page
POST /results {file, filter, offset} {entries, next_offset, eof, file_size, scanned_bytes}

offset is the byte position returned as next_offset by the previous page (0 = start from the end of the file). filter shape:

{
  "levels": ["ERROR", "CRITICAL"],
  "keywords": ["PaymentJob"],
  "date_from": "2026-07-14 00:00:00",
  "date_to": null
}

Security notes

  • File names from the client are reduced to their base name and resolved with realpath() inside the configured log directory — path traversal is not possible, and only *.log files are served.
  • The AI provider receives only the user's prompt and the file's date — never log contents.
  • Client-supplied filters are re-validated server-side (levels are whitelisted).

Testing

composer install
vendor/bin/phpunit

License

MIT — see LICENSE.