使用python分析nginx訪問日誌
阿新 • • 發佈:2019-02-03
一、在nginx配置檔案中新增日誌的格式,並開啟記錄訪問日誌的功能
#nginx配置檔案
#新增的欄位、
access_log on;
log_format '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
二、指令碼內容
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#script_name: nginx_log_analysis.py
#function: nginx訪問日誌分析指令碼
import sys
from time import clock
#該類是用來列印格式
class displayFormat(object):
def format_size(self,size):
'''格式化流量單位'''
KB = 1024 #KB -> B B是位元組
MB = 1048576 #MB -> B 1024 * 1024
GB = 1073741824 #GB -> B 1024 * 1024 * 1024
TB = 1099511627776 #TB -> B 1024 * 1024 * 1024 * 1024
if size >= TB :
size = str(size >> 40) + 'T'
elif size < KB :
size = str(size) + 'B'
elif size >= GB and size < TB:
size = str(size >> 30 ) + 'G'
elif size >= MB and size < GB :
size = str(size >> 20) + 'M'
else :
size = str(size >> 10) + 'K'
return size
#定義字串格式化
formatstring = '%-15s %-10s %-12s %8s %10s %10s %10s %10s %10s %10s %10s'
def transverse_line(self) :
'''輸出橫線'''
print(self.formatstring % ('-'*15,'-'*10,'-'*12,'-'*12,'-'*10,'-'*10,'-'*10,'-'*10,'-'*10,'-'*10,'-'*10))
def head(self):
'''輸出頭部資訊'''
print(self.formatstring % ('IP','Traffic','Times','Times%','200','404','500','403','302','304','503'))
def error_print(self) :
'''輸出錯誤資訊'''
print('Usage : ' + sys.argv[0] + ' NginxLogFilePath [Number]')
sys.exit(1)
def execut_time(self):
'''輸出指令碼執行的時間'''
print()
print("Script Execution Time: %.3f second" % clock())
print()
#該類是用來生成主機資訊的字典
class hostInfo(object):
#每個主機資訊,如某個主機200出現的次數 請求的次數 該主機請求的流量之和
host_info = ['200','404','500','302','304','503','403','times','size']
def __init__(self,host):
self.host = host = {}.fromkeys(self.host_info,0)
def increment(self,status_times_size,is_size):
'''該方法是用來給host_info中的各個值加1'''
if status_times_size == 'times':
self.host['times'] += 1
elif is_size:
self.host['size'] = self.host['size'] + status_times_size
else:
self.host[status_times_size] += 1
def get_value(self,value):
'''該方法是取到各個主機資訊中對應的值'''
return self.host[value]
#該類是用來分析檔案
class fileAnalysis(object):
def __init__(self):
'''初始化一個空字典'''
self.report_dict = {}
self.total_request_times,self.total_traffic,self.total_200, \
self.total_404,self.total_500,self.total_403,self.total_302, \
self.total_304,self.total_503 = 0,0,0,0,0,0,0,0,0
def split_eachline_todict(self,line):
'''分割檔案中的每一行,並返回一個字典'''
split_line = line.split()
split_dict = {'remote_host':split_line[0],'status':split_line[8],'bytes_sent':split_line[9],}
return split_dict
def generate_log_report(self,logfile):
'''讀取檔案,分析split_eachline_todict方法生成的字典'''
#遍歷這個日誌檔案,出現錯誤的行(如:空行或split拆分時出現錯誤的行),跳過繼續
for line in logfile:
try:
line_dict = self.split_eachline_todict(line)
host = line_dict['remote_host']
status = line_dict['status']
except ValueError :
continue
except IndexError :
continue
#如果這個主機(鍵)在report_dict不存在,就初始化一個該主機的物件,然後把該物件賦值給該鍵(該主機)
#如果存在,那麼就取出該主機的值(也就是主機物件)
if host not in self.report_dict :
host_info_obj = hostInfo(host)
self.report_dict[host] = host_info_obj
else :
host_info_obj = self.report_dict[host]
host_info_obj.increment('times',False) #主機的請求次數進行加1
if status in host_info_obj.host_info : #如果得到的狀態在事先定義好的列表裡面
host_info_obj.increment(status,False) #主機的狀態數進行加1
try:
bytes_sent = int(line_dict['bytes_sent']) #轉換位元組為int型別
except ValueError:
bytes_sent = 0
host_info_obj.increment(bytes_sent,True) #主機的訪問的位元組數
return self.report_dict
def return_sorted_list(self,true_dict):
'''計算各個狀態次數、流量總量,請求的總次數,並且計算各個狀態的總量 並生成一個正真的字典,方便排序'''
for host_key in true_dict :
host_value = true_dict[host_key]
times = host_value.get_value('times') #每個IP地址請求的次數
self.total_request_times = self.total_request_times + times #各個請求次數之和
size = host_value.get_value('size') #每個IP地址請求的位元組數
self.total_traffic = self.total_traffic + size #各個IP請求位元組數之和
o200 = host_value.get_value('200')
o404 = host_value.get_value('404')
o500 = host_value.get_value('500')
o403 = host_value.get_value('403')
o302 = host_value.get_value('302')
o304 = host_value.get_value('304')
o503 = host_value.get_value('503')
#生成一個真正的字典方便排序,這之前主機對應的值其實是一個物件
true_dict[host_key] = {'200':o200,'404':o404,'500':o500,
'403':o403,'302':o302,'304':o304,
'503':o503,'times':times,'size':size}
#計算各個狀態總量
self.total_200 = self.total_200 + o200
self.total_404 = self.total_404 + o404
self.total_500 = self.total_500 + o500
self.total_302 = self.total_302 + o302
self.total_304 = self.total_304 + o304
self.total_503 = self.total_503 + o503
#生成的真正的字典按照先請求次數排序,然後再安裝請求的位元組數進行排序
sorted_list = sorted(true_dict.items(),key=lambda t:(t[1]['times'],
t[1]['size']),reverse=True)
return sorted_list
class Main(object):
def main(self) :
'''主調函式'''
#初始化一個displayFormat類的例項,列印報告的行頭
display_format = displayFormat()
#判斷命令列引數是否為空及判斷命令列傳進來的檔案是否是有效的檔案
arg_length = len(sys.argv)
if arg_length == 1 :
display_format.error_print()
elif arg_length == 2 or arg_length == 3:
infile_name = sys.argv[1]
try :
infile = open(infile_name,'r')
if arg_length == 3 :
lines = int(sys.argv[2])
else :
lines = 0
except IOError as e :
print()
print(e)
display_format.error_print()
except ValueError :
print()
print("Please Enter A Volid Number !!")
display_format.error_print()
else :
display_format.error_print()
#例項化一個fileAnalysis類的物件
fileAnalysis_obj = fileAnalysis()
#呼叫generate_log_report方法生成字典
not_true_dict = fileAnalysis_obj.generate_log_report(infile)
#對上面生成的字典進行排序,生成一個有序列表
log_report = fileAnalysis_obj.return_sorted_list(not_true_dict)
total_ip = len(log_report)
if lines :
log_report = log_report[0:lines]
infile.close()
#列印報告頭及橫線
total_traffic = display_format.format_size(fileAnalysis_obj.total_traffic)
total_request_times = fileAnalysis_obj.total_request_times
print('Total IP: %s Total Traffic: %s Total Request Times: %d')% (total_ip,total_traffic,total_request_times)
display_format.head()
display_format.transverse_line()
#迴圈這個列表,列印主機的各個值
for host in log_report :
times = host[1]['times']
times_percent = (float(times) / float(fileAnalysis_obj.total_request_times)) * 100
print(display_format.formatstring % (host[0],
display_format.format_size(host[1]['size']),
times,str(times_percent)[0:5],
host[1]['200'],host[1]['404'],
host[1]['500'],host[1]['403'],
host[1]['302'],host[1]['304'],host[1]['503']))
#列印報告的尾部及列印各個引數之後
if (not lines) or total_ip == lines :
display_format.transverse_line()
print(display_format.formatstring % (total_ip,total_traffic,
total_request_times,'100%',
fileAnalysis_obj.total_200,
fileAnalysis_obj.total_404,
fileAnalysis_obj.total_500,
fileAnalysis_obj.total_403,
fileAnalysis_obj.total_302,
fileAnalysis_obj.total_304,
fileAnalysis_obj.total_503))
#輸出指令碼執行的時間
display_format.execut_time()
if __name__ == '__main__':
main_obj = Main()
main_obj.main()
三、執行指令碼效果
執行:python nginx_log_analysis.py nginx日誌檔案
備註:執行指令碼的時候,必須將日誌檔案的名稱傳進去