massive update. Master conf split, now modular with a load sceriprt to drive all configs to scripts. with unraid scpecific safeguard tests , and improved standardized ux. including dynamic host detect, who am i who else it there. EVERY SINGLE SCRIPT UPDATED. DEBATING THAT THIS IS ACUALLY V2
This commit is contained in:
+222
-102
@@ -1,134 +1,254 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# --------------------------------- Clear Logs Script ------------------------------------------
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# Clears unRAID system logs and Docker container logs safely.
|
||||
# Log file paths are configured in Master.conf under LOG_FILES.
|
||||
# Supports --dry-run to preview what would be cleared without making changes.
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ================================= Clear Logs =================================================
|
||||
# ==============================================================================================
|
||||
# Clears system and Docker container logs to prevent rootfs fill over time.
|
||||
# Runs weekly via WEEKLY_MAINTENANCE_SCRIPTS — Sunday 2:30am.
|
||||
# Uses size thresholds — only clears logs that have grown large enough to matter.
|
||||
#
|
||||
# ── WHAT IT CLEARS ────────────────────────────────────────────────────────────────────────────
|
||||
# System logs — LOG_FILES from master.conf (/var/log/syslog, messages, dmesg)
|
||||
# Cleared if size exceeds LOG_MIN_SIZE_MB
|
||||
# These grow continuously — weekly clearing keeps rootfs healthy
|
||||
#
|
||||
# Docker logs — /var/lib/docker/containers/**/*-json.log
|
||||
# Cleared only if individual container log exceeds LOG_DOCKER_MAX_MB
|
||||
# Active containers (Emby, SABnzbd) grow fastest — 100MB+ easily
|
||||
# Inactive containers not cleared — their logs are typically small
|
||||
#
|
||||
# ── SIZE THRESHOLD APPROACH ───────────────────────────────────────────────────────────────────
|
||||
# Truncating everything blindly destroys useful diagnostic context.
|
||||
# A 2MB log is not worth clearing — it contains useful recent history.
|
||||
# A 500MB log is consuming rootfs and contains mostly noise — clear it.
|
||||
#
|
||||
# LOG_MIN_SIZE_MB — system logs under this size are left alone
|
||||
# LOG_DOCKER_MAX_MB — Docker logs under this size are left alone
|
||||
#
|
||||
# ── WHY NOT LOGROTATE ─────────────────────────────────────────────────────────────────────────
|
||||
# unRAID writes to tmpfs (/var/log) — logrotate's compress + archive approach
|
||||
# would consume even more tmpfs space. Truncation (: > file) keeps the file
|
||||
# descriptor open and valid while emptying content — safe for running services.
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# acquire_lock — prevents concurrent runs corrupting logs
|
||||
# Root check — truncating system logs requires root
|
||||
# Size thresholds — only clears logs that have grown large enough
|
||||
# Byte tracking — reports MB freed for weekly digest
|
||||
# validate_unraid — notify validated before use
|
||||
# Silent on clean — small logs = nothing to clear = no output ✅
|
||||
#
|
||||
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
|
||||
# LOG_FILES — system log paths to check and clear
|
||||
# LOG_MIN_SIZE_MB — minimum system log size before clearing (default 10MB)
|
||||
# LOG_DOCKER_MAX_MB — clear Docker log only if above this size (default 100MB)
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# clear_logs.sh — normal run (threshold-based clearing)
|
||||
# clear_logs.sh --dry-run — show what would be cleared and sizes
|
||||
# clear_logs.sh --status — show current log sizes
|
||||
# clear_logs.sh --log — verbose output per file
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/../Master.conf"
|
||||
source "$SCRIPT_DIR/../common.sh"
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_GEAR Setup ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "━━━ $ICON_GEAR Setup ━━━"
|
||||
|
||||
# ROOT CHECK
|
||||
# ==============================================================================================
|
||||
# ━━━ Setup ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
error "Must be run as root"
|
||||
error "Must be run as root — truncating system logs requires root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Running as root"
|
||||
validate_unraid_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_SUMMARY Status ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
acquire_lock
|
||||
|
||||
detect_hosts
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be cleared"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Status ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$SHOW_STATUS" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
||||
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
|
||||
echo "$ICON_CONTAINERS Docker Logs: /var/lib/docker/containers"
|
||||
echo "$ICON_GEAR Dry Run: $DRY_RUN"
|
||||
echo "━━━━━ $ICON_SUMMARY LOG STATUS ━━━━━"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_GEAR Min system log: ${LOG_MIN_SIZE_MB:-10}MB before clearing"
|
||||
echo "$ICON_GEAR Max Docker log: ${LOG_DOCKER_MAX_MB:-100}MB before clearing"
|
||||
echo ""
|
||||
|
||||
echo "━━━ System Logs ━━━"
|
||||
for f in "${LOG_FILES[@]}"; do
|
||||
if [[ -f "$f" ]]; then
|
||||
size=$(du -sh "$f" 2>/dev/null | cut -f1)
|
||||
size_mb=$(du -sm "$f" 2>/dev/null | cut -f1)
|
||||
threshold="${LOG_MIN_SIZE_MB:-10}"
|
||||
if [[ "${size_mb:-0}" -ge "$threshold" ]]; then
|
||||
echo " $ICON_WARN $f — $size (above ${threshold}MB threshold — would clear)"
|
||||
else
|
||||
echo " $ICON_SUCCESS $f — $size (under threshold)"
|
||||
fi
|
||||
else
|
||||
echo " $ICON_SKIP $f — not found"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━ Docker Logs (top 10 by size) ━━━"
|
||||
if [[ -d /var/lib/docker/containers ]]; then
|
||||
find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null | \
|
||||
while IFS= read -r logfile; do
|
||||
size_mb=$(du -sm "$logfile" 2>/dev/null | cut -f1)
|
||||
container_id=$(basename "$(dirname "$logfile")" | cut -c1-12)
|
||||
container_name=$(docker inspect --format '{{.Name}}' "$container_id" \
|
||||
2>/dev/null | tr -d '/' || echo "$container_id")
|
||||
echo "${size_mb:-0} $container_name $logfile"
|
||||
done | sort -rn | head -10 | \
|
||||
while read -r size_mb name logfile; do
|
||||
threshold="${LOG_DOCKER_MAX_MB:-100}"
|
||||
if [[ "$size_mb" -ge "$threshold" ]]; then
|
||||
echo " $ICON_WARN ${size_mb}MB — $name (above ${threshold}MB — would clear)"
|
||||
else
|
||||
echo " $ICON_SUCCESS ${size_mb}MB — $name"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo " Docker directory not found"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# FUNCTIONS
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ==============================================================================================
|
||||
# ━━━ Clear Logs ━━━
|
||||
# ==============================================================================================
|
||||
START=$(date +%s)
|
||||
SYS_CLEARED=0
|
||||
SYS_SKIPPED=0
|
||||
SYS_BYTES=0
|
||||
DOCKER_CLEARED=0
|
||||
DOCKER_SKIPPED=0
|
||||
DOCKER_BYTES=0
|
||||
FAILED=()
|
||||
|
||||
# Clears a single log file if it exists.
|
||||
# Skips with a warning if the file is not found.
|
||||
clear_file() {
|
||||
local file="$1"
|
||||
# ── System Logs ───────────────────────────────────────────────────────────────────────────────
|
||||
for logfile in "${LOG_FILES[@]}"; do
|
||||
if [[ ! -f "$logfile" ]]; then
|
||||
log "$logfile — not found, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
warn "Not found: $file — skipping"
|
||||
return
|
||||
size_bytes=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
|
||||
size_mb=$(( size_bytes / 1048576 ))
|
||||
size_h=$(du -sh "$logfile" 2>/dev/null | cut -f1)
|
||||
threshold="${LOG_MIN_SIZE_MB:-10}"
|
||||
|
||||
if [[ "$size_mb" -lt "$threshold" ]]; then
|
||||
log "$logfile — ${size_h} (under ${threshold}MB — skipping)"
|
||||
(( SYS_SKIPPED++ ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would clear: $file"
|
||||
warn "DRY RUN — would clear: $logfile (${size_h})"
|
||||
(( SYS_CLEARED++ ))
|
||||
SYS_BYTES=$(( SYS_BYTES + size_bytes ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if : > "$logfile" 2>/dev/null; then
|
||||
log "Cleared: $logfile (freed ${size_h})"
|
||||
(( SYS_CLEARED++ ))
|
||||
SYS_BYTES=$(( SYS_BYTES + size_bytes ))
|
||||
else
|
||||
: > "$file"
|
||||
success "Cleared: $file"
|
||||
error "Failed to clear: $logfile"
|
||||
FAILED+=("$logfile")
|
||||
fi
|
||||
}
|
||||
|
||||
# Finds and clears all Docker container json log files.
|
||||
# Skips gracefully if Docker directory or log files are not found.
|
||||
clear_docker_logs() {
|
||||
if [[ ! -d /var/lib/docker/containers ]]; then
|
||||
warn "$ICON_CONTAINERS Docker directory not found — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
local files
|
||||
files=$(find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null || true)
|
||||
|
||||
if [[ -z "$files" ]]; then
|
||||
warn "$ICON_CONTAINERS No Docker logs found — skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS= read -r file; do
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would clear Docker log: $file"
|
||||
else
|
||||
: > "$file"
|
||||
success "Cleared Docker log: $(basename "$(dirname "$file")")"
|
||||
fi
|
||||
done <<< "$files"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_HEALTH Clear Logs ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "━━━ $ICON_HEALTH Clear Logs ━━━"
|
||||
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
|
||||
echo "$ICON_CONTAINERS Docker Logs: enabled"
|
||||
echo "$ICON_GEAR Dry Run: $DRY_RUN"
|
||||
echo ""
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
|
||||
|
||||
START=$(date +%s)
|
||||
CLEAR_FAILED=false
|
||||
|
||||
for logfile in "${LOG_FILES[@]}"; do
|
||||
clear_file "$logfile" || CLEAR_FAILED=true
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "━━━ $ICON_CONTAINERS Docker ━━━"
|
||||
clear_docker_logs
|
||||
# ── Docker Logs ───────────────────────────────────────────────────────────────────────────────
|
||||
if [[ ! -d /var/lib/docker/containers ]]; then
|
||||
log "Docker containers directory not found — skipping Docker log clear"
|
||||
else
|
||||
while IFS= read -r logfile; do
|
||||
[[ -z "$logfile" ]] && continue
|
||||
|
||||
size_bytes=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
|
||||
size_mb=$(( size_bytes / 1048576 ))
|
||||
size_h=$(du -sh "$logfile" 2>/dev/null | cut -f1)
|
||||
threshold="${LOG_DOCKER_MAX_MB:-100}"
|
||||
|
||||
# Get container name for display
|
||||
container_id=$(basename "$(dirname "$logfile")" | cut -c1-12)
|
||||
container_name=$(docker inspect --format '{{.Name}}' "$container_id" \
|
||||
2>/dev/null | tr -d '/' || echo "$container_id")
|
||||
|
||||
if [[ "$size_mb" -lt "$threshold" ]]; then
|
||||
log "Docker $container_name — ${size_h} (under ${threshold}MB — skipping)"
|
||||
(( DOCKER_SKIPPED++ ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would clear Docker log: $container_name (${size_h})"
|
||||
(( DOCKER_CLEARED++ ))
|
||||
DOCKER_BYTES=$(( DOCKER_BYTES + size_bytes ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if : > "$logfile" 2>/dev/null; then
|
||||
log "Cleared Docker log: $container_name (freed ${size_h})"
|
||||
(( DOCKER_CLEARED++ ))
|
||||
DOCKER_BYTES=$(( DOCKER_BYTES + size_bytes ))
|
||||
else
|
||||
error "Failed to clear Docker log: $container_name"
|
||||
FAILED+=("docker:$container_name")
|
||||
fi
|
||||
done < <(find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null)
|
||||
fi
|
||||
|
||||
END=$(date +%s)
|
||||
TOTAL_BYTES=$(( SYS_BYTES + DOCKER_BYTES ))
|
||||
TOTAL_FREED_H=$(awk "BEGIN {printf \"%.1fMB\", $TOTAL_BYTES / 1048576}")
|
||||
TOTAL_CLEARED=$(( SYS_CLEARED + DOCKER_CLEARED ))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_SUMMARY Summary ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY LOG CLEANER SUMMARY ━━━━━"
|
||||
echo "$ICON_HEALTH System Logs: ${LOG_FILES[*]}"
|
||||
echo "$ICON_CONTAINERS Docker Logs: $([[ "$DRY_RUN" == true ]] && echo "skipped (dry run)" || echo "cleared")"
|
||||
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "$ICON_WARN Status: DRY RUN — no changes made"
|
||||
elif [[ "$CLEAR_FAILED" == true ]]; then
|
||||
echo "$ICON_ERROR Status: $ICON_ERROR SOME LOGS FAILED TO CLEAR"
|
||||
notify "Log clear completed with errors on $(hostname)" "Clear Logs" "warning"
|
||||
# ==============================================================================================
|
||||
# ━━━ Summary ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$TOTAL_CLEARED" -gt 0 || ${#FAILED[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY LOG CLEANER SUMMARY ━━━━━"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_HEALTH System cleared: $SYS_CLEARED file(s)"
|
||||
echo "$ICON_CONTAINERS Docker cleared: $DOCKER_CLEARED file(s)"
|
||||
echo "$ICON_HEALTH Total freed: $TOTAL_FREED_H"
|
||||
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
||||
[[ "$SYS_SKIPPED" -gt 0 || "$DOCKER_SKIPPED" -gt 0 ]] && \
|
||||
log "Skipped: ${SYS_SKIPPED} system + ${DOCKER_SKIPPED} Docker (under threshold)"
|
||||
echo ""
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — no files cleared"
|
||||
elif [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
echo "$ICON_ERROR Status: SOME FILES FAILED — ${FAILED[*]}"
|
||||
notify "Log clear failed on $(hostname) ($MY_ID) — ${FAILED[*]}" \
|
||||
"Clear Logs" "warning"
|
||||
else
|
||||
log "$ICON_DONE Status: done — ${TOTAL_FREED_H} freed"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
else
|
||||
echo "$ICON_DONE Status: $ICON_SUCCESS DONE"
|
||||
notify "Logs cleared successfully on $(hostname)" "Clear Logs" "normal"
|
||||
# All logs under threshold — completely silent
|
||||
log "All logs under threshold — nothing to clear"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
[[ ${#FAILED[@]} -gt 0 ]] && exit 1
|
||||
exit 0
|
||||
Reference in New Issue
Block a user