1#!/usr/bin/env python3 2# 3# Copyright 2016 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17from acts.libs.proto.proto_utils import md5_proto 18from acts.metrics.core import ProtoMetric 19from acts.metrics.logger import MetricLogger 20from acts.metrics.loggers.protos.gen import acts_blackbox_pb2 21 22 23class BlackboxMappedMetricLogger(MetricLogger): 24 """A MetricLogger for logging and publishing Blackbox metrics from a dict. 25 The dict maps the metric name to the metric value. 26 27 The logger will publish an ActsBlackboxMetricResult message, containing 28 data intended to be uploaded to Blackbox. The message itself contains only 29 minimal information specific to the metric, with the intention being that 30 all other metadata is extracted from the test_summary.yaml. 31 32 This logger will extract an attribute from the test class as the metric 33 result. The metric key will be either the context's identifier or a custom 34 value assigned to this class. 35 36 Attributes: 37 metric_key: The metric key to use. If unset, the logger will use the 38 context's identifier. 39 _metric_map: the map of metric_name -> metric_value to publish 40 to blackbox. If the metric value is set to None, the 41 metric will not be reported. 42 """ 43 44 def __init__(self, metric_key=None, event=None): 45 """Initializes a logger for Blackbox metrics. 46 47 Args: 48 metric_key: The metric key to use. If unset, the logger will use 49 the context's identifier. 50 event: The event triggering the creation of this logger. 51 """ 52 super().__init__(event=event) 53 self.metric_key = metric_key 54 self._metric_map = {} 55 56 def _get_metric_key(self, metric_name): 57 """Gets the metric key to use. 58 59 If the metric_key is explicitly set, returns that value. Otherwise, 60 extracts an identifier from the context. 61 62 Args: 63 metric_name: The name of the metric to report. 64 """ 65 if self.metric_key: 66 key = self.metric_key 67 else: 68 key = self._get_blackbox_identifier() 69 key = '%s.%s' % (key, metric_name) 70 return key 71 72 def set_metric_data(self, metric_map): 73 """Sets the map of metrics to be uploaded to Blackbox. Note that 74 this will overwrite all existing added by this function or add_metric. 75 76 Args: 77 metric_map: the map of metric_name -> metric_value to publish 78 to blackbox. If the metric value is set to None, the 79 metric will not be reported. 80 """ 81 self._metric_map = metric_map 82 83 def add_metric(self, metric_name, metric_value): 84 """Adds a metric value to be published later. 85 86 Note that if the metric name has already been added, the metric value 87 will be overwritten. 88 89 Args: 90 metric_name: the name of the metric. 91 metric_value: the value of the metric. 92 """ 93 self._metric_map[metric_name] = metric_value 94 95 def _get_blackbox_identifier(self): 96 """Returns the testcase identifier, as expected by Blackbox.""" 97 # b/119787228: Blackbox requires function names to look like Java 98 # functions. 99 identifier = self.context.identifier 100 parts = identifier.rsplit('.', 1) 101 return '#'.join(parts) 102 103 def end(self, _): 104 """Creates and publishes a ProtoMetric with blackbox data. 105 106 Builds a list of ActsBlackboxMetricResult messages from the set 107 metric data, and sends them to the publisher. 108 """ 109 bundle = acts_blackbox_pb2.ActsBlackboxMetricResultsBundle() 110 for metric_name, metric_value in self._metric_map.items(): 111 if metric_value is None: 112 continue 113 result = acts_blackbox_pb2.ActsBlackboxMetricResult() 114 result.test_identifier = self._get_blackbox_identifier() 115 result.metric_key = self._get_metric_key(metric_name) 116 result.metric_value = metric_value 117 bundle.acts_blackbox_metric_results.append(result) 118 119 # Since there could technically be more than one concurrent logger 120 # instance we add a hash for files to not override each other. We use a 121 # static hash for repeatability. 122 bundle_name = 'blackbox_metrics_bundle.' + md5_proto(bundle)[0:8] 123 return self.publisher.publish( 124 [ProtoMetric(name=bundle_name, data=bundle)]) 125 126 127class BlackboxMetricLogger(BlackboxMappedMetricLogger): 128 """A MetricLogger for logging and publishing individual Blackbox metrics. 129 130 For additional information on reporting to Blackbox, see 131 BlackboxMappedMetricLogger. 132 133 Attributes: 134 metric_name: The name of the metric, used to determine output filename. 135 metric_key: The metric key to use. If unset, the logger will use the 136 context's identifier. 137 metric_value: The metric value. 138 """ 139 140 def __init__(self, metric_name, metric_key=None, event=None): 141 """Initializes a logger for Blackbox metrics. 142 143 Args: 144 metric_name: The name of the metric. 145 metric_key: The metric key to use. If unset, the logger will use 146 the context's identifier. 147 event: The event triggering the creation of this logger. 148 """ 149 super().__init__(metric_key=metric_key, event=event) 150 if not metric_name: 151 raise ValueError("metric_name must be supplied.") 152 self.metric_name = metric_name 153 self.metric_value = None 154 155 @property 156 def metric_value(self): 157 return self._metric_map[self.metric_name] 158 159 @metric_value.setter 160 def metric_value(self, value): 161 self.add_metric(self.metric_name, value) 162