--- # 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 "--- 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; do if [ -d "$log_dir" ]; then app_logs=$(timeout 15 find "$log_dir" -maxdepth 4 -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 }}) ---" timeout 15 find /var/log /var/lib/docker/containers -name "*.log" -size +{{ _max_log_size }} -type f 2>/dev/null | head -20 | while read large_log; do 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=$(timeout 15 find /var/log /var/lib/docker/containers -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 ===" rotated_list="" {% if _dry_run %} echo "DRY RUN: System log rotation simulation" 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 %} 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_list="system_logs" else echo "Logrotate not available" fi for log_file in /var/log/syslog /var/log/auth.log /var/log/kern.log; do if [ -f "$log_file" ]; then file_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) if [ "$file_size" -gt 104857600 ]; then 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_list="$rotated_list $(basename $log_file)" fi fi done {% endif %} echo "ROTATION SUMMARY: $rotated_list" if [ -z "$rotated_list" ]; then echo "No logs needed rotation" fi register: system_log_rotation - name: Manage Docker container logs shell: | echo "=== DOCKER LOG MANAGEMENT ===" managed_count=0 total_space_saved=0 {% if _dry_run %} echo "DRY RUN: Docker log management simulation" 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 '{% raw %}{{.Names}}{% endraw %}' 2>/dev/null || echo "unknown") echo " - $container_name: $size" done else echo "No large container logs found" fi {% else %} 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 '{% raw %}{{.Names}}{% endraw %}' 2>/dev/null || echo "unknown") size_before=$(stat -c%s "$log_file" 2>/dev/null || echo 0) echo "Truncating log for container: $container_name" tail -1000 "$log_file" > "$log_file.tmp" && mv "$log_file.tmp" "$log_file" size_after=$(stat -c%s "$log_file" 2>/dev/null || echo 0) space_saved=$((size_before - size_after)) echo " Truncated: $(echo $space_saved | numfmt --to=iec 2>/dev/null || echo ${space_saved}B) saved" fi done {% 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: done" register: docker_log_management - name: Clean up application logs shell: | echo "=== APPLICATION LOG CLEANUP ===" cleaned_count=0 {% if _dry_run %} echo "DRY RUN: Application log cleanup simulation" for log_dir in /volume1/docker /opt/docker; do if [ -d "$log_dir" ]; then old_app_logs=$(timeout 15 find "$log_dir" -maxdepth 4 -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 %} for log_dir in /volume1/docker /opt/docker; do if [ -d "$log_dir" ]; then echo "Cleaning logs in $log_dir..." {% 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" ]; then gzip "$log_file" 2>/dev/null && echo " Compressed: $(basename $log_file)" fi done {% endif %} old_logs_removed=$(find "$log_dir" -name "*.log" -mtime +{{ _max_log_age_days }} -type f -delete -print 2>/dev/null | wc -l) {% if _keep_compressed_logs %} max_gz_age=$(({{ _max_log_age_days }} * 2)) old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +$max_gz_age -type f -delete -print 2>/dev/null | wc -l) {% else %} old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +{{ _max_log_age_days }} -type f -delete -print 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" fi fi done {% endif %} echo "APPLICATION CLEANUP SUMMARY: done" register: app_log_cleanup - name: Configure log rotation for services shell: | echo "=== LOG ROTATION CONFIGURATION ===" config_changed="no" {% if _dry_run %} echo "DRY RUN: Would configure log rotation" {% else %} logrotate_config="/etc/logrotate.d/docker-containers" if [ ! -f "$logrotate_config" ]; then echo "Creating Docker container log rotation config..." printf '%s\n' '/var/lib/docker/containers/*/*.log {' ' rotate 7' ' daily' ' compress' ' size 100M' ' missingok' ' delaycompress' ' copytruncate' '}' > "$logrotate_config" config_changed="yes" echo " Docker container log rotation configured" fi docker_config="/etc/docker/daemon.json" if [ -f "$docker_config" ]; then if ! grep -q "log-driver" "$docker_config" 2>/dev/null; then echo "Docker daemon log configuration recommended" cp "$docker_config" "$docker_config.backup.$(date +%Y%m%d)" 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: config_changed=$config_changed" 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 %} 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 ===" restart_needed="no" if systemctl is-active --quiet rsyslog 2>/dev/null && echo "{{ system_log_rotation.stdout }}" | grep -q "system_logs"; then restart_needed="yes" {% if not _dry_run %} echo "Restarting rsyslog..." systemctl restart rsyslog && echo " rsyslog restarted" || echo " Failed to restart rsyslog" {% else %} echo "DRY RUN: Would restart rsyslog" {% endif %} fi if echo "{{ log_rotation_config.stdout }}" | grep -q "docker"; then echo "Docker daemon config changed - manual restart may be needed" echo " Run: sudo systemctl restart docker" fi if [ "$restart_needed" = "no" ]; then echo "No services need restarting" fi register: service_restart when: restart_services | default(true) | bool