|
1 #! /usr/bin/env python |
|
2 |
|
3 # Extract statistics from ftp daemon log. |
|
4 |
|
5 # Usage: |
|
6 # ftpstats [-m maxitems] [-s search] [file] |
|
7 # -m maxitems: restrict number of items in "top-N" lists, default 25. |
|
8 # -s string: restrict statistics to lines containing this string. |
|
9 # Default file is /usr/adm/ftpd; a "-" means read standard input. |
|
10 |
|
11 # The script must be run on the host where the ftp daemon runs. |
|
12 # (At CWI this is currently buizerd.) |
|
13 |
|
14 import os |
|
15 import sys |
|
16 import re |
|
17 import string |
|
18 import getopt |
|
19 |
|
20 pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$' |
|
21 prog = re.compile(pat) |
|
22 |
|
23 def main(): |
|
24 maxitems = 25 |
|
25 search = None |
|
26 try: |
|
27 opts, args = getopt.getopt(sys.argv[1:], 'm:s:') |
|
28 except getopt.error, msg: |
|
29 print msg |
|
30 print 'usage: ftpstats [-m maxitems] [file]' |
|
31 sys.exit(2) |
|
32 for o, a in opts: |
|
33 if o == '-m': |
|
34 maxitems = string.atoi(a) |
|
35 if o == '-s': |
|
36 search = a |
|
37 file = '/usr/adm/ftpd' |
|
38 if args: file = args[0] |
|
39 if file == '-': |
|
40 f = sys.stdin |
|
41 else: |
|
42 try: |
|
43 f = open(file, 'r') |
|
44 except IOError, msg: |
|
45 print file, ':', msg |
|
46 sys.exit(1) |
|
47 bydate = {} |
|
48 bytime = {} |
|
49 byfile = {} |
|
50 bydir = {} |
|
51 byhost = {} |
|
52 byuser = {} |
|
53 bytype = {} |
|
54 lineno = 0 |
|
55 try: |
|
56 while 1: |
|
57 line = f.readline() |
|
58 if not line: break |
|
59 lineno = lineno + 1 |
|
60 if search and string.find(line, search) < 0: |
|
61 continue |
|
62 if prog.match(line) < 0: |
|
63 print 'Bad line', lineno, ':', repr(line) |
|
64 continue |
|
65 items = prog.group(1, 2, 3, 4, 5, 6) |
|
66 (logtime, loguser, loghost, logfile, logbytes, |
|
67 logxxx2) = items |
|
68 ## print logtime |
|
69 ## print '-->', loguser |
|
70 ## print '--> -->', loghost |
|
71 ## print '--> --> -->', logfile |
|
72 ## print '--> --> --> -->', logbytes |
|
73 ## print '--> --> --> --> -->', logxxx2 |
|
74 ## for i in logtime, loghost, logbytes, logxxx2: |
|
75 ## if '!' in i: print '???', i |
|
76 add(bydate, logtime[-4:] + ' ' + logtime[:6], items) |
|
77 add(bytime, logtime[7:9] + ':00-59', items) |
|
78 direction, logfile = logfile[0], logfile[1:] |
|
79 # The real path probably starts at the last //... |
|
80 while 1: |
|
81 i = string.find(logfile, '//') |
|
82 if i < 0: break |
|
83 logfile = logfile[i+1:] |
|
84 add(byfile, logfile + ' ' + direction, items) |
|
85 logdir = os.path.dirname(logfile) |
|
86 ## logdir = os.path.normpath(logdir) + '/.' |
|
87 while 1: |
|
88 add(bydir, logdir + ' ' + direction, items) |
|
89 dirhead = os.path.dirname(logdir) |
|
90 if dirhead == logdir: break |
|
91 logdir = dirhead |
|
92 add(byhost, loghost, items) |
|
93 add(byuser, loguser, items) |
|
94 add(bytype, direction, items) |
|
95 except KeyboardInterrupt: |
|
96 print 'Interrupted at line', lineno |
|
97 show(bytype, 'by transfer direction', maxitems) |
|
98 show(bydir, 'by directory', maxitems) |
|
99 show(byfile, 'by file', maxitems) |
|
100 show(byhost, 'by host', maxitems) |
|
101 show(byuser, 'by user', maxitems) |
|
102 showbar(bydate, 'by date') |
|
103 showbar(bytime, 'by time of day') |
|
104 |
|
105 def showbar(dict, title): |
|
106 n = len(title) |
|
107 print '='*((70-n)//2), title, '='*((71-n)//2) |
|
108 list = [] |
|
109 keys = dict.keys() |
|
110 keys.sort() |
|
111 for key in keys: |
|
112 n = len(str(key)) |
|
113 list.append((len(dict[key]), key)) |
|
114 maxkeylength = 0 |
|
115 maxcount = 0 |
|
116 for count, key in list: |
|
117 maxkeylength = max(maxkeylength, len(key)) |
|
118 maxcount = max(maxcount, count) |
|
119 maxbarlength = 72 - maxkeylength - 7 |
|
120 for count, key in list: |
|
121 barlength = int(round(maxbarlength*float(count)/maxcount)) |
|
122 bar = '*'*barlength |
|
123 print '%5d %-*s %s' % (count, maxkeylength, key, bar) |
|
124 |
|
125 def show(dict, title, maxitems): |
|
126 if len(dict) > maxitems: |
|
127 title = title + ' (first %d)'%maxitems |
|
128 n = len(title) |
|
129 print '='*((70-n)//2), title, '='*((71-n)//2) |
|
130 list = [] |
|
131 keys = dict.keys() |
|
132 for key in keys: |
|
133 list.append((-len(dict[key]), key)) |
|
134 list.sort() |
|
135 for count, key in list[:maxitems]: |
|
136 print '%5d %s' % (-count, key) |
|
137 |
|
138 def add(dict, key, item): |
|
139 if dict.has_key(key): |
|
140 dict[key].append(item) |
|
141 else: |
|
142 dict[key] = [item] |
|
143 |
|
144 if __name__ == "__main__": |
|
145 main() |