nginx日志分析


     nginx是常見的web、緩存、代理等服務,以功能強大、服務穩定、占用資源少等優點著稱。然而實際使用中,大家對nginx有一種誤解:認為它很簡單,以至於很少會有人系統的去學習它。

    本篇博文主要講解nginx日志分析,日志的格式可以經過自定義以適用自己的需求。nginx日志主要分為訪問日志(access)、錯誤日志等(error),當nginx作為反向代理,前面使用cdn時,默認的nginx日志格式無法獲取到用戶端IP,需要修改。下面是我修改的nginx日志格式,請參考:

    log_format main 'WebAcessLogInformation dateTime="[$time_local]" '
                    'xForwardedFor="$http_x_forwarded_for" sourceAddress=$remote_addr '
                    'dstAddress=$server_addr dstHostName="$server_name" dstPort="$server_port" '
                    'userAgent="$http_user_agent" bytesOutInfo=$bytes_sent sourceUserName="$remote_user" '
                    'responseCode=$status httpRerfer="$http_referer" logSessionNum="-" '
                    'responseTimems=$request_time responseTimes=- requestRInfo="$request" ';

    說明:“=”前面的字符串是個人定義的常量名,可以為任意名稱,如:“WebAcessLogInformation”可以寫成“nginx-log”等字樣,之所以這樣寫,是為了更方便用awk、cut等工具對日志截取。

    訪問日志分析,常見的需求有:1.統計pv(當天訪問nginx的次數,即:access日志的行數);2.最活躍的前n個IP;3.訪問最頻繁的前n個url;4.各網頁狀態碼數目;5.最活躍的前n個IP的每個IP訪問的最頻繁的前n個URL;6.訪問最頻繁的前n個url的每個url最活躍的前n個IP(有點拗口,哈哈);7.以上各參數統計結果占比......可能對有些同學來說,乍一看這些需求無從下手的樣子,實際上很簡單——就是一些linux命令的組合——就是awk、sort、uniq、cut、head等命令的組合。

     運維這件事兒,很簡單~ 難得是沒有需求,目標不明確,就會感覺無從下手。很多時候,腳本可以幫助我們處理大部分事情,腳本寫的好與壞,關系到使用者能否准確定位到問題,所以請對腳本加上足夠的備注及說明。

     下面是我貢獻的一份腳本,在使用之前,請將nginx日志改成上面格式,腳本使用方式:sh nginx_access_analyze.sh { simple | detail | help }。說明:“simple”選項是簡單分析(簡單輸出)的意思;“detail”選項是詳細分析(詳細輸出)的意思,“help”選項是幫助文檔,包括腳本使用說明、日志格式。

#!/bin/bash
# func: analyze nginx access log.
# version: v1.2
# auth: 任小為 
# tel: 18658160015
# date: 2018.07.11

public(){
    echo ""
    read -p "請輸入要分析的訪問日志: " log_file
    echo ""
    
    if [ ! -f $log_file ];then
        echo -e "未找到: ${log_file} \n"
        exit 1
    fi
    
    if [ ! -s $log_file ];then
        echo -e "${log_file}是空文件 \n"
        exit 1
    fi
    
    top_num=10

    input_file=`echo $log_file | awk -F '/' '{print $(NF)}'`
    analyze_dir=/tmp/nginx_log_analyze
    top_ip_file=$analyze_dir/ngx_log_top_ip_${input_file}.txt
    top_src_url_file=$analyze_dir/ngx_log_top_src_url_${input_file}.txt
    top_dest_url_file=$analyze_dir/ngx_log_top_dest_url_${input_file}.txt
    top_code_file=$analyze_dir/ngx_log_top_code_${input_file}.txt
    
    mkdir -p $analyze_dir
    
    start_time=`head -1 $log_file | awk '{print $2}'|cut -d "[" -f2`
    end_time=`tail -1 $log_file | awk '{print $2}'|cut -d "[" -f2`
    total_nums=`wc -l $log_file | awk '{print $1}'`
    size=`du -sh $log_file | awk '{print $1}'`
    
    #獲取起始與截止時間
    echo -e "訪問起始時間: $start_time ; 截止時間: $end_time \n"
    
    #獲取總行數與大小
    echo -e "共訪問 $total_nums 次 ; 日志大小: $size \n"

    #獲取最活躍IP
    awk -F "sourceAddress=" '{print $2}' $log_file | awk '{print $1}' | sort | uniq -c | sort -rn | head -${top_num} > $top_ip_file

    #獲取訪問來源最多的url
    awk -F "httpRerfer=" '{print $2}' $log_file | awk -F '"' '{print $2}' | sort | uniq -c | sort -rn | head -${top_num} > $top_src_url_file

    #獲取請求最多的url
    awk -F 'requestRInfo=' '{print $2}' $log_file | awk '{print $2}' | sort | uniq -c | sort -rn | head -${top_num} > $top_dest_url_file

    #獲取返回最多的狀態碼
    awk -F " responseCode=" '{print $2}' $log_file | cut -d " " -f1 | sort | uniq -c | sort -rn | head -${top_num} > $top_code_file
}

