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 colorama import Fore, Back, Style, init 18import datetime 19import inspect 20import logging 21import os 22import xml.etree.cElementTree as et 23 24 25TYPE = { 26 'INFO': {'level': 10, 'enabled': True, 'style': None}, 27 'DEBUG': {'level': 20, 'enabled': True, 'style': Fore.GREEN + Style.BRIGHT}, 28 'WARNING': {'level': 30, 'enabled': True, 'style': Fore.YELLOW + Style.BRIGHT}, 29 'ERROR': {'level': 40, 'enabled': True, 'style': Fore.RED + Style.BRIGHT}, 30 'EXCEPTION': {'level': 0, 'enabled': True, 'style': Back.RED + Fore.WHITE + Style.BRIGHT}, 31 'CASE': {'level': 0, 'enabled': True, 'style': Back.BLUE + Fore.WHITE + Style.BRIGHT}, 32 'SUITE': {'level': 0, 'enabled': True, 'style': Back.MAGENTA + Fore.WHITE + Style.BRIGHT}, 33 'DEVICE': {'level': 50, 'enabled': True, 'style': Fore.CYAN + Style.BRIGHT}, 34 'STEP': {'level': 15, 'enabled': True, 'style': Fore.WHITE + Style.BRIGHT}} 35 36 37class TraceLogger(object): 38 def __init__(self, logger): 39 self._logger = logger 40 self.root = et.Element('logger') 41 self.cat = None 42 self.max_level = 100 43 self.type = TYPE 44 self.d = self.debug 45 self.e = self.error 46 self.i = self.info 47 self.t = self.step 48 self.w = self.warning 49 50 51 @staticmethod 52 def _get_trace_info(level=1, offset=2): 53 # We want the stack frame above this and above the error/warning/info 54 inspect_stack = inspect.stack() 55 trace_info = '' 56 for i in range(level): 57 try: 58 stack_frames = inspect_stack[offset + i] 59 info = inspect.getframeinfo(stack_frames[0]) 60 trace_info = '%s[%s:%s:%s]' % (trace_info, 61 os.path.basename(info.filename), 62 info.function, info.lineno) 63 except IndexError: 64 break 65 return trace_info 66 67 def _log_with(self, logging_lambda, trace_level, msg, *args, **kwargs): 68 trace_info = TraceLogger._get_trace_info(level=trace_level, offset=3) 69 logging_lambda('%s %s' % (msg, trace_info), *args, **kwargs) 70 71 def _check_verbosity(self, message_type): 72 if self.level: 73 return self.max_level >= self.type[message_type]['level'] 74 else: 75 return self.type[message_type]['enabled'] 76 77 78 def _xml(self, message_date, message_type, message_text): 79 if self.cat is None: 80 self.cat = et.SubElement(self.root, 'category', name='general', id='gen') 81 message = et.SubElement(self.cat, 'message', name=message_type, date=str(message_date)) 82 message.text = str(message_text) 83 84 85 def _print_message(self, message_type, message): 86 if self._check_verbosity(message_type): 87 now = datetime.datetime.now() 88 self._xml(now, message_type, message) 89 style = self.type[message_type]['style'] 90 default_format = '{} [{}] '.format(now, message_type) 91 if style: 92 for line in str(message).split('\n'): 93 print('{}{} {}'.format(style, default_format, line)) 94 else: 95 for line in str(message).split('\n'): 96 print('{} {}'.format(default_format, line)) 97 98 def exception(self, msg, *args, **kwargs): 99 self._log_with(self._logger.exception, 5, msg, *args, **kwargs) 100 101 def debug(self, msg, *args, **kwargs): 102 self._log_with(self._logger.debug, 3, msg, *args, **kwargs) 103 104 def error(self, msg, *args, **kwargs): 105 self._log_with(self._logger.error, 3, msg, *args, **kwargs) 106 107 def warn(self, msg, *args, **kwargs): 108 self._log_with(self._logger.warn, 1, msg, *args, **kwargs) 109 110 def warning(self, msg, *args, **kwargs): 111 self._log_with(self._logger.warning, 1, msg, *args, **kwargs) 112 113 def info(self, msg, *args, **kwargs): 114 self._log_with(self._logger.info, 1, msg, *args, **kwargs) 115 116 def step(self, message): 117 self._print_message(message_type='STEP', message=message) 118 119 def __getattr__(self, name): 120 return getattr(self._logger, name) 121 122 123class TakoTraceLogger(TraceLogger): 124 def __init__(self, *args, **kwargs): 125 super().__init__(*args, **kwargs) 126 self.d = self.debug 127 self.e = self.error 128 self.i = self.info 129 self.t = self.step 130 self.w = self.warning 131 132 def _logger_level(self, level_name): 133 level = logging.getLevelName(level_name) 134 return lambda *args, **kwargs: self._logger.log(level, *args, **kwargs) 135 136 def step(self, msg, *args, **kwargs): 137 """Delegate a step call to the underlying logger.""" 138 self._log_with(self._logger_level('STEP'), 1, msg, *args, **kwargs) 139 140 def device(self, msg, *args, **kwargs): 141 """Delegate a device call to the underlying logger.""" 142 self._log_with(self._logger_level('DEVICE'), 1, msg, *args, **kwargs) 143 144 def suite(self, msg, *args, **kwargs): 145 """Delegate a device call to the underlying logger.""" 146 self._log_with(self._logger_level('SUITE'), 1, msg, *args, **kwargs) 147 148 def case(self, msg, *args, **kwargs): 149 """Delegate a case call to the underlying logger.""" 150 self._log_with(self._logger_level('CASE'), 1, msg, *args, **kwargs) 151 152 def flush_log(self): 153 """This function exists for compatibility with Tako's logserial module. 154 155 Note that flushing the log is handled automatically by python's logging 156 module. 157 """ 158 pass 159