#!/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
