blob: 9545fe0b1c76a194078b3c0178092d78fadc7114 [file] [log] [blame]
Paul Hobbsef4e0702016-06-27 17:01:42 -07001#!/usr/bin/python2
2
3# Copyright 2016 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Script to upload metrics from apache logs to Monarch.
8
9We are interested in static file bandwidth, so it parses out GET requests to
10/static and uploads the sizes to a cumulative metric.
11"""
12from __future__ import print_function
13
14import argparse
Paul Hobbsef4e0702016-06-27 17:01:42 -070015import re
16import sys
17
18from devserver import MakeLogHandler
19
20from chromite.lib import ts_mon_config
21from chromite.lib import metrics
Paul Hobbs338baee2016-07-13 13:42:34 -070022from chromite.lib import cros_logging as logging
Paul Hobbsef4e0702016-06-27 17:01:42 -070023from infra_libs import ts_mon
24
25
26STATIC_GET_MATCHER = re.compile(
27 r'^(?P<ip_addr>\d+\.\d+\.\d+\.\d+) '
28 r'.*GET /static/\S*[^"]*" '
29 r'200 (?P<size>\S+) .*')
30
31STATIC_GET_METRIC_NAME = 'chromeos/devserver/apache/static_response_size'
32
33
34LAB_SUBNETS = (
35 ("172.17.40.0", 22),
36 ("100.107.160.0", 19),
37 ("100.115.128.0", 17),
38 ("100.115.254.126", 25),
39 ("100.107.141.128", 25),
40 ("172.27.212.0", 22),
41 ("100.107.156.192", 26),
42 ("172.22.29.0", 25),
43 ("172.22.38.0", 23),
44 ("100.107.224.0", 23),
45 ("100.107.226.0", 25),
46 ("100.107.126.0", 25),
47)
48
49def IPToNum(ip):
50 return reduce(lambda seed, x: seed * 2**8 + int(x), ip.split('.'), 0)
51
52
53def MatchesSubnet(ip, base, mask):
54 ip_value = IPToNum(ip)
55 base_value = IPToNum(base)
56 mask = (2**mask - 1) << (32 - mask)
57 return (ip_value & mask) == (base_value & mask)
58
59
60def InLab(ip):
61 return any(MatchesSubnet(ip, base, mask)
62 for (base, mask) in LAB_SUBNETS)
63
64
65def EmitStaticRequestMetric(m):
66 """Emits a Counter metric for sucessful GETs to /static endpoints."""
67 ipaddr, size = m.groups()
68 try:
69 size = int(size)
70 except ValueError: # Zero is represented by "-"
71 size = 0
72
73 metrics.Counter(STATIC_GET_METRIC_NAME).increment_by(
74 size, fields={
75 'builder': '',
76 'in_lab': InLab(ipaddr),
77 'endpoint': ''})
78
79
80def RunMatchers(stream, matchers):
81 """Parses lines of |stream| using patterns and emitters from |matchers|"""
Paul Hobbs338baee2016-07-13 13:42:34 -070082 for line in iter(stream.readline, ''):
Paul Hobbsef4e0702016-06-27 17:01:42 -070083 for matcher, emitter in matchers:
Paul Hobbs338baee2016-07-13 13:42:34 -070084 logging.debug('Emitting %s for input "%s"',
85 emitter.__name__, line.strip())
Paul Hobbsef4e0702016-06-27 17:01:42 -070086 m = matcher.match(line)
87 if m:
88 emitter(m)
89 # The input might terminate if the log gets rotated. Make sure that Monarch
90 # flushes any pending metrics before quitting.
91 ts_mon.close()
92
93
94# TODO(phobbs) add a matcher for all requests, not just static files.
95MATCHERS = [
96 (STATIC_GET_MATCHER, EmitStaticRequestMetric),
97]
98
99
100def ParseArgs():
101 """Parses command line arguments."""
102 p = argparse.ArgumentParser(
103 description='Parses apache logs and emits metrics to Monarch')
104 p.add_argument('--logfile')
105 return p.parse_args()
106
107
108def main():
109 """Sets up logging and runs matchers against stdin"""
110 args = ParseArgs()
111 root = logging.getLogger()
112 root.addHandler(MakeLogHandler(args.logfile))
113 root.setLevel(logging.DEBUG)
114 ts_mon_config.SetupTsMonGlobalState('devserver_apache_log_metrics')
115 RunMatchers(sys.stdin, MATCHERS)
116
117
118if __name__ == '__main__':
119 main()