#!/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 [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 "$@"