| Index: infra/tools/metric_tool/metric_tool.py
|
| diff --git a/infra/tools/metric_tool/metric_tool.py b/infra/tools/metric_tool/metric_tool.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..bcf2b22fe9b789459a186df6d81e56e8c7bd488f
|
| --- /dev/null
|
| +++ b/infra/tools/metric_tool/metric_tool.py
|
| @@ -0,0 +1,120 @@
|
| +# Copyright 2015 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +"""Main implementation of metric_tool."""
|
| +
|
| +import ast
|
| +import os
|
| +import logging
|
| +
|
| +
|
| +# https://chromium.googlesource.com/infra/infra/+/master/infra_libs/logs/README.md
|
| +LOGGER = logging.getLogger(__name__)
|
| +
|
| +METRICS_NAMES = set(('StringMetric', 'BooleanMetric',
|
| + 'CounterMetric', 'GaugeMetric', 'CumulativeMetric',
|
| + 'FloatMetric', 'CumulativeDistributionMetric',
|
| + 'NonCumulativeDistributionMetric'))
|
| +
|
| +
|
| +def extract_metrics_descriptions(filepath):
|
| + """Parse a python file and return all metrics descriptions it can find.
|
| +
|
| + A metric description is the value of the 'description' keyword passed to a
|
| + metric definition (classes deriving from ts_mon.Metric)
|
| +
|
| + Args:
|
| + filepath (str): path to a Python file.
|
| +
|
| + Returns:
|
| + description (list of tuples): each tuple being
|
| + (metric name, description string). Metric name is 'DYNAMIC' it something
|
| + different than a static string is used in the metric creation.
|
| + If a metric is instanciated without a description, return None.
|
| + """
|
| + descriptions = []
|
| + try:
|
| + with open(filepath, 'r') as f:
|
| + content = f.read() # pragma: no branch
|
| + except IOError:
|
| + return descriptions
|
| +
|
| + try:
|
| + root = ast.parse(content)
|
| + except SyntaxError: # just ignore invalid / python3 files.
|
| + return descriptions
|
| +
|
| + for node in ast.walk(root):
|
| + if not isinstance(node, ast.Call):
|
| + continue
|
| +
|
| + # Look for metrics definitions
|
| + calls = []
|
| + if isinstance(node.func, ast.Name):
|
| + if node.func.id in METRICS_NAMES:
|
| + LOGGER.debug('Method %s found line %d', node.func.id, node.func.lineno)
|
| + calls.append(node)
|
| + elif isinstance(node.func, ast.Attribute): # pragma: no branch
|
| + if node.func.attr in METRICS_NAMES:
|
| + LOGGER.debug('Method %s found line %d',
|
| + node.func.attr, node.func.lineno)
|
| + calls.append(node)
|
| +
|
| + # Extract parameters from function call
|
| + for fcall in calls:
|
| + # Metric name
|
| + metric_name = 'DYNAMIC'
|
| + if fcall.args and isinstance(fcall.args[0], ast.Str):
|
| + metric_name = fcall.args[0].s
|
| +
|
| + # Getting descriptions
|
| + description = None
|
| + for keyword in fcall.keywords:
|
| + if keyword.arg == 'description' and isinstance(keyword.value, ast.Str):
|
| + description = keyword.value.s
|
| +
|
| + descriptions.append((filepath, fcall.lineno, metric_name, description))
|
| +
|
| + return descriptions
|
| +
|
| +
|
| +def main(path):
|
| + """Recursively walk a directory structure and print metrics documentation.
|
| +
|
| + For all instanciated metrics in any Python file found under the directory
|
| + passed as argument, if a 'description' keyword is provided print it
|
| + alongside the metric name.
|
| +
|
| + Args:
|
| + path (str): directory.
|
| + """
|
| + documented = []
|
| + non_documented = []
|
| +
|
| + for (dirpath, _, filenames) in os.walk(path):
|
| + for filename in filenames:
|
| +
|
| + if (not filename.endswith('.py') # pragma: no branch
|
| + or filename.endswith('_test.py')):
|
| + continue # pragma: no cover
|
| +
|
| + full_filename = os.path.join(dirpath, filename)
|
| + LOGGER.debug('Scanning file %s', full_filename)
|
| + descriptions = extract_metrics_descriptions(full_filename)
|
| +
|
| + for description in descriptions:
|
| + # TODO(pgervais): use namedtuple here instead
|
| + if description[3]:
|
| + documented.append(description)
|
| + else:
|
| + non_documented.append(description)
|
| +
|
| + if documented: # pragma: no branch
|
| + print('\nDocumented metrics found:')
|
| + for description in documented:
|
| + print('/chrome/infra/{2} \t"{3}" at {0}:{1}'.format(*description))
|
| +
|
| + if non_documented: # pragma: no branch
|
| + print('\nUndocumented metrics found:')
|
| + for description in non_documented:
|
| + print('/chrome/infra/{2} \t at {0}:{1}'.format(*description))
|
|
|