• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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