Skill v1.0.2
Automated scan100/100+3 new
version: "1.0.2" name: analyze-logs description: Analyze application logs from the .evlog/logs/ directory. Use when debugging errors, investigating slow requests, understanding request patterns, or answering questions about application behavior. Reads structured NDJSON wide events written by evlog's file system drain. license: MIT metadata: author: HugoRCD version: "0.1"
Analyze application logs
Read and analyze structured wide-event logs from the local .evlog/logs/ directory to debug errors, investigate performance issues, and understand application behavior.
When to Use
- User asks to debug an error, investigate a bug, or understand why something failed
- User asks about request patterns, slow endpoints, or error rates
- User asks "what happened" or "what's going on" with their application
- User asks to analyze logs, check recent errors, or review application behavior
- User mentions a specific error message or status code they're seeing
Finding the logs
Logs are written by evlog's file system drain as .jsonl files, organized by date.
Format detection: The drain supports two modes:
- NDJSON (default,
pretty: false): One compact JSON object per line. Parse line-by-line. - Pretty (
pretty: true): Multi-line indented JSON per event. Parse by reading the entire file and splitting on top-level objects (e.g.JSON.parse('[' + content.replace(/\}\n\{/g, '},{') + ']')) or use a streaming JSON parser.
Always check the first few bytes of the file to detect the format: if the second character is a newline or ", it's NDJSON; if it's a space or newline followed by spaces, it's pretty-printed.
Search order — check these locations relative to the project root:
.evlog/logs/(default)- Any
.evlog/logs/inside app directories (monorepos:apps/*/.evlog/logs/)
Use glob to find log files:
.evlog/logs/*.jsonl*/.evlog/logs/*.jsonlapps/*/.evlog/logs/*.jsonl
Files are named by date: 2026-03-14.jsonl. Start with the most recent file.
If no logs are found
Before wiring a new drain, you can try npx @evlog/cli doctor --json — it checks whether evlog is installed and whether a local .evlog/logs sink already exists (read-only). Optional; skip if the CLI is unavailable.
The file system drain may not be enabled. Guide the user to set it up:
import { createFsDrain } from 'evlog/fs'// Nuxt / Nitro: server/plugins/evlog-drain.tsexport default defineNitroPlugin((nitroApp) => {nitroApp.hooks.hook('evlog:drain', createFsDrain())})// Hono / Express / Elysia: pass in middleware optionsapp.use(evlog({ drain: createFsDrain() }))// Fastify: pass in plugin optionsawait app.register(evlog, { drain: createFsDrain() })// NestJS: pass in module optionsEvlogModule.forRoot({ drain: createFsDrain() })// Standalone: pass to initLoggerinitLogger({ drain: createFsDrain() })
After setup, the user needs to trigger some requests to generate logs, then re-analyze.
Log format
Each line is a self-contained JSON object (wide event). Key fields:
| Field | Type | Description | |
|---|---|---|---|
timestamp | string | ISO 8601 timestamp | |
level | string | info, warn, error, debug | |
service | string | Service name | |
environment | string | development, production, etc. | |
method | string | HTTP method (GET, POST, etc.) | |
path | string | Request path (/api/checkout) | |
status | number | HTTP response status code | |
duration | string | Request duration ("234ms") | |
requestId | string | Unique request identifier | |
error | object | Error details: name, message, stack, statusCode, data | |
error.data.why | string | Human-readable explanation of what went wrong | |
error.data.fix | string | Suggested fix for the error | |
source | string | client for browser logs, absent for server logs | |
userAgent | object | Parsed browser/OS/device info |
All other fields are application-specific context added via log.set() (e.g. user, cart, payment).
How to analyze
Step 1: Read the most recent log file
Read the latest .jsonl file. Each line is one JSON event. Parse each line independently.
Step 2: Identify the relevant events
Filter based on the user's question:
- Errors: look for
"level":"error"orstatus >= 400 - Specific endpoint: match on
path - Slow requests: parse
duration(e.g."706ms") and filter high values - Specific user/action: match on application-specific fields
- Client-side issues: filter by
"source":"client" - Time range: compare
timestampvalues
Step 3: Analyze and explain
For each relevant event:
- What happened: summarize the
path,method,status,level - Why it failed (errors): read
error.message,error.data.why, and the stack trace - How to fix: check
error.data.fixfor suggested remediation - Context: examine application-specific fields for business context (user info, payment details, etc.)
- Patterns: look for recurring errors, degrading performance, or correlated failures
Analysis patterns
Find all errors
Filter: level === "error"Group by: error.message or pathLook for: recurring patterns, common failure modes
Find slow requests
Filter: parse duration string, compare > threshold (e.g. 1000ms)Sort by: duration descendingLook for: specific endpoints, time-of-day patterns
Trace a specific request
Filter: requestId === "the-request-id"Result: single wide event with all context for that request
Error rate by endpoint
Group events by: pathCount: total events vs error events per pathLook for: endpoints with high error ratios
Client vs server errors
Split by: source === "client" vs no source fieldCompare: error patterns between client and serverLook for: client errors that don't have corresponding server errors (network issues)
Important notes
- Each line is a complete, self-contained event. Unlike traditional logs, you don't need to correlate multiple lines — one line has all the context for one request.
- The
error.data.whyanderror.data.fixfields are evlog-specific structured error fields. When present, they provide the most actionable information. - Duration values are strings with units (e.g.
"706ms"). Parse the numeric part for comparisons. - Events with
"source":"client"originated from browser-side logging and were sent to the server via the transport endpoint. - Log files are
.gitignore'd automatically — they exist only on the local machine or server where the app runs.