Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(315)

Side by Side Diff: infra/tools/metric_tool/metric_tool.py

Issue 1368583005: Create metric_tool to deal with metric definitions (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Added tests, print undocumented metrics Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """Main implementation of metric_tool."""
5
6 import ast
7 import os
8 import logging
9
10
11 # https://chromium.googlesource.com/infra/infra/+/master/infra_libs/logs/README. md
12 LOGGER = logging.getLogger(__name__)
13
14 METRICS_NAMES = set(('StringMetric', 'BooleanMetric',
15 'CounterMetric', 'GaugeMetric', 'CumulativeMetric',
16 'FloatMetric', 'DistributionMetric',
Sergey Berezin 2015/09/29 00:01:18 nit: ideally, DistributionMetric shouldn't be used
pgervais 2015/09/30 00:09:37 Removed
17 'CumulativeDistributionMetric',
18 'NonCumulativeDistributionMetric'))
19
20
21 def extract_metrics_descriptions(filepath):
22 """Parse a python file and return all metrics descriptions it can find.
23
24 A metric description is the value of the 'description' keyword passed to a
25 metric definition (classes deriving from ts_mon.Metric)
26
27 Args:
28 filepath (str): path to a Python file.
29
30 Returns:
31 description (list of tuples): each tuple being
32 (metric name, description string). Metric name is 'DYNAMIC' it something
33 different than a static string is used in the metric creation.
34 If a metric is instanciated without a description, return None.
35 """
36 descriptions = []
37 try:
38 with open(filepath, 'r') as f:
39 content = f.read() # pragma: no branch
40 except IOError:
41 return descriptions
42
43 try:
44 root = ast.parse(content)
45 except SyntaxError: # just ignore invalid / python3 files.
46 return descriptions
47
48 for node in ast.walk(root):
49 if not isinstance(node, ast.Call):
50 continue
51
52 # Look for metrics definitions
53 calls = []
54 if isinstance(node.func, ast.Name):
55 if node.func.id in METRICS_NAMES:
56 LOGGER.debug('Method %s found line %d', node.func.id, node.func.lineno)
57 calls.append(node)
58 elif isinstance(node.func, ast.Attribute): # pragma: no branch
59 if node.func.attr in METRICS_NAMES:
60 LOGGER.debug('Method %s found line %d',
61 node.func.attr, node.func.lineno)
62 calls.append(node)
63
64 # Extract parameters from function call
65 for fcall in calls:
66 # Metric name
67 metric_name = 'DYNAMIC'
68 if fcall.args and isinstance(fcall.args[0], ast.Str):
69 metric_name = fcall.args[0].s
70
71 # Getting descriptions
72 description = None
73 for keyword in fcall.keywords:
74 if keyword.arg == 'description' and isinstance(keyword.value, ast.Str):
75 description = keyword.value.s
76
77 descriptions.append((filepath, fcall.lineno, metric_name, description))
78
79 return descriptions
80
81
82 def main(path):
83 """Recursively walk a directory structure and print metrics documentation.
84
85 For all instanciated metrics in any Python file found under the directory
86 passed as argument, if a 'description' keyword is provided print it
87 alongside the metric name.
88
89 Args:
90 path (str): directory.
91 """
92 documented = []
93 non_documented = []
94
95 for (dirpath, _, filenames) in os.walk(path):
96 for filename in filenames:
97
98 if (not filename.endswith('.py') # pragma: no branch
99 or filename.endswith('_test.py')):
100 continue # pragma: no cover
101
102 full_filename = os.path.join(dirpath, filename)
103 LOGGER.debug('Scanning file %s', full_filename)
104 descriptions = extract_metrics_descriptions(full_filename)
105
106 for description in descriptions:
107 # TODO(pgervais): use namedtuple here instead
108 if description[3]:
109 documented.append(description)
110 else:
111 non_documented.append(description)
112
113 if documented: # pragma: no branch
114 print('\nDocumented metrics found:')
115 for description in documented:
116 print('/chrome/infra/{2} \t"{3}" at {0}:{1}'.format(*description))
117
118 if non_documented: # pragma: no branch
119 print('\nUndocumented metrics found:')
120 for description in non_documented:
121 print('/chrome/infra/{2} \t at {0}:{1}'.format(*description))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698