Je viens de commencer à apprendre Python et je voudrais lire un fichier journal Apache et mettre des parties de chaque ligne dans différentes listes.
ligne du fichier
172.16.0.3 - - [25/Sep/2002: 14: 04: 19 +0200] "GET/HTTP/1.1" 401 - "" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv: 1.1 ) Gecko/20020827 "
selon site Web Apache le format est
% h% l% u% t\"% r \"%> s% b\"% {Referer} i \"\"% {User-Agent} i \
Je peux ouvrir le fichier et le lire tel quel, mais je ne sais pas comment le lire dans ce format afin de pouvoir mettre chaque partie dans une liste.
Ceci est un travail pour expressions régulières .
Par exemple:
line = '172.16.0.3 - - [25/Sep/2002:14:04:19 +0200] "GET / HTTP/1.1" 401 - "" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827"'
regex = '([(\d\.)]+) - - \[(.*?)\] "(.*?)" (\d+) - "(.*?)" "(.*?)"'
import re
print re.match(regex, line).groups()
La sortie serait un Tuple avec 6 éléments d'information de la ligne (en particulier, les groupes entre parenthèses dans ce modèle):
('172.16.0.3', '25/Sep/2002:14:04:19 +0200', 'GET / HTTP/1.1', '401', '', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827')
Utilisez une expression régulière pour diviser une ligne en "jetons" distincts:
>>> row = """172.16.0.3 - - [25/Sep/2002:14:04:19 +0200] "GET / HTTP/1.1" 401 - "" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827" """
>>> import re
>>> map(''.join, re.findall(r'\"(.*?)\"|\[(.*?)\]|(\S+)', row))
['172.16.0.3', '-', '-', '25/Sep/2002:14:04:19 +0200', 'GET / HTTP/1.1', '401', '-', '', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.1) Gecko/20020827']
Une autre solution consiste à utiliser un outil dédié, par ex. http://pypi.python.org/pypi/pylogsparser/0.4
J'ai créé une bibliothèque python qui fait exactement cela: Apache-log-parser .
>>> import Apache_log_parser
>>> line_parser = Apache_log_parser.make_parser("%h <<%P>> %t %Dus \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %l %u")
>>> log_line_data = line_parser('127.0.0.1 <<6113>> [16/Aug/2013:15:45:34 +0000] 1966093us "GET / HTTP/1.1" 200 3478 "https://example.com/" "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.18)" - -')
>>> pprint(log_line_data)
{'pid': '6113',
'remote_Host': '127.0.0.1',
'remote_logname': '-',
'remote_user': '',
'request_first_line': 'GET / HTTP/1.1',
'request_header_referer': 'https://example.com/',
'request_header_user_agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.18)',
'response_bytes_clf': '3478',
'status': '200',
'time_received': '[16/Aug/2013:15:45:34 +0000]',
'time_us': '1966093'}
RegEx semblait extrême et problématique compte tenu de la simplicité du format, j'ai donc écrit ce petit séparateur que d'autres peuvent également trouver utile:
def Apache2_logrow(s):
''' Fast split on Apache2 log lines
http://httpd.Apache.org/docs/trunk/logs.html
'''
row = [ ]
qe = qp = None # quote end character (qe) and quote parts (qp)
for s in s.replace('\r','').replace('\n','').split(' '):
if qp:
qp.append(s)
Elif '' == s: # blanks
row.append('')
Elif '"' == s[0]: # begin " quote "
qp = [ s ]
qe = '"'
Elif '[' == s[0]: # begin [ quote ]
qp = [ s ]
qe = ']'
else:
row.append(s)
l = len(s)
if l and qe == s[-1]: # end quote
if l == 1 or s[-2] != '\\': # don't end on escaped quotes
row.append(' '.join(qp)[1:-1].replace('\\'+qe, qe))
qp = qe = None
return row
Ajoutez ceci dans httpd.conf pour convertir les journaux Apache en json.
LogFormat "{\"time\":\"%t\", \"remoteIP\" :\"%a\", \"Host\": \"%V\", \"request_id\": \"%L\", \"request\":\"%U\", \"query\" : \"%q\", \"method\":\"%m\", \"status\":\"%>s\", \"userAgent\":\"%{User-agent}i\", \"referer\":\"%{Referer}i\" }" json_log
CustomLog /var/log/Apache_access_log json_log
CustomLog "|/usr/bin/python -u apacheLogHandler.py" json_log
Vous voyez maintenant vos access_logs au format json. Utilisez le code ci-dessous python pour analyser les journaux json qui sont constamment mis à jour.
apacheLogHandler.py
import time
f = open('Apache_access_log.log', 'r')
for line in f: # read all lines already in the file
print line.strip()
# keep waiting forever for more lines.
while True:
line = f.readline() # just read more
if line: # if you got something...
print 'got data:', line.strip()
time.sleep(1)
import re
Host = r'^(?P<Host>.*?)'
SPACE = r'\s'
IDENTITY = r'\S+'
USER = r'\S+'
TIME = r'(?P<time>\[.*?\])'
REQUEST = r'\"(?P<request>.*?)\"'
STATUS = r'(?P<status>\d{3})'
SIZE = r'(?P<size>\S+)'
REGEX = Host+SPACE+IDENTITY+SPACE+USER+SPACE+TIME+SPACE+REQUEST+SPACE+STATUS+SPACE+SIZE+SPACE
def parser(log_line):
match = re.search(REGEX,log_line)
return ( (match.group('Host'),
match.group('time'),
match.group('request') ,
match.group('status') ,
match.group('size')
)
)
logLine = """180.76.15.30 - - [24/Mar/2017:19:37:57 +0000] "GET /shop/page/32/?count=15&orderby=title&add_to_wishlist=4846 HTTP/1.1" 404 10202 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"""
result = parser(logLine)
print(result)