guardia-itsm/scripts/sm/log/log_analysis.sh
2026-05-30 23:02:43 +09:00

192 lines
8.1 KiB
Bash

#!/bin/bash
# ============================================================
# GUARDiA SM | log_analysis.sh
# 대상: 다목적 서버 로그 분석 스크립트
# 파라미터: LOG_FILES="/app/logs/app.log /var/log/messages"
# ANALYZE_HOURS=1 (최근 N시간)
# ERROR_PATTERN="ERROR|FATAL|Exception|OOM|killed"
# WARN_PATTERN="WARN|WARNING|timeout|refused"
# TOP_N=10 (상위 N개 오류 패턴)
# LOG_SIZE_WARN_MB=500 ROTATE_CHECK=true
# ============================================================
set -euo pipefail
LOG_FILES=${LOG_FILES:-""}
ANALYZE_HOURS=${ANALYZE_HOURS:-1}
ERROR_PATTERN=${ERROR_PATTERN:-'ERROR|FATAL|Exception|OutOfMemory|killed process|Caused by'}
WARN_PATTERN=${WARN_PATTERN:-'WARN|WARNING|timeout|refused|connection reset|too many'}
TOP_N=${TOP_N:-10}
LOG_SIZE_WARN_MB=${LOG_SIZE_WARN_MB:-500}
ROTATE_CHECK=${ROTATE_CHECK:-true}
OK="[OK]"; WARN="[WARN]"; CRIT="[CRIT]"
SEP="─────────────────────────────────────────"
RESULT=0
echo "======================================================"
echo " GUARDiA SM 점검 | 로그 분석 | $(hostname -s)"
echo " 점검 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo " 분석 범위: 최근 ${ANALYZE_HOURS}시간"
echo "======================================================"
# 로그 파일이 미지정인 경우 자동 탐색
if [ -z "$LOG_FILES" ]; then
LOG_FILES=""
for AUTO_LOG in \
/opt/tomcat/logs/catalina.out \
/app/was/logs/catalina.out \
/opt/wildfly/standalone/log/server.log \
/opt/jeus/logs/*.log \
/var/log/httpd/error_log \
/var/log/nginx/error.log \
/var/log/messages \
/var/log/syslog; do
ls $AUTO_LOG 2>/dev/null | while read F; do
[ -r "$F" ] && LOG_FILES="${LOG_FILES} ${F}"
done
done
fi
if [ -z "${LOG_FILES// }" ]; then
echo " ${WARN} 분석할 로그 파일이 없음 (LOG_FILES 환경변수 설정 필요)"
exit 1
fi
# ── 각 로그 파일 분석 ─────────────────────────────────────
FILE_NUM=0
for LOGFILE in $LOG_FILES; do
[ -r "$LOGFILE" ] || continue
FILE_NUM=$(( FILE_NUM + 1 ))
FILESIZE_MB=$(du -m "$LOGFILE" 2>/dev/null | awk '{print $1}' || echo 0)
echo
echo "[$SEP] 파일 ${FILE_NUM}: ${LOGFILE}"
echo " 파일 크기: ${FILESIZE_MB} MB 수정: $(stat -c '%y' "$LOGFILE" 2>/dev/null | cut -c1-19 || echo 'N/A')"
# 파일 크기 경고
if [ "${FILESIZE_MB:-0}" -gt "$LOG_SIZE_WARN_MB" ]; then
echo " ${WARN} 로그 파일 크기 과다 (${FILESIZE_MB}MB > ${LOG_SIZE_WARN_MB}MB) — 로테이션 권고"
[ $RESULT -lt 1 ] && RESULT=1
fi
# 최근 라인 수 결정 (파일이 크면 더 많이)
TAIL_LINES=5000
[ "$FILESIZE_MB" -gt 100 ] && TAIL_LINES=10000
RECENT=$(tail -${TAIL_LINES} "$LOGFILE" 2>/dev/null || echo "")
if [ -z "$RECENT" ]; then
echo " ${WARN} 내용을 읽을 수 없음"
continue
fi
# 총 라인 수
TOTAL_LINES=$(echo "$RECENT" | wc -l)
echo " 분석 라인 수: ${TOTAL_LINES} (최근 ${TAIL_LINES}줄)"
# ── 에러 집계 ─────────────────────────────────────────
ERR_COUNT=$(echo "$RECENT" | grep -cE "$ERROR_PATTERN" || echo 0)
WARN_COUNT=$(echo "$RECENT" | grep -cE "$WARN_PATTERN" | grep -v -cE "$ERROR_PATTERN" || echo 0)
if [ "${ERR_COUNT:-0}" -gt 50 ]; then
echo " ${CRIT} 에러 ${ERR_COUNT}건 감지 (임계: 50)"
RESULT=2
elif [ "${ERR_COUNT:-0}" -gt 0 ]; then
echo " ${WARN} 에러 ${ERR_COUNT}건 감지"
[ $RESULT -lt 1 ] && RESULT=1
else
echo " ${OK} 에러 없음"
fi
echo " 경고 수: ${WARN_COUNT}"
# ── 상위 에러 패턴 ────────────────────────────────────
if [ "${ERR_COUNT:-0}" -gt 0 ]; then
echo
echo " [상위 에러 패턴 (Top ${TOP_N})]"
echo "$RECENT" | grep -E "$ERROR_PATTERN" | \
sed 's/[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}//g' | \
sed 's/[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}//g' | \
sed 's/\[.*\]//g' | \
sort | uniq -c | sort -rn | head "${TOP_N}" | \
awk '{count=$1; $1=""; printf " %5d회 %s\n", count, substr($0,2,100)}' || true
echo
echo " [최근 에러 5건]"
echo "$RECENT" | grep -E "$ERROR_PATTERN" | tail -5 | sed 's/^/ /'
fi
# ── OOM 감지 ──────────────────────────────────────────
OOM=$(echo "$RECENT" | grep -iE "OutOfMemoryError|out of memory|java heap space|GC overhead" | tail -5 || echo "")
if [ -n "$OOM" ]; then
echo
echo " ${CRIT} OOM 이벤트 감지:"
echo "$OOM" | sed 's/^/ /'
RESULT=2
fi
# ── 예외 스택트레이스 감지 ────────────────────────────
EXCEPTIONS=$(echo "$RECENT" | grep -cE "^(Caused by:|at [a-z].*\()" || echo 0)
echo " 스택트레이스 관련 라인: ${EXCEPTIONS}"
# ── HTTP 에러 코드 집계 (웹 로그 형식 감지) ───────────
HTTP5XX=$(echo "$RECENT" | grep -cE '" 5[0-9]{2} ' || echo 0)
HTTP4XX=$(echo "$RECENT" | grep -cE '" 4[0-9]{2} ' || echo 0)
if [ "${HTTP5XX:-0}" -gt 0 ] || [ "${HTTP4XX:-0}" -gt 0 ]; then
echo
echo " [HTTP 에러 코드 집계]"
echo " 5xx 에러: ${HTTP5XX}"
echo " 4xx 에러: ${HTTP4XX}"
[ "${HTTP5XX:-0}" -gt 10 ] && echo " ${WARN} 5xx 에러 과다" && [ $RESULT -lt 1 ] && RESULT=1
fi
# ── 주요 키워드 타임라인 (마지막 발생 시각) ──────────
echo
echo " [주요 이벤트 마지막 발생 시각]"
for KW in "ERROR" "WARN" "OutOfMemory" "Connection refused" "timeout"; do
LAST=$(echo "$RECENT" | grep -i "$KW" | tail -1 | cut -c1-30 || echo "")
[ -n "$LAST" ] && printf " %-20s: %s\n" "$KW" "$LAST"
done
done
[ "$FILE_NUM" -eq 0 ] && echo " ${WARN} 읽을 수 있는 로그 파일 없음"
# ── 시스템 로그 (dmesg) ───────────────────────────────────
echo
echo "[$SEP] 시스템 커널 메시지 (최근 1시간)"
DMESG=$(dmesg --since="1 hour ago" 2>/dev/null | \
grep -iE "error|oom|kill|fault|panic|blocked for" | tail -10 || \
dmesg 2>/dev/null | tail -50 | grep -iE "error|oom|kill|fault" | tail -10 || echo "")
if [ -n "$DMESG" ]; then
echo " ${WARN} 커널 메시지:"
echo "$DMESG" | sed 's/^/ /'
[ $RESULT -lt 1 ] && RESULT=1
else
echo " ${OK} 이상 없음"
fi
# ── 로그 로테이션 상태 ───────────────────────────────────
if [ "$ROTATE_CHECK" = "true" ]; then
echo
echo "[$SEP] 로그 로테이션 설정"
if [ -f /etc/logrotate.conf ]; then
echo " ${OK} logrotate 설정 존재"
CRON_ROTATE=$(crontab -l 2>/dev/null | grep -i logrotate || \
ls /etc/cron.daily/logrotate 2>/dev/null && echo "cron.daily 설정" || echo "")
[ -n "$CRON_ROTATE" ] && echo " ${OK} logrotate 예약 실행: ${CRON_ROTATE}" || \
echo " ${WARN} logrotate cron 미설정"
else
echo " ${WARN} logrotate 설정 없음"
[ $RESULT -lt 1 ] && RESULT=1
fi
fi
# ── 요약 ─────────────────────────────────────────────────
echo
echo "======================================================"
case $RESULT in
0) echo " 최종 결과: ${OK} 로그 정상" ;;
1) echo " 최종 결과: ${WARN} 주의 항목 있음" ;;
2) echo " 최종 결과: ${CRIT} 심각한 오류 감지" ;;
esac
echo " 점검 완료: $(date '+%Y-%m-%d %H:%M:%S')"
echo "======================================================"
exit $RESULT