1# Copyright 2018, The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Metrics base class. 17""" 18import logging 19import random 20import time 21import uuid 22 23import atest_utils 24import asuite_metrics 25import constants 26 27from proto import clientanalytics_pb2 28from proto import external_user_log_pb2 29from proto import internal_user_log_pb2 30 31from . import clearcut_client 32 33INTERNAL_USER = 0 34EXTERNAL_USER = 1 35 36ATEST_EVENTS = { 37 INTERNAL_USER: internal_user_log_pb2.AtestLogEventInternal, 38 EXTERNAL_USER: external_user_log_pb2.AtestLogEventExternal 39} 40# log source 41ATEST_LOG_SOURCE = { 42 INTERNAL_USER: 971, 43 EXTERNAL_USER: 934 44} 45 46class MetricsBase(object): 47 """Class for separating allowed fields and sending metric.""" 48 49 _run_id = str(uuid.uuid4()) 50 try: 51 #pylint: disable=protected-access 52 _user_key = str(asuite_metrics._get_grouping_key()) 53 #pylint: disable=broad-except 54 except Exception: 55 _user_key = asuite_metrics.DUMMY_UUID 56 _user_type = (EXTERNAL_USER if atest_utils.is_external_run() 57 else INTERNAL_USER) 58 _log_source = ATEST_LOG_SOURCE[_user_type] 59 cc = clearcut_client.Clearcut(_log_source) 60 tool_name = None 61 62 def __new__(cls, **kwargs): 63 """Send metric event to clearcut. 64 65 Args: 66 cls: this class object. 67 **kwargs: A dict of named arguments. 68 69 Returns: 70 A Clearcut instance. 71 """ 72 # pylint: disable=no-member 73 if not cls.tool_name: 74 logging.debug('There is no tool_name, and metrics stops sending.') 75 return None 76 allowed = ({constants.EXTERNAL} if cls._user_type == EXTERNAL_USER 77 else {constants.EXTERNAL, constants.INTERNAL}) 78 fields = [k for k, v in vars(cls).items() 79 if not k.startswith('_') and v in allowed] 80 fields_and_values = {} 81 for field in fields: 82 if field in kwargs: 83 fields_and_values[field] = kwargs.pop(field) 84 params = {'user_key': cls._user_key, 85 'run_id': cls._run_id, 86 'user_type': cls._user_type, 87 'tool_name': cls.tool_name, 88 cls._EVENT_NAME: fields_and_values} 89 log_event = cls._build_full_event(ATEST_EVENTS[cls._user_type](**params)) 90 cls.cc.log(log_event) 91 return cls.cc 92 93 @classmethod 94 def _build_full_event(cls, atest_event): 95 """This is all protobuf building you can ignore. 96 97 Args: 98 cls: this class object. 99 atest_event: A client_pb2.AtestLogEvent instance. 100 101 Returns: 102 A clientanalytics_pb2.LogEvent instance. 103 """ 104 log_event = clientanalytics_pb2.LogEvent() 105 log_event.event_time_ms = int((time.time() - random.randint(1, 600)) * 1000) 106 log_event.source_extension = atest_event.SerializeToString() 107 return log_event 108