/
/
/
1#!/bin/bash
2# ==============================================================================
3# Docker Logs Script
4# ==============================================================================
5#
6# Description: View and manage Docker service logs with advanced filtering
7# Usage: ./docker-logs.sh [service] [follow|tail|search|errors|stats|cleanup]
8#
9# This script is automatically generated by Ansible - DO NOT EDIT MANUALLY
10# Template: docker-logs.sh.j2
11#
12# ==============================================================================
13
14set -euo pipefail
15
16# Configuration
17DOCKER_COMPOSE_DIR="{{ docker_base_path }}"
18LOG_FILE="/var/log/docker-logs-manager.log"
19DEFAULT_TAIL_LINES=100
20MAX_LOG_FILES=10
21LOG_RETENTION_DAYS=30
22
23# Service order
24SERVICES=(
25 "{{ unbound_service_name }}"
26 "{{ pihole_service_name }}"
27 "{{ nginx_proxy_db_service_name }}"
28 "{{ nginx_proxy_service_name }}"
29 "{{ wireguard_service_name }}"
30)
31
32# Logging function
33log() {
34 echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "${LOG_FILE}"
35}
36
37# Error handling function
38error_exit() {
39 log "ERROR: $1"
40 exit 1
41}
42
43# Check if container exists
44container_exists() {
45 docker inspect "$1" >/dev/null 2>&1
46}
47
48# Check if container is running
49container_running() {
50 docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null | grep -q "true"
51}
52
53# Show logs for a specific service
54show_service_logs() {
55 local service="$1"
56 local mode="${2:-tail}"
57 local lines="${3:-${DEFAULT_TAIL_LINES}}"
58
59 if ! container_exists "${service}"; then
60 error_exit "Container does not exist: ${service}"
61 fi
62
63 case "${mode}" in
64 follow)
65 log "Following logs for ${service}"
66 docker logs -f "${service}" 2>&1
67 ;;
68 tail)
69 log "Showing last ${lines} lines for ${service}"
70 docker logs --tail "${lines}" "${service}" 2>&1
71 ;;
72 full)
73 log "Showing full logs for ${service}"
74 docker logs "${service}" 2>&1
75 ;;
76 *)
77 error_exit "Invalid mode: ${mode}"
78 ;;
79 esac
80}
81
82# Search logs across all services
83search_logs() {
84 local pattern="$1"
85 local since="${2:-1h}"
86
87 log "Searching for '${pattern}' in logs (since: ${since})"
88
89 local total_matches=0
90
91 for service in "${SERVICES[@]}"; do
92 if container_exists "${service}"; then
93 local matches=$(docker logs --since "${since}" "${service}" 2>&1 | grep -i "${pattern}" | wc -l)
94
95 if [[ ${matches} -gt 0 ]]; then
96 echo ""
97 echo "=== ${service} (${matches} matches) ==="
98 docker logs --since "${since}" "${service}" 2>&1 | grep -i "${pattern}" | tail -10
99 total_matches=$((total_matches + matches))
100 fi
101 fi
102 done
103
104 echo ""
105 echo "Total matches found: ${total_matches}"
106}
107
108# Show error logs
109show_errors() {
110 local since="${1:-1h}"
111
112 log "Showing error logs (since: ${since})"
113
114 local total_errors=0
115
116 for service in "${SERVICES[@]}"; do
117 if container_exists "${service}"; then
118 local errors=$(docker logs --since "${since}" "${service}" 2>&1 | grep -i -E "(error|fail|exception|critical|warning)" | wc -l)
119
120 if [[ ${errors} -gt 0 ]]; then
121 echo ""
122 echo "=== ${service} (${errors} errors) ==="
123 docker logs --since "${since}" "${service}" 2>&1 | grep -i -E "(error|fail|exception|critical|warning)" | tail -5
124 total_errors=$((total_errors + errors))
125 fi
126 fi
127 done
128
129 echo ""
130 echo "Total errors found: ${total_errors}"
131}
132
133# Show log statistics
134show_log_stats() {
135 local since="${1:-24h}"
136
137 log "Log Statistics (since: ${since})"
138 echo "=============================================================================="
139 echo "Service Log Lines Errors Warnings Size"
140 echo "------------------------------------------------------------------------------"
141
142 local total_lines=0
143 local total_errors=0
144 local total_warnings=0
145
146 for service in "${SERVICES[@]}"; do
147 if container_exists "${service}"; then
148 local logs=$(docker logs --since "${since}" "${service}" 2>&1)
149 local line_count=$(echo "${logs}" | wc -l)
150 local error_count=$(echo "${logs}" | grep -i -E "(error|fail|exception|critical)" | wc -l)
151 local warning_count=$(echo "${logs}" | grep -i "warning" | wc -l)
152
153 # Estimate log size
154 local size_estimate=$(( line_count * 100 )) # Approximate bytes per line
155 local size_display=""
156
157 if [[ ${size_estimate} -lt 1024 ]]; then
158 size_display="${size_estimate}B"
159 elif [[ ${size_estimate} -lt 1048576 ]]; then
160 size_display="$(echo "scale=1; ${size_estimate}/1024" | bc)KB"
161 else
162 size_display="$(echo "scale=1; ${size_estimate}/1048576" | bc)MB"
163 fi
164
165 printf "%-22s %-11s %-9s %-10s %s\n" "${service}" "${line_count}" "${error_count}" "${warning_count}" "${size_display}"
166
167 total_lines=$((total_lines + line_count))
168 total_errors=$((total_errors + error_count))
169 total_warnings=$((total_warnings + warning_count))
170 else
171 printf "%-22s %-11s %-9s %-10s %s\n" "${service}" "N/A" "N/A" "N/A" "N/A"
172 fi
173 done
174
175 echo "------------------------------------------------------------------------------"
176 echo "Total: ${total_lines} ${total_errors} ${total_warnings}"
177 echo "=============================================================================="
178}
179
180# Cleanup old log files
181cleanup_logs() {
182 local retention_days="${1:-${LOG_RETENTION_DAYS}}"
183
184 log "Cleaning up old log files (retention: ${retention_days} days)"
185
186 local total_removed=0
187
188 # Cleanup Docker container logs
189 for service in "${SERVICES[@]}"; do
190 if container_exists "${service}"; then
191 local container_id=$(docker inspect -f '{{.Id}}' "${service}" 2>/dev/null)
192 if [[ -n "${container_id}" ]]; then
193 local log_path="/var/lib/docker/containers/${container_id}/${container_id}-json.log"
194 if [[ -f "${log_path}" ]]; then
195 # Rotate log file if it's too large
196 local log_size=$(stat -c%s "${log_path}" 2>/dev/null || echo 0)
197 if [[ ${log_size} -gt 104857600 ]]; then # 100MB
198 log "Rotating large log file for ${service} (${log_size} bytes)"
199 docker logs "${service}" > "${log_path}.old" 2>/dev/null
200 echo "" > "${log_path}"
201 total_removed=$((total_removed + 1))
202 fi
203 fi
204 fi
205 fi
206 done
207
208 # Cleanup script log files
209 local script_logs_removed=$(find "/var/log" -name "docker-*.log" -mtime +${retention_days} -delete -print | wc -l)
210 total_removed=$((total_removed + script_logs_removed))
211
212 log "Cleanup completed: ${total_removed} log files processed"
213}
214
215# Show service list
216show_services() {
217 echo "Available Services:"
218 echo "==================="
219
220 for service in "${SERVICES[@]}"; do
221 if container_exists "${service}"; then
222 if container_running "${service}"; then
223 echo " ${service} (RUNNING)"
224 else
225 echo " ${service} (STOPPED)"
226 fi
227 else
228 echo " ${service} (NOT CREATED)"
229 fi
230 done
231
232 echo ""
233 echo "Usage Examples:"
234 echo " $0 {{ unbound_service_name }} follow"
235 echo " $0 {{ pihole_service_name }} tail 50"
236 echo " $0 search 'error'"
237 echo " $0 errors 2h"
238 echo " $0 stats"
239 echo " $0 cleanup"
240}
241
242# Show usage
243usage() {
244 cat << EOF
245Docker Logs Script
246
247Usage: $0 [service] [command] [options]
248
249Commands for specific service:
250 follow Follow logs in real-time
251 tail [N] Show last N lines (default: ${DEFAULT_TAIL_LINES})
252 full Show full log history
253
254Global commands:
255 search PATTERN Search for pattern across all services
256 errors [TIME] Show error logs (default: 1h)
257 stats [TIME] Show log statistics (default: 24h)
258 cleanup [DAYS] Cleanup old logs (default: ${LOG_RETENTION_DAYS} days)
259 services List available services
260 help Show this help message
261
262Time formats: 1h, 2h, 24h, 2d, 1w, etc.
263
264Examples:
265 $0 {{ unbound_service_name }} follow
266 $0 {{ nginx_proxy_service_name }} tail 50
267 $0 search "connection refused"
268 $0 errors 2h
269 $0 stats
270 $0 cleanup 14
271 $0 services
272 $0 help
273EOF
274}
275
276# Main execution
277main() {
278 local service="${1:-}"
279 local command="${2:-}"
280 local option="${3:-}"
281
282 # Global commands
283 case "${service}" in
284 search)
285 search_logs "${command}" "${option:-1h}"
286 return
287 ;;
288 errors)
289 show_errors "${command:-1h}"
290 return
291 ;;
292 stats)
293 show_log_stats "${command:-24h}"
294 return
295 ;;
296 cleanup)
297 cleanup_logs "${command:-${LOG_RETENTION_DAYS}}"
298 return
299 ;;
300 services)
301 show_services
302 return
303 ;;
304 help|*)
305 if [[ -z "${service}" ]]; then
306 usage
307 return
308 fi
309 ;;
310 esac
311
312 # Service-specific commands
313 if [[ -z "${service}" ]]; then
314 usage
315 return
316 fi
317
318 case "${command}" in
319 follow)
320 show_service_logs "${service}" "follow"
321 ;;
322 tail)
323 show_service_logs "${service}" "tail" "${option:-${DEFAULT_TAIL_LINES}}"
324 ;;
325 full)
326 show_service_logs "${service}" "full"
327 ;;
328 *)
329 # Default to tail if no command specified
330 show_service_logs "${service}" "tail" "${DEFAULT_TAIL_LINES}"
331 ;;
332 esac
333}
334
335# Run main function with all arguments
336main "$@"