simple(){
    echo -e "\033[44;36m+-+-+-+-+-+- 下面是粗略分析 +-+-+-+-+-+-\033[0m \n"
    
    #獲取最活躍IP
    printf "\033[44;36m最活躍的前${top_num}個訪問IP: \033[0m \n"
    cat $top_ip_file
    echo ""
    
    #獲取訪問來源最多的url
    printf "\033[44;36m訪問來源最多的前${top_num}個url: \033[0m \n"
    cat $top_src_url_file
    echo ""
    
    #獲取請求最多的url
    printf "\033[44;36m請求最多的前${top_num}個url: \033[0m \n"
    cat $top_dest_url_file
    echo ""
    
    #獲取返回最多的狀態碼
    printf "\033[44;36m返回最多的前${top_num}個狀態碼: \033[0m \n"
    cat $top_code_file
    echo ""
}

detail(){
    echo -e "\033[44;36m+-+-+-+-+-+- 下面是詳細分析 +-+-+-+-+-+-\033[0m \n"
    
    printf "\033[44;36m最活躍的前${top_num}個訪問IP詳情: \033[0m \n"
    ip_nums=`wc -l $top_ip_file | awk '{print $1}'`
    for ((i=1; i<=$ip_nums; i++))
    do
        ip_num=`head -$i $top_ip_file | tail -1 | awk '{print $1}'`
        ip_addr=`head -$i $top_ip_file | tail -1 | awk '{print $2}'`
        ip_percent=`awk 'BEGIN { printf "%.1f%",('$ip_num'/'$total_nums')*100 }'`
        #分別計算訪問最多的url所占百分比
        echo ""
        echo -e "共有來自 ${ip_addr} 的 ${ip_num} 條訪問,占比:\033[31;49;1m ${ip_percent} \033[31;49;0m \n"
        #分別計算每個訪問最多的IP的最多來源url
        echo -e "\033[32;49;1m+=+=+=+= ${ip_addr} \033[31;49;0m訪問來源最多的前${top_num}個url: "
        grep ${ip_addr} $log_file | awk -F "httpRerfer=" '{print $2}' | awk -F '"' '{print $2}' | sort | uniq -c | sort -rn | head -${top_num}
        echo ""
        #分別計算每個請求最多的IP的最多來源url
        echo -e "\033[32;49;1m#+#+#+#+\033[31;49;0m${ip_addr}請求最多的前${top_num}個url: "
        grep ${ip_addr} $log_file | awk -F 'requestRInfo=' '{print $2}' | awk '{print $2}' | sort | uniq -c | sort -rn | head -${top_num}
    done
    
    echo ""
    printf "\033[44;36m訪問來源最多的前${top_num}個url詳情: \033[0m \n"
    url_src_nums=`wc -l $top_src_url_file | awk '{print $1}'`
    for ((k=1; k<=$url_src_nums; k++))
    do
        url_src_num=`head -$k $top_src_url_file | tail -1 | awk '{print $1}'`
        url_src_addr=`head -$k $top_src_url_file | tail -1 | awk '{print $2}'`
        url_src_percent=`awk 'BEGIN { printf "%.1f%",('$url_src_num'/'$total_nums')*100 }'`
        #分別計算訪問來源最多的url所占百分比
        echo ""
        echo -e "共有 ${url_src_num} 條 ${url_src_addr} 的訪問,占比:\033[31;49;1m ${url_src_percent} \033[31;49;0m \n"
        #分別計算每個訪問來源最多的url的最多ip
        echo -e "\033[32;49;1m#-#-#-#-\033[31;49;0m${url_src_addr}訪問最多的前${top_num}個ip: "
        grep "${url_src_addr}" $log_file | awk -F 'sourceAddress=' '{print $2}' | awk '{print $1}' |sort | uniq -c | sort -rn | head -${top_num}
    done
    
    echo ""
    printf "\033[44;36m請求最多的前${top_num}個url詳情: \033[0m \n"
    url_dest_nums=`wc -l $top_dest_url_file | awk '{print $1}'`
    for ((j=1; j<=$url_dest_nums; j++))
    do
        url_dest_num=`head -$j $top_dest_url_file | tail -1 | awk '{print $1}'`
        url_dest_addr=`head -$j $top_dest_url_file | tail -1 | awk '{print $2}'`
        url_dest_percent=`awk 'BEGIN { printf "%.1f%",('$url_dest_num'/'$total_nums')*100 }'`
        #分別計算訪問請求最多的url所占百分比
        echo ""
        echo -e "共有 ${url_dest_num} 條 ${url_dest_addr} 請求的訪問,占比:\033[31;49;1m ${url_dest_percent} \033[31;49;0m \n"
        #分別計算每個訪問請求最多的url的最多ip
        echo -e "\033[32;49;1m#.#.#.#.\033[31;49;0m${url_dest_addr}訪問最多的前${top_num}個ip: "
        grep "${url_dest_addr}" $log_file | awk -F 'sourceAddress=' '{print $2}' | awk '{print $1}' |sort | uniq -c | sort -rn | head -${top_num}
    done
    
    echo ""
    printf "\033[44;36m返回最多的前${top_num}個狀態碼詳情: \033[0m \n"
    code_nums=`wc -l $top_code_file | awk '{print $1}'`
    for ((h=1; h<=$code_nums; h++))
    do
        code_num=`head -$h $top_code_file | tail -1 | awk '{print $1}'`
        code_name=`head -$h $top_code_file | tail -1 | awk '{print $2}'`
        code_percent=`awk 'BEGIN { printf "%.1f%",('$code_num'/'$total_nums')*100 }'`
        #分別計算請求最多的狀態碼百分比
        echo ""
        echo -e "狀態碼為 ${code_name} 的共有 ${code_num} 條,占比:\033[31;49;1m ${code_percent} \033[31;49;0m \n"
        #分別計算每個最多狀態碼的最多ip
        echo -e "\033[32;49;1m*.*.*.*.\033[31;49;0m狀態碼為${code_name}訪問最多的前${top_num}個ip: "
        grep "responseCode=${code_name}" $log_file | awk -F "sourceAddress=" '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -rn | head -${top_num}
    done
    echo ""
}

