particle-os-tools/particle-logrotate.sh
robojerk 74c7bede5f Initial commit: Particle-OS tools repository
- Complete Particle-OS rebranding from uBlue-OS
- Professional installation system with standardized paths
- Self-initialization system with --init and --reset commands
- Enhanced error messages and dependency checking
- Comprehensive testing infrastructure
- All source scriptlets updated with runtime improvements
- Clean codebase with redundant files moved to archive
- Complete documentation suite
2025-07-11 21:14:33 -07:00

330 lines
No EOL
9.1 KiB
Bash

#!/bin/bash
# Ubuntu uBlue Log Rotation Utility
# Provides log rotation functionality for Ubuntu uBlue logs
set -euo pipefail
# Source unified configuration
if [[ -f "/usr/local/etc/particle-config.sh" ]]; then
source "/usr/local/etc/particle-config.sh"
else
# Fallback configuration
PARTICLE_LOG_DIR="/var/log/particle-os"
PARTICLE_LOG_MAX_SIZE="${PARTICLE_LOG_MAX_SIZE:-100M}"
PARTICLE_LOG_MAX_FILES="${PARTICLE_LOG_MAX_FILES:-5}"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Convert size string to bytes
size_to_bytes() {
local size="$1"
local number
local unit
# Extract number and unit
if [[ "$size" =~ ^([0-9]+)([KMGT]?)$ ]]; then
number="${BASH_REMATCH[1]}"
unit="${BASH_REMATCH[2]}"
else
log_error "Invalid size format: $size"
return 1
fi
case "$unit" in
"K"|"k") echo $((number * 1024)) ;;
"M"|"m") echo $((number * 1024 * 1024)) ;;
"G"|"g") echo $((number * 1024 * 1024 * 1024)) ;;
"T"|"t") echo $((number * 1024 * 1024 * 1024 * 1024)) ;;
"") echo "$number" ;;
*) log_error "Unknown unit: $unit"; return 1 ;;
esac
}
# Get file size in bytes
get_file_size() {
local file="$1"
if [[ -f "$file" ]]; then
stat -c%s "$file" 2>/dev/null || echo "0"
else
echo "0"
fi
}
# Rotate a single log file
rotate_log_file() {
local log_file="$1"
local max_size_bytes="$2"
local max_files="$3"
if [[ ! -f "$log_file" ]]; then
return 0
fi
local current_size
current_size=$(get_file_size "$log_file")
if [[ $current_size -gt $max_size_bytes ]]; then
log_info "Rotating log file: $log_file (size: $current_size bytes)"
# Remove oldest backup if we've reached max_files
local oldest_backup="$log_file.$max_files"
if [[ -f "$oldest_backup" ]]; then
rm -f "$oldest_backup"
fi
# Shift existing backups
for ((i=max_files-1; i>=1; i--)); do
local src="$log_file.$i"
local dst="$log_file.$((i+1))"
if [[ -f "$src" ]]; then
mv "$src" "$dst"
fi
done
# Compress and move current log
local compression_cmd
case "$PARTICLE_LOG_COMPRESSION" in
"gzip")
compression_cmd="gzip -c"
;;
"bzip2")
compression_cmd="bzip2 -c"
;;
"xz")
compression_cmd="xz -c"
;;
"zstd")
compression_cmd="zstd -c"
;;
*)
log_warning "Unknown compression: $PARTICLE_LOG_COMPRESSION, using gzip"
compression_cmd="gzip -c"
;;
esac
local extension
case "$PARTICLE_LOG_COMPRESSION" in
"gzip") extension="gz" ;;
"bzip2") extension="bz2" ;;
"xz") extension="xz" ;;
"zstd") extension="zst" ;;
*) extension="gz" ;;
esac
if eval "$compression_cmd" "$log_file" > "$log_file.1.$extension"; then
# Truncate original file
: > "$log_file"
log_success "Rotated $log_file"
else
log_error "Failed to compress $log_file"
return 1
fi
fi
}
# Rotate all Particle-OS logs
rotate_all_logs() {
log_info "Starting Particle-OS log rotation..."
local max_size_bytes
max_size_bytes=$(size_to_bytes "$PARTICLE_LOG_MAX_SIZE")
# Find all log files in the log directory
if [[ ! -d "$PARTICLE_LOG_DIR" ]]; then
log_warning "Log directory not found: $PARTICLE_LOG_DIR"
return 0
fi
local rotated_count=0
local error_count=0
# Parse log file patterns
local patterns
IFS=' ' read -ra patterns <<< "$PARTICLE_LOG_FILES_PATTERN"
# Rotate each log file matching patterns
for pattern in "${patterns[@]}"; do
while IFS= read -r -d '' log_file; do
if rotate_log_file "$log_file" "$max_size_bytes" "$PARTICLE_LOG_MAX_FILES"; then
rotated_count=$((rotated_count + 1))
else
error_count=$((error_count + 1))
fi
done < <(find "$PARTICLE_LOG_DIR" -name "$pattern" -type f -print0)
done
if [[ $error_count -eq 0 ]]; then
log_success "Log rotation completed successfully"
log_info "Rotated $rotated_count log files"
else
log_warning "Log rotation completed with $error_count errors"
fi
}
# Clean up old log files
cleanup_old_logs() {
log_info "Cleaning up old log files..."
local cleanup_count=0
# Parse log file patterns for cleanup
local patterns
IFS=' ' read -ra patterns <<< "$PARTICLE_LOG_FILES_PATTERN"
# Remove log files older than 30 days
for pattern in "${patterns[@]}"; do
while IFS= read -r -d '' log_file; do
if [[ $(find "$log_file" -mtime +30 2>/dev/null) ]]; then
rm -f "$log_file"
cleanup_count=$((cleanup_count + 1))
log_info "Removed old log file: $log_file"
fi
done < <(find "$PARTICLE_LOG_DIR" -name "$pattern.*" -type f -print0)
done
log_success "Cleanup completed: removed $cleanup_count old log files"
}
# Show log statistics
show_log_stats() {
log_info "Particle-OS Log Statistics"
echo "============================"
echo
if [[ ! -d "$PARTICLE_LOG_DIR" ]]; then
log_warning "Log directory not found: $PARTICLE_LOG_DIR"
return 1
fi
echo "Log Directory: $PARTICLE_LOG_DIR"
echo "Max Size: $PARTICLE_LOG_MAX_SIZE"
echo "Max Files: $PARTICLE_LOG_MAX_FILES"
echo
# Show current log files and their sizes
echo "Current Log Files:"
echo "------------------"
# Parse log file patterns
local patterns
IFS=' ' read -ra patterns <<< "$PARTICLE_LOG_FILES_PATTERN"
for pattern in "${patterns[@]}"; do
while IFS= read -r -d '' log_file; do
local size
size=$(get_file_size "$log_file")
local size_human
size_human=$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B")
echo " $(basename "$log_file"): $size_human"
done < <(find "$PARTICLE_LOG_DIR" -name "$pattern" -type f -print0 | sort -z)
done
echo
# Show backup log files
echo "Backup Log Files:"
echo "-----------------"
local backup_count=0
local backup_size=0
for pattern in "${patterns[@]}"; do
while IFS= read -r -d '' log_file; do
local size
size=$(get_file_size "$log_file")
backup_size=$((backup_size + size))
backup_count=$((backup_count + 1))
local size_human
size_human=$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B")
echo " $(basename "$log_file"): $size_human"
done < <(find "$PARTICLE_LOG_DIR" -name "$pattern.*" -type f -print0 | sort -z)
done
if [[ $backup_count -gt 0 ]]; then
echo
local backup_size_human
backup_size_human=$(numfmt --to=iec-i --suffix=B "$backup_size" 2>/dev/null || echo "${backup_size}B")
echo "Total backup files: $backup_count"
echo "Total backup size: $backup_size_human"
else
echo " No backup files found"
fi
}
# Main function
main() {
case "${1:-}" in
"rotate")
rotate_all_logs
;;
"cleanup")
cleanup_old_logs
;;
"stats")
show_log_stats
;;
"all")
rotate_all_logs
cleanup_old_logs
;;
"help"|"-h"|"--help")
cat << EOF
Particle-OS Log Rotation Utility
Usage: $0 <command> [options]
Commands:
rotate Rotate log files that exceed max size
cleanup Remove log files older than 30 days
stats Show log file statistics
all Run both rotate and cleanup
help, -h, --help Show this help message
Environment Variables:
PARTICLE_LOG_MAX_SIZE=100M Maximum log file size before rotation
PARTICLE_LOG_MAX_FILES=5 Maximum number of backup files to keep
Examples:
$0 rotate # Rotate oversized logs
$0 cleanup # Clean up old logs
$0 stats # Show log statistics
$0 all # Run full maintenance
This utility manages log rotation for Particle-OS logs, ensuring
they don't consume excessive disk space while maintaining a history
of recent activity.
EOF
;;
*)
log_error "Unknown command: ${1:-}"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
}
# Run main function
main "$@"