Avoid duplicate time parsings when multiple LogTimeProcessors are used.
[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.processor.time import LogTimeProcessor as _LogTimeProcessor
25 from apachelog.resolve import Resolver as _Resolver
26
27
28 PROCESSORS = {
29     'bandwidth': _BandwidthProcessor,
30     'ip-bandwidth': _IPBandwidthProcessor,
31     'set': _SetProcessor,
32     'status': _StatusProcessor,
33     }
34
35
36 def display_processor(processor, **kwargs):
37     for name,type_ in PROCESSORS.items():
38         if type(processor) == type_:
39             pname = name.replace('-', '_')
40             display = globals()['display_{}'.format(pname)]
41             return display(processor=processor, **kwargs)
42
43 def display_bandwidth(stream, processor, args, **kwargs):
44     scale = args.scale
45     stream.write('# IP bandwidth ({})\n'.format(scale))
46     stream.write('{}\n'.format(processor.bandwidth(scale=scale)))
47
48 def display_ip_bandwidth(stream, processor, resolver, args):
49     scale = args.scale
50     top = args.top
51     stream.write('# IP bandwidth ({})\n'.format(scale))
52     if resolver is not None:
53         processor.resolve(resolver=resolver, top=top)
54     remaining = processor.bandwidth(scale=scale)
55     for ip,bw in processor.ip_bandwidth(
56         scale=scale, sort_by_bandwidth=True)[-1:-top:-1]:
57         remaining -= bw
58         stream.write('\t'.join([str(bw), ip]))
59         if resolver is not None:  # also print the raw IPs
60             ips = resolver.ips(ip)
61             try:
62                 ips.remove(ip)
63             except KeyError:
64                 pass
65             stream.write('\t{}'.format(' '.join(sorted(ips))))
66         stream.write('\n')
67     stream.write('\t'.join([str(remaining), 'REMAINING']))
68     stream.write('\n')
69
70 def display_set(stream, processor, **kwargs):
71     stream.write('# Value sets\n')
72     for key,values in sorted(processor.values.items()):
73         stream.write('{}\n'.format(key))
74         for value in sorted(values):
75             stream.write('\t{}\n'.format(value))
76
77 def display_status(stream, processor, **kwargs):
78     stream.write('# Status\n')
79     for request,status in sorted(processor.request.items()):
80         stream.write('\t'.join([request, ', '.join(sorted(status))]))
81         stream.write('\n')
82     for status,request in sorted(processor.status.items()):
83         stream.write('{}\n'.format(status))
84         for r in sorted(request):
85             stream.write('\t{}\n'.format(r))
86
87
88 if __name__ == '__main__':
89     import argparse
90     import sys
91
92     parser = argparse.ArgumentParser(description=__doc__, version=__version__)
93     parser.add_argument(
94         '-f', '--format', default='common',
95         help='Log format string, or one of the predefined formats: {}'.format(
96             ', '.join(sorted(_FORMATS.keys()))))
97     for processor in sorted(PROCESSORS.keys()):
98         parser.add_argument(
99             '--{}'.format(processor), default=False, action='store_const',
100             const=True,
101             help='Use the {} processor'.format(processor))
102     parser.add_argument(
103         '-r', '--resolve', default=False, action='store_const', const=True,
104         help='Resolve IP addresses for bandwidth measurements')
105     parser.add_argument(
106         '-t', '--top', default=10, type=int,
107         help='Number of IPs to print for ip-bandwidth measurements')
108     parser.add_argument(
109         '-s', '--scale', default='MB/month',
110         choices=sorted(_BandwidthProcessor._scales.keys()),
111         help='Scale for the bandwidth processors')
112     parser.add_argument(
113         '-k', '--key', action='append', help='Add a key to the set processor')
114     parser.add_argument(
115         'file', nargs='+', help='Path to log file')
116
117     args = parser.parse_args()
118
119     if hasattr(_socket, 'setdefaulttimeout'):
120         _socket.setdefaulttimeout(5)  # set 5 second timeout
121
122     fmt = _FORMATS.get(args.format, args.format)
123     parser = _Parser(fmt)
124
125     if args.resolve:
126         resolver = _Resolver(smart=True)
127     else:
128         resolver = None
129
130     processors = []
131     log_time_processor = None
132     for processor in sorted(PROCESSORS.keys()):
133         pattr = processor.replace('-', '_')
134         if not getattr(args, pattr):
135             continue
136         kwargs = {}
137         if pattr == 'set':
138             kwargs['keys'] = args.key
139         if (log_time_processor is not None and
140             issubclass(PROCESSORS[processor], _LogTimeProcessor)):
141             kwargs['previous_log_time_processor'] = log_time_processor
142         p = PROCESSORS[processor](**kwargs)
143         if log_time_processor is None and isinstance(p, _LogTimeProcessor):
144             log_time_processor = p
145         processors.append(p)
146
147     for filename in args.file:
148         with _open(filename) as f:
149             _process(stream=f, parser=parser, processors=processors)
150     for processor in processors:
151         display_processor(
152             stream=sys.stdout, processor=processor, resolver=resolver,
153             args=args)
154         if processor != processors[-1]:
155             print ''  # blank line between output blocks