1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
""" Outils de parsing des fichiers de logs """
|
13
|
from twisted.python import log
|
14
|
from ead2.backend.actions.lib.logparser.config import LOG_DIR, TEMP_FILENAME, LOGPARSER_DIR
|
15
|
from ead2.backend.actions.lib.logparser import file_listing
|
16
|
from os.path import join, isdir, splitext
|
17
|
import commands
|
18
|
from os import unlink
|
19
|
from time import ctime, strptime, mktime, strftime, localtime
|
20
|
import locale
|
21
|
|
22
|
|
23
|
def get_logs(filenames=[], ip=None, url=None, login=None, hours=(None, None), denied=None):
|
24
|
""" renvoie les logs selon les critères fournis
|
25
|
@filenmames: noms des fichiers à traiter
|
26
|
@ip: ip de l'utilisateur
|
27
|
@url: url visitée
|
28
|
@login: login de l'utilisateur
|
29
|
@hours: tranche horaire
|
30
|
@denied: logs interdits?
|
31
|
"""
|
32
|
if type(filenames) != list:
|
33
|
filenames = [filenames]
|
34
|
if hours == (None, None):
|
35
|
return _get_filtered_logs(filenames, ip=ip, url=url, login=login, denied=denied)
|
36
|
if None in hours:
|
37
|
raise "Erreur : Il manque l'heure de début ou de fin."
|
38
|
result = []
|
39
|
beg, end = hours
|
40
|
for filename in filenames:
|
41
|
date = _get_file_date(filename)
|
42
|
if date:
|
43
|
timestamps = _construct_datetime_range(date, beg, end)
|
44
|
parsed_logs = _get_filtered_log(filename, ip=ip, url=url,
|
45
|
login=login, date=timestamps, denied=denied)
|
46
|
result.extend(parsed_logs)
|
47
|
return result
|
48
|
|
49
|
|
50
|
def _get_filtered_logs(filenames=[], url=None, ip=None, login=None, denied=None):
|
51
|
""" renvoie les logs filtrés pour les fichiers filenames """
|
52
|
if filenames == []:
|
53
|
raise Exception, "Précisez les jours de la recherche logparsing.py"
|
54
|
result = []
|
55
|
for filename in filenames:
|
56
|
result.extend(_get_filtered_log(filename, url=url, ip=ip, login=login,
|
57
|
denied=denied))
|
58
|
return result
|
59
|
|
60
|
|
61
|
|
62
|
def _get_filtered_log(filename, url=None, ip=None, login=None, date=None, denied=None):
|
63
|
""" renvoie les logs filtrés selon les parametres envoyés """
|
64
|
filepath = join(LOG_DIR, filename)
|
65
|
cmd = ''
|
66
|
if splitext(filename)[1] == '.gz':
|
67
|
filetype = "gz"
|
68
|
act_grep = 'zgrep'
|
69
|
act_cat = 'zcat'
|
70
|
else:
|
71
|
filetype = "plain"
|
72
|
act_grep = 'grep'
|
73
|
act_cat = 'cat'
|
74
|
|
75
|
|
76
|
|
77
|
grep = act_grep + ' \'%s\' ' + filepath
|
78
|
othergrep = ' | grep \'%s\' '
|
79
|
if url is not None:
|
80
|
cmd += grep % url
|
81
|
grep = othergrep
|
82
|
if ip is not None:
|
83
|
|
84
|
|
85
|
ip = '%s' % ip
|
86
|
cmd += grep % ip
|
87
|
grep = othergrep
|
88
|
if login is not None:
|
89
|
|
90
|
|
91
|
esc_login = '%s' % login
|
92
|
cmd += grep % esc_login
|
93
|
grep = othergrep
|
94
|
if denied is not None:
|
95
|
cmd += grep % 'DENIED'
|
96
|
if date is not None:
|
97
|
if cmd == '':
|
98
|
if filetype == "gz" :
|
99
|
cmd = 'gunzip -c ' + filepath + '| awk \''+str(date[0])+'<\$1 && '+str(date[1])+'>\$1 { print \$N }\' '
|
100
|
else:
|
101
|
cmd = 'awk \''+str(date[0])+'<\$1 && '+str(date[1])+'>\$1 { print \$N }\' ' + filepath
|
102
|
else:
|
103
|
cmd += '| awk \''+str(date[0])+'<\$1 && '+str(date[1])+'>\$1 { print \$N }\' '
|
104
|
if cmd != '':
|
105
|
parsersh = join(LOGPARSER_DIR, 'parser.sh')
|
106
|
cmd = "%s \"%s\" \"%s\"" % (parsersh, cmd, TEMP_FILENAME)
|
107
|
else:
|
108
|
|
109
|
|
110
|
|
111
|
date_range = _construct_datetime_range(_get_file_date(filename), 0, 24)
|
112
|
if filetype == "gz":
|
113
|
cmd = 'gunzip -c ' + filepath + '| awk \''+str(date_range[0])+'<$1 && '+str(date_range[1])+'>$1 { print $N }\' >' + TEMP_FILENAME
|
114
|
else:
|
115
|
cmd = 'awk \''+str(date_range[0])+'<$1 && '+str(date_range[1])+'>$1 { print $N }\' ' + filepath + '>' + TEMP_FILENAME
|
116
|
|
117
|
return_code, return_value = commands.getstatusoutput(cmd)
|
118
|
if return_code % 256:
|
119
|
log.err("Erreur: logparsing.py %s" % return_value)
|
120
|
raise Exception, "Erreur : le parsing en ligne de commande a échoué"
|
121
|
grepped_datas = file(TEMP_FILENAME).read().splitlines()
|
122
|
unlink(TEMP_FILENAME)
|
123
|
parsed_datas = list(_parse_log_lines(grepped_datas))
|
124
|
p_datas = []
|
125
|
for line in parsed_datas:
|
126
|
if line not in p_datas:
|
127
|
p_datas.append(line)
|
128
|
if login is not None:
|
129
|
parsed_datas = _filter_login(p_datas, login)
|
130
|
return p_datas
|
131
|
|
132
|
|
133
|
|
134
|
|
135
|
|
136
|
|
137
|
|
138
|
|
139
|
|
140
|
|
141
|
|
142
|
|
143
|
|
144
|
|
145
|
|
146
|
def _parse_log_lines(datas):
|
147
|
""" renvoie les logs sous forme de liste de dico """
|
148
|
|
149
|
for line in datas:
|
150
|
if len(line.split())>3:
|
151
|
log = {'login':line.split()[7],
|
152
|
'date':ctime(float(line.split()[0])),
|
153
|
'url':_get_domain(line.split()[6]),
|
154
|
'ip':line.split()[2],
|
155
|
'longurl':line.split()[6]}
|
156
|
if 'DENIED' in line:
|
157
|
log['denied'] = 'True'
|
158
|
|
159
|
log['date'] = strftime("%A %d %B %Y %X", localtime(float(line.split()[0])))
|
160
|
yield log
|
161
|
|
162
|
|
163
|
|
164
|
def _get_domain(url):
|
165
|
""" renvoie le nom de domaine d'une url choisie """
|
166
|
return url.split('/')[2]
|
167
|
|
168
|
def _replace_cars(url):
|
169
|
""" remplacement de caractères indésirables """
|
170
|
return url.replace('%', '%')
|
171
|
|
172
|
|
173
|
def _filter_login(datas, login):
|
174
|
""" renvoie les logs de 'login' (filtre les urls contenant 'login) """
|
175
|
return [a for a in datas if a['login'] == login]
|
176
|
|
177
|
|
178
|
def _construct_datetime_range(date, beg, end):
|
179
|
""" renvoie la liste des chaines "date heure:"
|
180
|
qui correspondent à la beach hour (plage horaire) demandée
|
181
|
"""
|
182
|
try:
|
183
|
beg = int(beg)
|
184
|
end = int(end)
|
185
|
except:
|
186
|
raise Exception, "Erreur : Le début ou la fin de la plage horaire demandée ne sont pas des entiers."
|
187
|
if beg >= end:
|
188
|
raise Exception, "Erreur : Le début de la plage horaire demandée est supérieure ou égale à la fin."
|
189
|
|
190
|
tmp = ctime(float(date)).split()
|
191
|
tmp[3] = '00:00:00'
|
192
|
start_timestamp = mktime(strptime((' '.join(tmp))))
|
193
|
plage = [float(start_timestamp)+float(beg*3600), float(start_timestamp)+float(end*3600)]
|
194
|
return plage
|
195
|
|
196
|
def _get_file_date(filename):
|
197
|
""" renvoie la date d'un fichier de log en
|
198
|
parsant la première ligne
|
199
|
"""
|
200
|
filepath = join(LOG_DIR, filename)
|
201
|
if splitext(filename)[1] == '.gz':
|
202
|
cmd = "zgrep -m1 . %s" % filepath
|
203
|
else:
|
204
|
cmd = "grep -m1 . %s" % filepath
|
205
|
return_code, first_file_line = commands.getstatusoutput(cmd)
|
206
|
if return_code % 256:
|
207
|
log.err("Erreur : logparsing.py %s pour la commande : %s, code retour: %s" % (first_file_line, cmd, return_code))
|
208
|
raise Exception, "Une erreur s'est produite au parsing de %s : %s" % (filepath, first_file_line)
|
209
|
if first_file_line:
|
210
|
date = first_file_line.split()[0]
|
211
|
return date
|
212
|
else:
|
213
|
raise Exception, "Une erreur s'est produite au parsing de %s : %s le fichier semble vide" % (filepath, first_file_line)
|
214
|
|
215
|
def reformate_date(date):
|
216
|
""" renvoie une date à un nouveau format: (jour,mois,année)
|
217
|
depuis une date au format : '"année:mois:jour' """
|
218
|
elts = date.split()
|
219
|
year = elts[4]
|
220
|
month = elts[1]
|
221
|
day = elts[0]
|
222
|
return day, month, year
|