#!/usr/bin/env bash # LogSizer for Linux: measures how much log data this machine produces, for SIEM sizing. # Copyright (c) 2026 Cyber Affinity LLC. Free to run; see https://www.cyber-affinity.com/terms # # What it does: # - For each common log file (auth, syslog, audit, web server ...), counts lines and bytes and # reads the first and last timestamps, giving events per second and bytes per day. # - Measures the systemd journal over the last 24 hours, if there is one. # What it does not do: # - It changes nothing, installs nothing and makes no network connections. # - The summary file holds counts and sizes only: no log messages, user names or IP addresses. # # Usage: sudo bash logsizer.sh [--role linux|web|db|dns] [--name NAME] [--out DIR] # Or paste the command from https://www.cyber-affinity.com/tools/log-estimator # Run with sudo so it can read the auth and audit logs. set -u umask 077 ROLE="linux"; NAME="$(hostname -s 2>/dev/null || hostname)"; OUT="." while [ $# -gt 0 ]; do case "$1" in --role) ROLE="${2:-linux}"; shift 2 ;; --name) NAME="${2:-$NAME}"; shift 2 ;; --out) OUT="${2:-.}"; shift 2 ;; -h|--help) sed -n '2,16p' "$0"; exit 0 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done case "$ROLE" in linux|web|db|dns) ;; *) echo "--role must be linux, web, db or dns" >&2; exit 1 ;; esac SAFE_NAME="$(printf '%s' "$NAME" | tr -c 'A-Za-z0-9._-' '_' | cut -c1-60)" [ -d "$OUT" ] || { echo "Output folder not found: $OUT" >&2; exit 1; } OUTFILE="$OUT/${SAFE_NAME}-logsizer.json" [ "$(id -u)" -eq 0 ] || echo "Not running as root: logs this user can't read are skipped. Run with sudo for a full reading." # Log files a SIEM usually collects. Rotated copies (.1, .2.gz ...) are included to cover more history. FILES="${LOGSIZER_FILES:-/var/log/auth.log /var/log/secure /var/log/syslog /var/log/messages /var/log/kern.log /var/log/audit/audit.log /var/log/nginx/access.log /var/log/nginx/error.log /var/log/apache2/access.log /var/log/apache2/error.log /var/log/httpd/access_log /var/log/httpd/error_log /var/log/mysql/error.log /var/log/postgresql/postgresql.log /var/log/named/query.log /var/log/cron /var/log/maillog /var/log/mail.log /var/log/fail2ban.log /var/log/ufw.log}" # Reads a timestamp from the start of a log line; prints epoch seconds or nothing. ts_of() { local line="$1" t="" case "$line" in *"msg=audit("*) t="$(printf '%s' "$line" | sed -n 's/.*msg=audit(\([0-9]*\)\..*/\1/p')"; [ -n "$t" ] && { echo "$t"; return; } ;; esac # ISO 8601 (2026-10-07T21:34:01 or 2026-10-07 21:34:01) t="$(printf '%s' "$line" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}[T ][0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n1)" # Classic syslog (Oct 7 21:34:01) [ -z "$t" ] && t="$(printf '%s' "$line" | grep -oE '^[A-Z][a-z]{2} +[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n1)" # Web access logs ([07/Oct/2026:21:34:01 -0500]) if [ -z "$t" ]; then t="$(printf '%s' "$line" | grep -oE '\[[0-9]{2}/[A-Z][a-z]{2}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}' | head -n1 | tr -d '[' | sed 's#/# #g; s#:# #')" fi [ -n "$t" ] && date -d "$t" +%s 2>/dev/null } catf() { case "$1" in *.gz) gzip -dc -- "$1" 2>/dev/null ;; *) cat -- "$1" 2>/dev/null ;; esac; } json_str() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; } SOURCES="" TOTAL_BPD=0 TOTAL_EPS=0 add_source() { # name lines bytes span_seconds local name="$1" lines="$2" bytes="$3" span="$4" [ "$lines" -gt 0 ] && [ "$span" -ge 600 ] || return 0 local eps avg bpd eps="$(awk -v l="$lines" -v s="$span" 'BEGIN{printf "%.4f", l/s}')" avg="$(awk -v b="$bytes" -v l="$lines" 'BEGIN{printf "%d", b/l}')" bpd="$(awk -v b="$bytes" -v s="$span" 'BEGIN{printf "%d", b/s*86400}')" TOTAL_BPD="$(awk -v a="$TOTAL_BPD" -v b="$bpd" 'BEGIN{printf "%d", a+b}')" TOTAL_EPS="$(awk -v a="$TOTAL_EPS" -v b="$eps" 'BEGIN{printf "%.4f", a+b}')" local hours; hours="$(awk -v s="$span" 'BEGIN{printf "%.2f", s/3600}')" [ -n "$SOURCES" ] && SOURCES="$SOURCES," SOURCES="$SOURCES{\"name\":\"$(json_str "$name")\",\"records\":$lines,\"spanHours\":$hours,\"eps\":$eps,\"avgEventBytes\":$avg,\"bytesPerDay\":$bpd}" printf '%-40s %10s events over %6.1f days %8.2f EPS %8.1f MB/day\n' "$name" "$lines" "$(awk -v s="$span" 'BEGIN{print s/86400}')" "$eps" "$(awk -v b="$bpd" 'BEGIN{print b/1048576}')" } for base in $FILES; do [ -r "$base" ] || continue # The current file plus rotated copies, oldest first. list="$(ls -1 "$base" "$base".[0-9]* "$base"-[0-9]* 2>/dev/null | sort -V -r | awk -v b="$base" '$0!=b' ; echo "$base")" lines=0; bytes=0; first=""; last="" for f in $list; do [ -r "$f" ] || continue read -r l c < <(catf "$f" | wc -lc) [ "${l:-0}" -gt 0 ] || continue lines=$((lines + l)); bytes=$((bytes + c)) t="$(ts_of "$(catf "$f" | head -n1)")"; [ -n "$t" ] && { [ -z "$first" ] || [ "$t" -lt "$first" ]; } && first="$t" t="$(ts_of "$(catf "$f" | tail -n1)")"; [ -n "$t" ] && { [ -z "$last" ] || [ "$t" -gt "$last" ]; } && last="$t" done [ -n "$first" ] && [ -n "$last" ] && add_source "$base" "$lines" "$bytes" "$((last - first))" done # systemd journal: the last 24 hours, in the classic syslog line format. if command -v journalctl >/dev/null 2>&1; then read -r l c < <(journalctl -q --no-pager -o short-iso --since "-24h" 2>/dev/null | wc -lc) [ "${l:-0}" -gt 0 ] && add_source "journald (last 24 hours)" "$l" "$c" 86400 fi NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)" ADMIN=false; [ "$(id -u)" -eq 0 ] && ADMIN=true printf '{"tool":"cyber-affinity-logsizer","version":1,"kind":"host","name":"%s","os":"linux","role":"%s","collectedAt":"%s","admin":%s,"totals":{"eps":%s,"bytesPerDay":%s},"sources":[%s]}\n' \ "$(json_str "$NAME")" "$ROLE" "$NOW" "$ADMIN" "$TOTAL_EPS" "$TOTAL_BPD" "$SOURCES" > "$OUTFILE" # Run through sudo: hand the file back to the person who ran it. [ -n "${SUDO_UID:-}" ] && chown "$SUDO_UID:${SUDO_GID:-$SUDO_UID}" "$OUTFILE" 2>/dev/null echo printf 'Total: %s EPS, %.1f MB per day\n' "$TOTAL_EPS" "$(awk -v b="$TOTAL_BPD" 'BEGIN{print b/1048576}')" [ -z "$SOURCES" ] && echo "No log files with enough history were found. Note: logs that only go to journald are measured over the last 24 hours." echo "Saved: $OUTFILE" echo "Open it to see exactly what it contains, then upload it at https://www.cyber-affinity.com/tools/log-estimator"