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()
|
---|