💻
Code Snippet of the Day — August 6, 2026Bash: Log Rotation with Compression, Size Triggers & SIGUSR1 Reloadlogrotate runs on a schedule and doesn't signal your app to reopen its log handle. This script does size-based rotation with zero lost lines.
#!/usr/bin/env bash
set -euo pipefail
MAX_SIZE_MB=100; KEEP=5; COMPRESS="gzip"
APP_PID_FILE=""; WATCH_MODE=false
while [[ $# -gt 0 ]]; do
case "$1" in
--max-size) MAX_SIZE_MB="$2"; shift 2 ;;
--keep) KEEP="$2"; shift 2 ;;
--compress) COMPRESS="$2"; shift 2 ;;
--app-pid) APP_PID_FILE="$2"; shift 2 ;;
--watch) WATCH_MODE=true; shift ;;
*) LOG_FILES+=("$1"); shift ;;
esac
done
rotate_one() {
local f="$1"
[[ -f "$f" ]] || return 0
local mb=$(( $(stat -c '%s' "$f") / 1048576 ))
[[ "$mb" -lt "$MAX_SIZE_MB" ]] && return 0
local ts=$(date +%Y%m%d-%H%M%S)
cp "$f" "${f}.${ts}"; : > "$f"
[[ -n "$APP_PID_FILE" && -f "$APP_PID_FILE" ]] && \
kill -USR1 "$(cat "$APP_PID_FILE")" 2>/dev/null
[[ "$COMPRESS" == "gzip" ]] && gzip -9 "${f}.${ts}"
}
if [[ "$WATCH_MODE" == true ]]; then
while true; do for f in "${LOG_FILES[@]}"; do rotate_one "$f"; done; sleep 30; done
else
for f in "${LOG_FILES[@]}"; do rotate_one "$f"; done
fi
Usage:./rotate_logs.sh --max-size 100 --keep 5 --compress gzip /var/log/app/server.log
./rotate_logs.sh --max-size 500 --app-pid /var/run/app.pid /var/log/app/*.log
./rotate_logs.sh --watch --max-size 256 /var/log/app/*.log &
💡
Copy+truncate instead of mv keeps the inode alive. SIGUSR1 tells the app to reopen — zero lost lines.