--- # Log Rotation and Cleanup Playbook # Manage log files across all services and system components # Usage: ansible-playbook playbooks/log_rotation.yml # Usage: ansible-playbook playbooks/log_rotation.yml -e "aggressive_cleanup=true" # Usage: ansible-playbook playbooks/log_rotation.yml -e "dry_run=true" - name: Log Rotation and Cleanup hosts: "{{ host_target | default('all') }}" gather_facts: yes vars: dry_run: "{{ dry_run | default(false) }}" aggressive_cleanup: "{{ aggressive_cleanup | default(false) }}" max_log_age_days: "{{ max_log_age_days | default(30) }}" max_log_size: "{{ max_log_size | default('100M') }}" keep_compressed_logs: "{{ keep_compressed_logs | default(true) }}" compress_old_logs: "{{ compress_old_logs | default(true) }}" tasks: - name: Create log cleanup report directory file: path: "/tmp/log_cleanup/{{ ansible_date_time.date }}" state: directory mode: '0755' - name: Display log cleanup plan debug: msg: | ๐Ÿ“ LOG ROTATION AND CLEANUP PLAN ================================ ๐Ÿ–ฅ๏ธ Host: {{ inventory_hostname }} ๐Ÿ“… Date: {{ ansible_date_time.date }} ๐Ÿงช Dry Run: {{ dry_run }} ๐Ÿ’ช Aggressive: {{ aggressive_cleanup }} ๐Ÿ“… Max Age: {{ max_log_age_days }} days ๐Ÿ“ฆ Max Size: {{ max_log_size }} ๐Ÿ—œ๏ธ Compress: {{ compress_old_logs }} - name: Analyze current log usage shell: | echo "๐Ÿ“Š LOG USAGE ANALYSIS" echo "====================" total_log_size=0 log_file_count=0 echo "=== SYSTEM LOGS ===" if [ -d "/var/log" ]; then system_log_size=$(du -sh /var/log 2>/dev/null | cut -f1 || echo "0") system_log_count=$(find /var/log -type f -name "*.log" 2>/dev/null | wc -l) echo "System logs: $system_log_size ($system_log_count files)" echo "Largest system logs:" find /var/log -type f -name "*.log" -exec du -h {} \; 2>/dev/null | sort -hr | head -10 || echo "No system logs found" fi echo "" echo "=== DOCKER CONTAINER LOGS ===" if [ -d "/var/lib/docker/containers" ]; then docker_log_size=$(du -sh /var/lib/docker/containers 2>/dev/null | cut -f1 || echo "0") docker_log_count=$(find /var/lib/docker/containers -name "*-json.log" 2>/dev/null | wc -l) echo "Docker logs: $docker_log_size ($docker_log_count files)" echo "Largest container logs:" find /var/lib/docker/containers -name "*-json.log" -exec du -h {} \; 2>/dev/null | sort -hr | head -10 || echo "No Docker logs found" fi echo "" echo "=== APPLICATION LOGS ===" for log_dir in /volume1/docker /opt/docker /home; do if [ -d "$log_dir" ]; then app_logs=$(find "$log_dir" -name "*.log" -type f 2>/dev/null | head -20) if [ -n "$app_logs" ]; then echo "Application logs in $log_dir:" echo "$app_logs" | while read log_file; do if [ -f "$log_file" ]; then du -h "$log_file" 2>/dev/null || echo "Cannot access $log_file" fi done fi fi done echo "" echo "=== LARGE LOG FILES (>{{ max_log_size }}) ===" find /var/log /var/lib/docker/containers /volume1 /opt -name "*.log" -size +{{ max_log_size }} -type f 2>/dev/null | head -20 | while read large_log; do echo "$(du -h "$large_log" 2>/dev/null || echo "? $large_log")" done || echo "No large log files found" echo "" echo "=== OLD LOG FILES (>{{ max_log_age_days }} days) ===" old_logs=$(find /var/log /var/lib/docker/containers /volume1 /opt -name "*.log" -mtime +{{ max_log_age_days }} -type f 2>/dev/null | wc -l) echo "Old log files found: $old_logs" register: log_analysis changed_when: false - name: Rotate system logs shell: | echo "๐Ÿ”„ SYSTEM LOG ROTATION" echo "=====================" rotated_logs=() {% if dry_run %} echo "DRY RUN: System log rotation simulation" # Check what would be rotated if command -v logrotate >/dev/null 2>&1; then echo "Would run: logrotate -d /etc/logrotate.conf" logrotate -d /etc/logrotate.conf 2>/dev/null | head -20 || echo "Logrotate config not found" fi {% else %} # Force log rotation if command -v logrotate >/dev/null 2>&1; then echo "Running logrotate..." logrotate -f /etc/logrotate.conf 2>/dev/null && echo "โœ… System log rotation completed" || echo "โš ๏ธ Logrotate had issues" rotated_logs+=("system_logs") else echo "โš ๏ธ Logrotate not available" fi # Manual rotation for specific large logs for log_file in /var/log/syslog /var/log/auth.log /var/log/kern.log; do if [ -f "$log_file" ] && [ $(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0) -gt 104857600 ]; then # 100MB echo "Rotating large log: $log_file" {% if compress_old_logs %} gzip -c "$log_file" > "${log_file}.$(date +%Y%m%d).gz" && > "$log_file" {% else %} cp "$log_file" "${log_file}.$(date +%Y%m%d)" && > "$log_file" {% endif %} rotated_logs+=("$(basename $log_file)") fi done {% endif %} echo "๐Ÿ“Š ROTATION SUMMARY:" for log in "${rotated_logs[@]}"; do echo " - $log" done register: system_log_rotation - name: Manage Docker container logs shell: | echo "๐Ÿณ DOCKER LOG MANAGEMENT" echo "========================" managed_containers=() total_space_saved=0 {% if dry_run %} echo "DRY RUN: Docker log management simulation" # Show what would be cleaned large_logs=$(find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null) if [ -n "$large_logs" ]; then echo "Would truncate large container logs:" echo "$large_logs" | while read log_file; do size=$(du -h "$log_file" 2>/dev/null | cut -f1) container_id=$(basename $(dirname "$log_file")) container_name=$(docker ps -a --filter "id=$container_id" --format "{{.Names}}" 2>/dev/null || echo "unknown") echo " - $container_name: $size" done fi {% else %} # Truncate large container logs find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null | while read log_file; do if [ -f "$log_file" ]; then container_id=$(basename $(dirname "$log_file")) container_name=$(docker ps -a --filter "id=$container_id" --format "{{.Names}}" 2>/dev/null || echo "unknown") size_before=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0) echo "Truncating log for container: $container_name" # Keep last 1000 lines tail -1000 "$log_file" > "${log_file}.tmp" && mv "${log_file}.tmp" "$log_file" size_after=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0) space_saved=$((size_before - size_after)) total_space_saved=$((total_space_saved + space_saved)) managed_containers+=("$container_name") echo " โœ… Truncated: $(echo $space_saved | numfmt --to=iec) saved" fi done # Clean up old rotated Docker logs {% if aggressive_cleanup %} echo "Cleaning old Docker log files..." find /var/lib/docker/containers -name "*.log.*" -mtime +{{ max_log_age_days }} -delete 2>/dev/null {% endif %} {% endif %} echo "๐Ÿ“Š DOCKER LOG SUMMARY:" echo "Containers managed: ${#managed_containers[@]}" if [ $total_space_saved -gt 0 ]; then echo "Total space saved: $(echo $total_space_saved | numfmt --to=iec)" fi for container in "${managed_containers[@]}"; do echo " - $container" done register: docker_log_management - name: Clean up application logs shell: | echo "๐Ÿ“ฑ APPLICATION LOG CLEANUP" echo "==========================" cleaned_apps=() {% if dry_run %} echo "DRY RUN: Application log cleanup simulation" # Show what would be cleaned for log_dir in /volume1/docker /opt/docker; do if [ -d "$log_dir" ]; then old_app_logs=$(find "$log_dir" -name "*.log" -mtime +{{ max_log_age_days }} -type f 2>/dev/null) if [ -n "$old_app_logs" ]; then echo "Would clean logs in $log_dir:" echo "$old_app_logs" | head -10 fi fi done {% else %} # Clean old application logs for log_dir in /volume1/docker /opt/docker; do if [ -d "$log_dir" ]; then echo "Cleaning logs in $log_dir..." # Compress old logs if requested {% if compress_old_logs %} find "$log_dir" -name "*.log" -mtime +7 -mtime -{{ max_log_age_days }} -type f 2>/dev/null | while read log_file; do if [ -f "$log_file" ] && [[ "$log_file" != *.gz ]]; then gzip "$log_file" 2>/dev/null && echo " Compressed: $(basename $log_file)" fi done {% endif %} # Remove very old logs old_logs_removed=$(find "$log_dir" -name "*.log" -mtime +{{ max_log_age_days }} -type f -delete 2>/dev/null | wc -l) {% if keep_compressed_logs %} old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +$(({{ max_log_age_days }} * 2)) -type f -delete 2>/dev/null | wc -l) {% else %} old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +{{ max_log_age_days }} -type f -delete 2>/dev/null | wc -l) {% endif %} if [ $old_logs_removed -gt 0 ] || [ $old_gz_removed -gt 0 ]; then echo " โœ… Cleaned $old_logs_removed logs, $old_gz_removed compressed logs" cleaned_apps+=("$(basename $log_dir)") fi fi done {% endif %} echo "๐Ÿ“Š APPLICATION CLEANUP SUMMARY:" for app in "${cleaned_apps[@]}"; do echo " - $app" done register: app_log_cleanup - name: Configure log rotation for services shell: | echo "โš™๏ธ LOG ROTATION CONFIGURATION" echo "=============================" config_updates=() {% if dry_run %} echo "DRY RUN: Would configure log rotation" {% else %} # Create custom logrotate config for Docker containers logrotate_config="/etc/logrotate.d/docker-containers" if [ ! -f "$logrotate_config" ]; then echo "Creating Docker container log rotation config..." cat > "$logrotate_config" << 'LOGROTATE_EOF' /var/lib/docker/containers/*/*.log { rotate 7 daily compress size 100M missingok delaycompress copytruncate } LOGROTATE_EOF config_updates+=("docker-containers") echo " โœ… Docker container log rotation configured" fi # Update Docker daemon configuration for log limits docker_config="/etc/docker/daemon.json" if [ -f "$docker_config" ]; then # Check if log driver is already configured if ! grep -q "log-driver" "$docker_config" 2>/dev/null; then echo "Updating Docker daemon log configuration..." # Backup existing config cp "$docker_config" "${docker_config}.backup.$(date +%Y%m%d)" # Add log configuration (this would need careful JSON manipulation in practice) echo " โ„น๏ธ Manual Docker daemon config update recommended" echo " Add: \"log-driver\": \"json-file\", \"log-opts\": {\"max-size\": \"{{ max_log_size }}\", \"max-file\": \"3\"}" fi fi {% endif %} echo "๐Ÿ“Š CONFIGURATION SUMMARY:" for config in "${config_updates[@]}"; do echo " - $config" done register: log_rotation_config - name: Generate log cleanup report copy: content: | ๐Ÿ“ LOG ROTATION AND CLEANUP REPORT - {{ inventory_hostname }} ========================================================== ๐Ÿ“… Cleanup Date: {{ ansible_date_time.iso8601 }} ๐Ÿ–ฅ๏ธ Host: {{ inventory_hostname }} ๐Ÿงช Dry Run: {{ dry_run }} ๐Ÿ’ช Aggressive Mode: {{ aggressive_cleanup }} ๐Ÿ“… Max Age: {{ max_log_age_days }} days ๐Ÿ“ฆ Max Size: {{ max_log_size }} ๐Ÿ“Š LOG USAGE ANALYSIS: {{ log_analysis.stdout }} ๐Ÿ”„ SYSTEM LOG ROTATION: {{ system_log_rotation.stdout }} ๐Ÿณ DOCKER LOG MANAGEMENT: {{ docker_log_management.stdout }} ๐Ÿ“ฑ APPLICATION LOG CLEANUP: {{ app_log_cleanup.stdout }} โš™๏ธ CONFIGURATION UPDATES: {{ log_rotation_config.stdout }} ๐Ÿ’ก RECOMMENDATIONS: - Schedule regular log rotation via cron - Monitor disk usage: ansible-playbook playbooks/disk_usage_report.yml - Configure application-specific log rotation - Set up log monitoring and alerting {% if not dry_run %} - Verify services are functioning after log cleanup {% endif %} {% if 'Large log files' in log_analysis.stdout %} - Consider more aggressive log size limits {% endif %} ๐Ÿ“Š CLEANUP SUMMARY: - System logs: {{ 'Rotated' if 'system_logs' in system_log_rotation.stdout else 'No action needed' }} - Docker logs: {{ 'Managed' if 'managed' in docker_log_management.stdout else 'No action needed' }} - Application logs: {{ 'Cleaned' if 'cleaned' in app_log_cleanup.stdout else 'No action needed' }} - Configuration: {{ 'Updated' if 'config_updates' in log_rotation_config.stdout else 'No changes' }} โœ… LOG CLEANUP COMPLETE dest: "/tmp/log_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_log_cleanup_report.txt" - name: Display log cleanup summary debug: msg: | ๐Ÿ“ LOG CLEANUP COMPLETE - {{ inventory_hostname }} ========================================== ๐Ÿ“… Date: {{ ansible_date_time.date }} ๐Ÿงช Mode: {{ 'Dry Run' if dry_run else 'Live Cleanup' }} ๐Ÿ’ช Aggressive: {{ aggressive_cleanup }} ๐Ÿ“Š ACTIONS TAKEN: {{ system_log_rotation.stdout | regex_replace('\n.*', '') }} {{ docker_log_management.stdout | regex_replace('\n.*', '') }} {{ app_log_cleanup.stdout | regex_replace('\n.*', '') }} ๐Ÿ“„ Full report: /tmp/log_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_log_cleanup_report.txt ๐Ÿ” Next Steps: {% if dry_run %} - Run without dry_run to perform actual cleanup {% endif %} - Monitor disk usage improvements - Schedule regular log rotation - Verify service functionality ========================================== - name: Restart services if needed shell: | echo "๐Ÿ”„ SERVICE RESTART CHECK" echo "========================" # Check if any critical services need restarting after log cleanup services_to_restart=() # Check if rsyslog needs restart (if log files were rotated) if systemctl is-active --quiet rsyslog && [[ "{{ system_log_rotation.stdout }}" == *"system_logs"* ]]; then services_to_restart+=("rsyslog") fi # Check if Docker daemon needs restart (if config was changed) if [[ "{{ log_rotation_config.stdout }}" == *"docker"* ]]; then echo "โš ๏ธ Docker daemon config changed - manual restart may be needed" echo " Run: sudo systemctl restart docker" fi {% if not dry_run %} # Restart services that need it for service in "${services_to_restart[@]}"; do echo "Restarting $service..." systemctl restart "$service" && echo " โœ… $service restarted" || echo " โŒ Failed to restart $service" done {% else %} echo "DRY RUN: Would restart services: ${services_to_restart[*]}" {% endif %} if [ ${#services_to_restart[@]} -eq 0 ]; then echo "โ„น๏ธ No services need restarting" fi register: service_restart when: restart_services | default(true) | bool