name: log-analyzer
description: Expert guide for analyzing application logs including log searching, pattern detection, error tracking, and debugging. Use when investigating issues, tracking errors, or understanding application behavior.
slug: log-analyzer
category: operations
complexity: complex
version: "1.0.0"
author: "id8Labs"
triggers:
- "log-analyzer"
- "log analyzer"
tags:
- development
- tool-factory-retrofitted---
Log Analyzer Skill
Core Workflows
Workflow 1: Primary Action
- Analyze the input and context
- Validate prerequisites are met
- Execute the core operation
- Verify the output meets expectations
- Report results
Overview
This skill helps you effectively analyze application logs to diagnose issues, track errors, and understand system behavior. Covers log searching, pattern detection, structured logging, and integration with monitoring tools.
Log Analysis Philosophy
Principles
- Structure over text: Structured logs are easier to analyze
- Context matters: Include relevant metadata
- Levels have meaning: Use appropriate severity levels
- Correlation is key: Link related events across services
Log Levels
| Level | When to Use | Example |
|---|
| ERROR | Something failed, needs attention | Database connection failed |
| WARN | Unexpected but handled | Retry succeeded after failure |
| INFO | Normal operation milestones | User signed up |
| DEBUG | Detailed diagnostic info | Query executed in 50ms |
| TRACE | Very detailed, usually disabled | Function entered/exited |
Structured Logging
Winston Configuration (Node.js)
import winston from 'winston';
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'my-app',
environment: process.env.NODE_ENV,
},
transports: [
new winston.transports.Console({
format: process.env.NODE_ENV === 'development'
? winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
: winston.format.json(),
}),
],
});
export function createRequestLogger(requestId: string, userId?: string) {
return logger.child({
requestId,
userId,
});
}
export { logger };
Pino Logger (High Performance)
import pino from 'pino';
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV === 'development'
? {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:standard',
},
}
: undefined,
base: {
service: 'my-app',
env: process.env.NODE_ENV,
},
redact: {
paths: ['password', 'token', 'apiKey', '*.password', '*.token'],
censor: '[REDACTED]',
},
});
export function requestLogger(requestId: string, userId?: string) {
return logger.child({ requestId, userId });
}
Logging Best Practices
logger.info('User signed up', {
userId: user.id,
email: user.email,
plan: 'free',
source: 'web',
});
logger.error('Payment failed', {
error: err.message,
stack: err.stack,
userId: user.id,
amount: 99.99,
provider: 'stripe',
});
logger.debug('Database query', {
query: 'SELECT * FROM users WHERE id = ?',
duration: 45,
rows: 1,
});
Log Searching & Filtering
Command Line (grep, jq)
cat logs.json | jq 'select(.level == "error")'
cat logs.json | jq 'select(.userId == "usr_123")'
cat logs.json | jq 'select(.timestamp >= "2024-01-15T10:00:00")'
cat logs.json | jq 'select(.level == "error") | .error.code' | sort | uniq -c
cat logs.json | jq '{timestamp, level, message, userId}'
grep -i "error" logs.txt
grep -E "error|warn" logs.txt
grep -A5 "ERROR" logs.txt
grep -B3 "ERROR" logs.txt
Vercel Log Analysis
vercel logs --follow
vercel logs --level error
vercel logs --since 2h
vercel logs --deployment-url https://myapp-abc123.vercel.app
vercel logs --output json | jq 'select(.level == "error")'
Supabase Log Analysis
SELECT *
FROM postgres_logs
WHERE timestamp > now() - interval '1 hour'
AND error_severity = 'ERROR'
ORDER BY timestamp DESC
LIMIT 100;
SELECT *
FROM auth.audit_log_entries
WHERE timestamp > now() - interval '24 hours'
AND payload->>'action' = 'login'
ORDER BY timestamp DESC;
SELECT
query,
calls,
mean_exec_time,
total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
Pattern Detection
Common Error Patterns
interface ErrorPattern {
pattern: RegExp;
category: string;
severity: 'critical' | 'high' | 'medium' | 'low';
suggestion: string;
}
const errorPatterns: ErrorPattern[] = [
{
pattern: /ECONNREFUSED|connection refused/i,
category: 'connectivity',
severity: 'critical',
suggestion: 'Check if the target service is running and accessible',
},
{
pattern: /timeout|ETIMEDOUT/i,
category: 'timeout',
severity: 'high',
suggestion: 'Increase timeout or check for slow operations',
},
{
pattern: /out of memory|heap|OOM/i,
category: 'memory',
severity: 'critical',
suggestion: 'Increase memory allocation or fix memory leak',
},
{
pattern: /rate.?limit|429|too many requests/i,
category: 'rate-limiting',
severity: 'medium',
suggestion: 'Implement backoff strategy or increase rate limits',
},
{
pattern: /unauthorized|401|invalid.?token/i,
category: 'auth',
severity: 'medium',
suggestion: 'Check authentication credentials or token expiry',
},
{
pattern: /not.?found|404/i,
category: 'not-found',
severity: 'low',
suggestion: 'Verify resource exists or check URL',
},
];
function categorizeError(message: string): ErrorPattern | null {
for (const pattern of errorPatterns) {
if (pattern.pattern.test(message)) {
return pattern;
}
}
return null;
}
Log Aggregation Script
import * as readline from 'readline';
import * as fs from 'fs';
interface LogEntry {
timestamp: string;
level: string;
message: string;
error?: {
message: string;
code?: string;
};
[key: string]: any;
}
interface LogSummary {
totalEntries: number;
byLevel: Record<string, number>;
errorMessages: Record<string, number>;
timeRange: {
start: string;
end: string;
};
}
async function analyzeLogs(filePath: string): Promise<LogSummary> {
const summary: LogSummary = {
totalEntries: 0,
byLevel: {},
errorMessages: {},
timeRange: { start: '', end: '' },
};
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
for await (const line of rl) {
try {
const entry: LogEntry = JSON.parse(line);
summary.totalEntries++;
summary.byLevel[entry.level] = (summary.byLevel[entry.level] || 0) + 1;
if (entry.level === 'error' && entry.error?.message) {
const msg = entry.error.message.substring(0, 100);
summary.errorMessages[msg] = (summary.errorMessages[msg] || 0) + 1;
}
if (!summary.timeRange.start || entry.timestamp < summary.timeRange.start) {
summary.timeRange.start = entry.timestamp;
}
if (!summary.timeRange.end || entry.timestamp > summary.timeRange.end) {
summary.timeRange.end = entry.timestamp;
}
} catch {
}
}
return summary;
}
analyzeLogs('logs.json').then(console.log);
Request Tracing
Request ID Middleware
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { v4 as uuidv4 } from 'uuid';
export function middleware(request: NextRequest) {
const requestId = request.headers.get('x-request-id') || uuidv4();
const response = NextResponse.next();
response.headers.set('x-request-id', requestId);
return response;
}
Tracing Context
import { AsyncLocalStorage } from 'async_hooks';
interface TraceContext {
requestId: string;
userId?: string;
startTime: number;
}
const asyncLocalStorage = new AsyncLocalStorage<TraceContext>();
export function withTrace<T>(
context: TraceContext,
fn: () => T | Promise<T>
): T | Promise<T> {
return asyncLocalStorage.run(context, fn);
}
export function getTrace(): TraceContext | undefined {
return asyncLocalStorage.getStore();
}
export function log(level: string, message: string, data?: object) {
const trace = getTrace();
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level,
message,
requestId: trace?.requestId,
userId: trace?.userId,
duration: trace ? Date.now() - trace.startTime : undefined,
...data,
}));
}
Error Tracking Integration
Sentry Integration
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.1,
beforeSend(event, hint) {
const error = hint.originalException;
if (error instanceof Error && error.message.includes('Network')) {
return null;
}
return event;
},
});
export function captureError(error: Error, context?: Record<string, any>) {
Sentry.withScope((scope) => {
if (context) {
scope.setExtras(context);
}
Sentry.captureException(error);
});
}
try {
await riskyOperation();
} catch (error) {
captureError(error, {
userId: user.id,
action: 'payment',
amount: 99.99,
});
throw error;
}
Custom Error Boundary Logging
'use client';
import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
Sentry.captureException(error);
console.error('Client Error:', {
message: error.message,
digest: error.digest,
stack: error.stack,
url: window.location.href,
userAgent: navigator.userAgent,
});
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
Log Analysis Queries
Common Investigation Queries
jq 'select(.userId == "usr_123" and .level == "error")' logs.json
jq --arg time "$(date -d '1 hour ago' -Iseconds)" \
'select(.timestamp > $time and .level == "error")' logs.json
jq -s 'group_by(.error.message) |
map({message: .[0].error.message, count: length}) |
sort_by(-.count)' logs.json
jq 'select(.duration > 1000)' logs.json
jq 'select(.requestId == "req_abc123")' logs.json | sort_by(.timestamp)
jq -r 'select(.level == "error") | .error.message' logs.json | \
sort | uniq -c | sort -rn | head -20
SQL-Style Log Analysis
SELECT
date_trunc('hour', timestamp) as hour,
count(*) FILTER (WHERE level = 'error') as errors,
count(*) as total,
round(100.0 * count(*) FILTER (WHERE level = 'error') / count(*), 2) as error_rate
FROM logs
WHERE timestamp > now() - interval '24 hours'
GROUP BY 1
ORDER BY 1;
SELECT
error_message,
count(*) as occurrences,
min(timestamp) as first_seen,
max(timestamp) as last_seen
FROM logs
WHERE level = 'error'
AND timestamp > now() - interval '24 hours'
GROUP BY error_message
ORDER BY occurrences DESC
LIMIT 10;
SELECT
path,
count(*) as requests,
avg(duration) as avg_duration,
max(duration) as max_duration,
percentile_cont(0.95) WITHIN GROUP (ORDER BY duration) as p95
FROM logs
WHERE timestamp > now() - interval '1 hour'
GROUP BY path
ORDER BY avg_duration DESC
LIMIT 10;
Log Retention & Cleanup
Log Rotation Configuration
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload myapp
endscript
}
Automated Log Cleanup
import * as fs from 'fs';
import * as path from 'path';
const LOG_DIR = '/var/log/myapp';
const RETENTION_DAYS = 30;
function cleanupOldLogs() {
const now = Date.now();
const maxAge = RETENTION_DAYS * 24 * 60 * 60 * 1000;
const files = fs.readdirSync(LOG_DIR);
for (const file of files) {
const filePath = path.join(LOG_DIR, file);
const stats = fs.statSync(filePath);
if (now - stats.mtime.getTime() > maxAge) {
fs.unlinkSync(filePath);
console.log(`Deleted: ${file}`);
}
}
}
cleanupOldLogs();
Debugging Checklist
When Investigating Issues
Log Quality Check
When to Use This Skill
Invoke this skill when:
- Investigating production issues
- Setting up logging infrastructure
- Debugging application errors
- Analyzing performance issues
- Creating log analysis scripts
- Setting up error tracking
- Implementing request tracing
- Cleaning up log data