531a8eec2a7d61ef85efa2d79a9829ef1667d533
[apachelog.git] / apachelog-process.py
1 #!/usr/bin/env python
2
3 """Process Apache (or similarly formated) log files using ``apachelog``.
4
5 Use the options to build a parser and list of processors.  Each file
6 listed on the command line will be parsed by this parser and processed
7 by each processor.  After processing is complete, interesting
8 information from each processor will be printed to stdout.
9 """
10
11 import socket as _socket
12
13 from apachelog import __version__
14 from apachelog.file import open as _open
15 from apachelog.parser import FORMATS as _FORMATS
16 from apachelog.parser import Parser as _Parser
17 from apachelog.processor import process as _process
18 from apachelog.processor.bandwidth import (
19     BandwidthProcessor as _BandwidthProcessor)
20 from apachelog.processor.bandwidth import (
21     IPBandwidthProcessor as _IPBandwidthProcessor)
22 from apachelog.processor.set import SetProcessor as _SetProcessor
23 from apachelog.processor.status import StatusProcessor as _StatusProcessor
24 from apachelog.resolve import Resolver as _Resolver
25
26
27 PROCESSORS = {
28     'bandwidth': _BandwidthProcessor,
29     'ip-bandwidth': _IPBandwidthProcessor,
30     'set': _SetProcessor,
31     'status': _StatusProcessor,
32     }
33
34
35 def display_processor(processor, **kwargs):
36     for name,type_ in PROCESSORS.items():
37         if type(processor) == type_:
38             pname = name.replace('-', '_')
39             display = globals()['display_{}'.format(pname)]
40             return display(processor=processor, **kwargs)
41
42 def display_bandwidth(stream, processor, args, **kwargs):
43     scale = args.scale
44     stream.write('# IP bandwidth ({})\n'.format(scale))
45     stream.write('{}\n'.format(processor.bandwidth(scale=scale)))
46
47 def display_ip_bandwidth(stream, processor, resolver, args):
48     scale = args.scale
49     top = args.top
50     stream.write('# IP bandwidth ({})\n'.format(scale))
51     if resolver is not None:
52         processor.resolve(resolver=resolver, top=top)
53     remaining = processor.bandwidth(scale=scale)
54     for ip,bw in processor.ip_bandwidth(
55         scale=scale, sort_by_bandwidth=True)[-1:-top:-1]:
56         remaining -= bw
57         stream.write('\t'.join([str(bw), ip]))
58         if resolver is not None:  # also print the raw IPs
59             ips = resolver.ips(ip)
60             try:
61                 ips.remove(ip)
62             except KeyError:
63                 pass
64             stream.write('\t{}'.format(' '.join(sorted(ips))))
65         stream.write('\n')
66     stream.write('\t'.join([str(remaining), 'REMAINING']))
67     stream.write('\n')
68
69 def display_set(stream, processor, **kwargs):
70     stream.write('# Value sets\n')
71     for key,values in sorted(processor.values.items()):
72         stream.write('{}\n'.format(key))
73         for value in sorted(values):
74             stream.write('\t{}\n'.format(value))
75
76 def display_status(stream, processor, **kwargs):
77     stream.write('# Status\n')
78     for request,status in sorted(processor.request.items()):
79         stream.write('\t'.join([request, ', '.join(sorted(status))]))
80         stream.write('\n')
81     for status,request in sorted(processor.status.items()):
82         stream.write('{}\n'.format(status))
83         for r in sorted(request):
84             stream.write('\t{}\n'.format(r))
85
86
87 if __name__ == '__main__':
88     import argparse
89     import sys
90
91     parser = argparse.ArgumentParser(description=__doc__, version=__version__)
92     parser.add_argument(
93         '-f', '--format', default='common',
94         help='Log format string, or one of the predefined formats: {}'.format(
95             ', '.join(sorted(_FORMATS.keys()))))
96     for processor in sorted(PROCESSORS.keys()):
97         parser.add_argument(
98             '--{}'.format(processor), default=False, action='store_const',
99             const=True,
100             help='Use the {} processor'.format(processor))
101     parser.add_argument(
102         '-r', '--resolve', default=False, action='store_const', const=True,
103         help='Resolve IP addresses for bandwidth measurements')
104     parser.add_argument(
105         '-t', '--top', default=10, type=int,
106         help='Number of IPs to print for ip-bandwidth measurements')
107     parser.add_argument(
108         '-s', '--scale', default='MB/month',
109         choices=sorted(_BandwidthProcessor._scales.keys()),
110         help='Scale for the bandwidth processors')
111     parser.add_argument(
112         '-k', '--key', action='append', help='Add a key to the set processor')
113     parser.add_argument(
114         'file', nargs='+', help='Path to log file')
115
116     args = parser.parse_args()
117
118     if hasattr(_socket, 'setdefaulttimeout'):
119         _socket.setdefaulttimeout(5)  # set 5 second timeout
120
121     fmt = _FORMATS.get(args.format, args.format)
122     parser = _Parser(fmt)
123
124     if args.resolve:
125         resolver = _Resolver(smart=True)
126     else:
127         resolver = None
128
129     processors = []
130     for processor in sorted(PROCESSORS.keys()):
131         pattr = processor.replace('-', '_')
132         if not getattr(args, pattr):
133             continue
134         kwargs = {}
135         if pattr == 'set':
136             kwargs['keys'] = args.key
137         p = PROCESSORS[processor](**kwargs)
138         processors.append(p)
139
140     for filename in args.file:
141         with _open(filename) as f:
142             _process(stream=f, parser=parser, processors=processors)
143     for processor in processors:
144         display_processor(
145             stream=sys.stdout, processor=processor, resolver=resolver,
146             args=args)
147         if processor != processors[-1]:
148             print ''  # blank line between output blocks