blob: c0c5b26aedf56711b7f3b8c99d878df2c4786644 [file] [log] [blame]
Will Bradley7e5b8c12019-07-30 12:44:15 -06001# Copyright 2019 The Chromium OS 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
5"""Metrics for general consumption.
6
7See infra/proto/metrics.proto for a description of the type of record that this
8module will be creating.
9"""
10
Alex Klein9964a1c2022-04-19 09:42:13 -060011import collections
Chris McDonald1672ddb2021-07-21 11:48:23 -060012import logging
13
Alex Kleinaef41942022-04-19 14:13:17 -060014from chromite.lib import metrics_lib
Will Bradley7e5b8c12019-07-30 12:44:15 -060015
16
17def deserialize_metrics_log(output_events, prefix=None):
Alex Klein1699fab2022-09-08 08:46:06 -060018 """Read the current metrics events, adding to output_events.
Will Bradley7e5b8c12019-07-30 12:44:15 -060019
Alex Klein1699fab2022-09-08 08:46:06 -060020 This layer facilitates converting between the internal
21 chromite.utils.metrics representation of metric events and the
22 infra/proto/src/chromiumos/metrics.proto output type.
Will Bradley7e5b8c12019-07-30 12:44:15 -060023
Alex Klein1699fab2022-09-08 08:46:06 -060024 Args:
25 output_events: A chromiumos.MetricEvent protobuf message.
26 prefix: A string to prepend to all metric event names.
27 """
28 counters = collections.defaultdict(int)
29 counter_times = {}
30 timers = {}
Will Bradley7e5b8c12019-07-30 12:44:15 -060031
Alex Klein1699fab2022-09-08 08:46:06 -060032 def make_name(name):
33 """Prepend a closed-over prefix to the given name."""
34 if prefix:
35 return "%s.%s" % (prefix, name)
36 else:
37 return name
Will Bradley7e5b8c12019-07-30 12:44:15 -060038
Alex Klein1699fab2022-09-08 08:46:06 -060039 # Reduce over the input events to append output_events.
40 for input_event in metrics_lib.read_metrics_events():
41 if input_event.op == metrics_lib.OP_START_TIMER:
42 timers[input_event.arg] = (
43 input_event.name,
44 input_event.timestamp_epoch_millis,
45 )
46 elif input_event.op == metrics_lib.OP_STOP_TIMER:
47 # TODO(wbbradley): Drop the None fallback https://crbug.com/1001909.
48 timer = timers.pop(input_event.arg, None)
49 if timer is None:
50 logging.error(
51 "%s: stop timer recorded, but missing start timer!?",
52 input_event.arg,
53 )
54 if timer:
55 assert input_event.name == timer[0]
56 output_event = output_events.add()
57 output_event.name = make_name(timer[0])
58 output_event.timestamp_milliseconds = (
59 input_event.timestamp_epoch_millis
60 )
61 output_event.duration_milliseconds = (
62 output_event.timestamp_milliseconds - timer[1]
63 )
64 elif input_event.op == metrics_lib.OP_NAMED_EVENT:
65 output_event = output_events.add()
66 output_event.name = make_name(input_event.name)
67 output_event.timestamp_milliseconds = (
68 input_event.timestamp_epoch_millis
69 )
70 elif input_event.op == metrics_lib.OP_GAUGE:
71 output_event = output_events.add()
72 output_event.name = make_name(input_event.name)
73 output_event.timestamp_milliseconds = (
74 input_event.timestamp_epoch_millis
75 )
76 output_event.gauge = input_event.arg
77 elif input_event.op == metrics_lib.OP_INCREMENT_COUNTER:
78 counters[input_event.name] += input_event.arg
79 counter_times[input_event.name] = max(
80 input_event.timestamp_epoch_millis,
81 counter_times.get(input_event.name, 0),
82 )
83 elif input_event.op == metrics_lib.OP_DECREMENT_COUNTER:
84 counters[input_event.name] -= input_event.arg
85 counter_times[input_event.name] = max(
86 input_event.timestamp_epoch_millis,
87 counter_times.get(input_event.name, 0),
88 )
89 else:
90 raise ValueError(
91 'unexpected op "%s" found in metric event: %s'
92 % (input_event.op, input_event)
93 )
94
95 for counter, value in counters.items():
Will Bradley7e5b8c12019-07-30 12:44:15 -060096 output_event = output_events.add()
Alex Klein1699fab2022-09-08 08:46:06 -060097 output_event.name = make_name(counter)
98 output_event.gauge = value
99 output_event.timestamp_milliseconds = counter_times[counter]
Will Bradley7e5b8c12019-07-30 12:44:15 -0600100
Alex Klein1699fab2022-09-08 08:46:06 -0600101 # Check for any unhandled timers.
102 # TODO(wbbradley): Turn this back into an assert https://crbug.com/1001909.
103 if timers:
104 logging.error("excess timer metric data left over: %s", timers)