Skip to content

Enhancements — Log Analyzer

Extend the core analyzer once the basic report works.

Enhancement 1 — Date Range Filtering

Analyze only a specific time window:

# Add options: --from "2024-01-15 08:00" --to "2024-01-15 10:00"
FROM_DATE=""
TO_DATE=""

filter_by_date() {
    local from="$1" to="$2"
    # Apache log date format: 15/Jan/2024:08:30:00
    awk -v from="$from" -v to="$to" '
    function parse_date(d,    months, m, dt) {
        months["Jan"]=1; months["Feb"]=2; months["Mar"]=3; months["Apr"]=4
        months["May"]=5; months["Jun"]=6; months["Jul"]=7; months["Aug"]=8
        months["Sep"]=9; months["Oct"]=10; months["Nov"]=11; months["Dec"]=12
        # Extract day/month/year:HH:MM:SS from [15/Jan/2024:08:30:00
        gsub(/[\[\]]/, "", d)
        split(d, dt, /[\/:]/)
        return sprintf("%04d%02d%02d%02d%02d%02d",
            dt[3], months[dt[2]], dt[1], dt[4], dt[5], dt[6])
    }
    {
        ts = parse_date($4)
        if ((from == "" || ts >= from) && (to == "" || ts <= to))
            print
    }' "$LOGFILE"
}

Usage:

bash log_analyzer.sh --from "20240115080000" --to "20240115100000" access.log

Enhancement 2 — Hourly Traffic Heatmap

Visualize traffic distribution across hours of the day:

print_hourly_heatmap() {
    report "── Hourly Traffic ───────────────────────"

    awk '{
        # Extract hour from [15/Jan/2024:08:30:00
        match($4, /:([0-9]{2}):/, h)
        hours[h[1]]++
    }
    END {
        max = 0
        for (h in hours) if (hours[h] > max) max = hours[h]
        for (h = 0; h < 24; h++) {
            count = hours[sprintf("%02d", h)] + 0
            bar_len = int(count / max * 30)
            bar = ""
            for (i = 0; i < bar_len; i++) bar = bar "#"
            printf "  %02d:00 | %-30s %d\n", h, bar, count
        }
    }' "$LOGFILE" >&3
}

Sample output:

── Hourly Traffic ─────────────────────── 00:00 | ### 142 01:00 | ## 89 09:00 | ############################## 1204 ...

Enhancement 3 — Suspicious IP Detection

Flag IPs with unusually high error rates or request volume:

detect_suspicious_ips() {
    local threshold="${1:-100}"
    report "── Suspicious IPs (>${threshold} 4xx/5xx errors) ───"

    awk -v thresh="$threshold" '
    $9 ~ /^[45]/ { errors[$1]++ }
    END {
        for (ip in errors)
            if (errors[ip] >= thresh)
                printf "  %-20s %d error requests\n", ip, errors[ip]
    }' "$LOGFILE" | sort -rn -k2 >&3
}

Enhancement 4 — Response Time Analysis

If your log format includes response time (common in Nginx with $request_time):

# Nginx log format with response time as field $NF
print_response_times() {
    awk '{
        rt = $NF + 0
        sum += rt; count++
        if (rt > max) { max = rt; slow_line = $0 }
    }
    END {
        printf "  Avg response time: %.3fs\n", sum/count
        printf "  Max response time: %.3fs\n", max
        printf "  Slowest request:   %s %s\n", $7, $9
    }' "$LOGFILE" >&3
}

Enhancement 5 — GeoIP Lookup

Look up the country for each top IP (requires geoiplookup or mmdbinspect):

print_ip_geo() {
    report "── Top IPs with Country ─────────────────"
    awk '{print $1}' "$LOGFILE" | sort | uniq -c | sort -rn | head -10 |
    while read -r count ip; do
        local country
        country=$(geoiplookup "$ip" 2>/dev/null | awk -F': ' '{print $2}' | head -1 || echo "Unknown")
        printf "  %-20s %-25s %7d reqs\n" "$ip" "$country" "$count" >&3
    done
}

Enhancement 6 — HTML Report

Generate a self-contained HTML page:

generate_html_report() {
    local out="${1:-report.html}"
    local total status_data ip_data path_data
    total=$(wc -l < "$LOGFILE")
    status_data=$(awk '{print $9}' "$LOGFILE" | sort | uniq -c | sort -rn)
    ip_data=$(awk '{print $1}' "$LOGFILE" | sort | uniq -c | sort -rn | head -10)
    path_data=$(awk '{print $7}' "$LOGFILE" | sort | uniq -c | sort -rn | head -10)

    cat > "$out" << HTMLEOF
<!DOCTYPE html>
<html>
<head>
<title>Log Report — $(hostname) — $(date '+%Y-%m-%d')</title>
<style>
body { font-family: monospace; background: #0f172a; color: #f1f5f9; padding: 2em; }
h1 { color: #2dd4bf; }
table { border-collapse: collapse; width: 100%; margin: 1em 0; }
th, td { border: 1px solid #334155; padding: 8px 12px; text-align: left; }
th { background: #1e293b; color: #94a3b8; }
tr:nth-child(even) { background: #1e293b; }
.ok { color: #2dd4bf; } .warn { color: #f59e0b; } .err { color: #ef4444; }
</style>
</head>
<body>
<h1>Log Analysis Report</h1>
<p>File: <strong>$LOGFILE</strong> &nbsp; Total lines: <strong>$total</strong></p>
<h2>Status Codes</h2>
<table>
<tr><th>Code</th><th>Count</th></tr>
$(echo "$status_data" | awk '{printf "<tr><td>%s</td><td>%s</td></tr>\n", $2, $1}')
</table>
</body>
</html>
HTMLEOF
    echo "HTML report: $out"
}

testing | solution