250 lines
10 KiB
YAML
250 lines
10 KiB
YAML
---
|
|
# Container Logs Collection Playbook
|
|
# Collect logs from multiple containers for troubleshooting
|
|
# Usage: ansible-playbook playbooks/container_logs.yml -e "service_name=plex"
|
|
# Usage: ansible-playbook playbooks/container_logs.yml -e "service_pattern=immich"
|
|
# Usage: ansible-playbook playbooks/container_logs.yml -e "collect_all=true"
|
|
|
|
- name: Collect Container Logs
|
|
hosts: "{{ host_target | default('all') }}"
|
|
gather_facts: yes
|
|
vars:
|
|
target_service_name: "{{ service_name | default('') }}"
|
|
target_service_pattern: "{{ service_pattern | default('') }}"
|
|
target_collect_all: "{{ collect_all | default(false) }}"
|
|
target_log_lines: "{{ log_lines | default(100) }}"
|
|
target_log_since: "{{ log_since | default('1h') }}"
|
|
output_dir: "/tmp/container_logs/{{ ansible_date_time.date }}"
|
|
target_include_timestamps: "{{ include_timestamps | default(true) }}"
|
|
target_follow_logs: "{{ follow_logs | default(false) }}"
|
|
|
|
tasks:
|
|
- name: Validate input parameters
|
|
fail:
|
|
msg: "Specify either service_name, service_pattern, or collect_all=true"
|
|
when:
|
|
- target_service_name == ""
|
|
- target_service_pattern == ""
|
|
- not (target_collect_all | bool)
|
|
|
|
- name: Check if Docker is running
|
|
systemd:
|
|
name: docker
|
|
register: docker_status
|
|
failed_when: docker_status.status.ActiveState != "active"
|
|
|
|
- name: Create local log directory
|
|
file:
|
|
path: "{{ output_dir }}/{{ inventory_hostname }}"
|
|
state: directory
|
|
mode: '0755'
|
|
delegate_to: localhost
|
|
|
|
- name: Create remote log directory
|
|
file:
|
|
path: "{{ output_dir }}/{{ inventory_hostname }}"
|
|
state: directory
|
|
mode: '0755'
|
|
|
|
- name: Get specific service container
|
|
shell: 'docker ps -a --filter "name={{ target_service_name }}" --format "{%raw%}{{.Names}}{%endraw%}"'
|
|
register: specific_container
|
|
when: target_service_name != ""
|
|
changed_when: false
|
|
|
|
- name: Get containers matching pattern
|
|
shell: 'docker ps -a --filter "name={{ target_service_pattern }}" --format "{%raw%}{{.Names}}{%endraw%}"'
|
|
register: pattern_containers
|
|
when: target_service_pattern != ""
|
|
changed_when: false
|
|
|
|
- name: Get all containers
|
|
shell: 'docker ps -a --format "{%raw%}{{.Names}}{%endraw%}"'
|
|
register: all_containers
|
|
when: target_collect_all | bool
|
|
changed_when: false
|
|
|
|
- name: Combine container lists
|
|
set_fact:
|
|
target_containers: >-
|
|
{{
|
|
(specific_container.stdout_lines | default([])) +
|
|
(pattern_containers.stdout_lines | default([])) +
|
|
(all_containers.stdout_lines | default([]) if target_collect_all | bool else [])
|
|
}}
|
|
|
|
- name: Display target containers
|
|
debug:
|
|
msg: |
|
|
📦 CONTAINER LOG COLLECTION
|
|
===========================
|
|
🖥️ Host: {{ inventory_hostname }}
|
|
📋 Target Containers: {{ target_containers | length }}
|
|
{% for container in target_containers %}
|
|
- {{ container }}
|
|
{% endfor %}
|
|
📏 Log Lines: {{ target_log_lines }}
|
|
⏰ Since: {{ target_log_since }}
|
|
|
|
- name: Fail if no containers found
|
|
fail:
|
|
msg: "No containers found matching the criteria"
|
|
when: target_containers | length == 0
|
|
|
|
- name: Get container information
|
|
shell: |
|
|
docker inspect {{ item }} --format='
|
|
Container: {{ item }}
|
|
Image: {%raw%}{{.Config.Image}}{%endraw%}
|
|
Status: {%raw%}{{.State.Status}}{%endraw%}
|
|
Started: {%raw%}{{.State.StartedAt}}{%endraw%}
|
|
Restart Count: {%raw%}{{.RestartCount}}{%endraw%}
|
|
Health: {%raw%}{{if .State.Health}}{{.State.Health.Status}}{{else}}No health check{{end}}{%endraw%}
|
|
'
|
|
register: container_info
|
|
loop: "{{ target_containers }}"
|
|
changed_when: false
|
|
|
|
- name: Collect container logs
|
|
shell: |
|
|
echo "=== CONTAINER INFO ===" > {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log
|
|
docker inspect {{ item }} --format='
|
|
Container: {{ item }}
|
|
Image: {%raw%}{{.Config.Image}}{%endraw%}
|
|
Status: {%raw%}{{.State.Status}}{%endraw%}
|
|
Started: {%raw%}{{.State.StartedAt}}{%endraw%}
|
|
Restart Count: {%raw%}{{.RestartCount}}{%endraw%}
|
|
Health: {%raw%}{{if .State.Health}}{{.State.Health.Status}}{{else}}No health check{{end}}{%endraw%}
|
|
' >> {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log
|
|
echo "" >> {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log
|
|
echo "=== CONTAINER LOGS ===" >> {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log
|
|
{% if target_include_timestamps | bool %}
|
|
docker logs {{ item }} --since={{ target_log_since }} --tail={{ target_log_lines }} -t >> {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log 2>&1
|
|
{% else %}
|
|
docker logs {{ item }} --since={{ target_log_since }} --tail={{ target_log_lines }} >> {{ output_dir }}/{{ inventory_hostname }}/{{ item }}.log 2>&1
|
|
{% endif %}
|
|
loop: "{{ target_containers }}"
|
|
ignore_errors: yes
|
|
|
|
- name: Get container resource usage
|
|
shell: 'docker stats {{ target_containers | join(" ") }} --no-stream --format "table {%raw%}{{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}{%endraw%}"'
|
|
register: container_stats
|
|
when: target_containers | length > 0
|
|
ignore_errors: yes
|
|
|
|
- name: Save container stats
|
|
copy:
|
|
content: |
|
|
Container Resource Usage - {{ ansible_date_time.iso8601 }}
|
|
Host: {{ inventory_hostname }}
|
|
|
|
{{ container_stats.stdout }}
|
|
dest: "{{ output_dir }}/{{ inventory_hostname }}/container_stats.txt"
|
|
when: container_stats.stdout is defined
|
|
|
|
- name: Check for error patterns in logs
|
|
shell: |
|
|
echo "=== ERROR ANALYSIS ===" > {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
echo "Host: {{ inventory_hostname }}" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
echo "Timestamp: {{ ansible_date_time.iso8601 }}" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
echo "" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
|
|
for container in {{ target_containers | join(' ') }}; do
|
|
echo "=== $container ===" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
|
|
# Count error patterns
|
|
error_count=$(docker logs $container --since={{ target_log_since }} 2>&1 | grep -i -E "(error|exception|failed|fatal|panic)" | wc -l)
|
|
warn_count=$(docker logs $container --since={{ target_log_since }} 2>&1 | grep -i -E "(warn|warning)" | wc -l)
|
|
|
|
echo "Errors: $error_count" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
echo "Warnings: $warn_count" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
|
|
# Show recent errors
|
|
if [ $error_count -gt 0 ]; then
|
|
echo "Recent Errors:" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
docker logs $container --since={{ target_log_since }} 2>&1 | grep -i -E "(error|exception|failed|fatal|panic)" | tail -5 >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
fi
|
|
echo "" >> {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
done
|
|
when: target_containers | length > 0
|
|
ignore_errors: yes
|
|
|
|
- name: Create summary report
|
|
copy:
|
|
content: |
|
|
📊 CONTAINER LOG COLLECTION SUMMARY
|
|
===================================
|
|
|
|
🖥️ Host: {{ inventory_hostname }}
|
|
📅 Collection Time: {{ ansible_date_time.iso8601 }}
|
|
📦 Containers Processed: {{ target_containers | length }}
|
|
📏 Log Lines per Container: {{ target_log_lines }}
|
|
⏰ Time Range: {{ target_log_since }}
|
|
|
|
📋 CONTAINERS:
|
|
{% for container in target_containers %}
|
|
- {{ container }}
|
|
{% endfor %}
|
|
|
|
📁 LOG FILES LOCATION:
|
|
{{ output_dir }}/{{ inventory_hostname }}/
|
|
|
|
📄 FILES CREATED:
|
|
{% for container in target_containers %}
|
|
- {{ container }}.log
|
|
{% endfor %}
|
|
- container_stats.txt
|
|
- error_summary.txt
|
|
- collection_summary.txt (this file)
|
|
|
|
🔍 QUICK ANALYSIS:
|
|
Use these commands to analyze the logs:
|
|
|
|
# View error summary
|
|
cat {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
|
|
# Search for specific patterns
|
|
grep -i "error" {{ output_dir }}/{{ inventory_hostname }}/*.log
|
|
|
|
# View container stats
|
|
cat {{ output_dir }}/{{ inventory_hostname }}/container_stats.txt
|
|
|
|
# Follow live logs (if needed)
|
|
{% for container in target_containers[:3] %}
|
|
docker logs -f {{ container }}
|
|
{% endfor %}
|
|
|
|
dest: "{{ output_dir }}/{{ inventory_hostname }}/collection_summary.txt"
|
|
|
|
- name: Display collection results
|
|
debug:
|
|
msg: |
|
|
|
|
✅ LOG COLLECTION COMPLETE
|
|
==========================
|
|
🖥️ Host: {{ inventory_hostname }}
|
|
📦 Containers: {{ target_containers | length }}
|
|
📁 Location: {{ output_dir }}/{{ inventory_hostname }}/
|
|
|
|
📄 Files Created:
|
|
{% for container in target_containers %}
|
|
- {{ container }}.log
|
|
{% endfor %}
|
|
- container_stats.txt
|
|
- error_summary.txt
|
|
- collection_summary.txt
|
|
|
|
🔍 Quick Commands:
|
|
# View errors: cat {{ output_dir }}/{{ inventory_hostname }}/error_summary.txt
|
|
# View stats: cat {{ output_dir }}/{{ inventory_hostname }}/container_stats.txt
|
|
|
|
==========================
|
|
|
|
- name: Archive logs (optional)
|
|
archive:
|
|
path: "{{ output_dir }}/{{ inventory_hostname }}"
|
|
dest: "{{ output_dir }}/{{ inventory_hostname }}_logs_{{ ansible_date_time.epoch }}.tar.gz"
|
|
remove: no
|
|
when: archive_logs | default(false) | bool
|
|
delegate_to: localhost
|