log_format(){
cat << EOF
log_format main 'WebAcessLogInformation dateTime="[\$time_local]" '
                'xForwardedFor="\$http_x_forwarded_for" sourceAddress=\$remote_addr '
                'dstAddress=\$server_addr dstHostName="\$server_name" dstPort="\$server_port" '
                'userAgent="\$http_user_agent" bytesOutInfo=\$bytes_sent sourceUserName="\$remote_user" '
                'responseCode=\$status httpRerfer="\$http_referer" logSessionNum="-" '
                'responseTimems=\$request_time responseTimes=- requestRInfo="\$request" ';
EOF
echo ""
}

case $1 in
    simple)
        public
        simple
        ;;

    detail)
        public
        detail
        ;;

    help)
        echo ""
        echo -e "\033[44;36m1. 腳本使用方法: sh $0 { simple | detail | help }\033[0m"
        echo -e "\033[44;36m   simple選項代表錯略分析; detail代表詳細分析.\033[0m \n"
        echo -e "\033[44;36m2. 請確保日志必須為如下格式: \033[0m \n"
        log_format
        ;;

    *)
        echo ""
        echo -e $"Usage: $0 { simple | detail | help }\n"
esac
exit 0

    幾點建議:

          1.請將腳本命名成一個有意義的名字,如:nginx_access_analyze.sh ;

          2.寫腳本時,合理利用縮進、空行、注釋、備注等,便於后期維護及他人閱讀;

          3.腳本的輸出結果,合理利用“echo " "”或“print " "”空行,輸出到終端的顏色(如正確的用綠色或不用顏色,錯誤用紅色標記等)利於腳本輸出結果的排版,便於閱讀;

          4.腳本盡量用“模塊”話“拼湊”,這里的“模塊”即bash函數,每個函數盡量簡單,最好只做一件事,如此下來,通篇腳本也會盡量簡單,也會只做“一件事”,不建議寫的太花哨;

          5.腳本的變量盡量放在文件開頭,尤其是容易改動的變量要放在前排;腳本主體或者函數盡量不出現常量(如:引用某個文件的路徑),盡量用變量替代常量(如:用${file_log}代替某個日志文件),這樣做利於他人修改。

 

      光說不練,不如回家喝稀飯!下面是我執行腳本的截圖,請大家參考:

    如果有更好觀點的小伙伴歡迎留言討論,感謝您的閱覽,授人以魚不如授人以漁!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM