1# Copyright (C) 2016 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'''Initialise the Python logging facility for the test suite. 16 17from __future__ import absolute_import 18 19It provides the function to initialise the logging facility and retrieve an 20instance of the logger class. It also contains the definition of the internal 21logger class. 22''' 23from __future__ import print_function 24 25import io 26import sys 27import logging 28 29 30INITIALISED = False 31NAMESPACE = 'RS_LLDB_TESTSUITE' 32 33def initialise(identifier, level=logging.INFO, print_to_stdout=False, 34 file_path=None, file_mode='a'): 35 '''Initialise the logging facility for the test suite. 36 37 This function should be invoked only once, at the start of the program, and 38 before emitting any log. 39 40 Args: 41 identifier: String, a label that will be part of each record. It is 42 usually the test case name. 43 level: Integer, all messages above this log level will be discarded. 44 Valid values are those recognised by the python logging module: 45 https://docs.python.org/2/library/logging.html#levels . 46 print_to_stdout: Boolean, whether the logs should be redirected to 47 sys.stdout (true) or stored into a text file (false). 48 file_path: String, path to the text file in which to store the logs. 49 This option is only meaningful when print_to_stdout = False. 50 file_mode: String, the mode to open the text file. Valid modes are 51 those recognised by the standard Python `open' function. 52 This option is only meaningful when print_to_stdout = False. 53 54 Raises: 55 RuntimeError: If the logging has already been initialised 56 ValueError: If the argument "file_path" has not been provided when 57 print_to_stdout=False 58 ''' 59 # pylint: disable=global-statement 60 global INITIALISED 61 if INITIALISED: 62 raise RuntimeError('Already initialised') 63 64 # set the logging class 65 old_logger_class = logging.getLoggerClass() 66 logging.setLoggerClass(RsLogger) 67 68 # initialise the Logger 69 log = logging.getLogger(NAMESPACE) 70 log.setLevel(level) # reject all logs below 71 72 # don't propagate the log records to the logging root 73 log.propagate = False 74 75 # restore the previous class 76 logging.setLoggerClass(old_logger_class) 77 78 # handler 79 if print_to_stdout: 80 handler_default = logging.StreamHandler(sys.stdout) 81 else: 82 if file_path is None: 83 raise ValueError('Missing mandatory argument "file_path"') 84 85 handler_default = logging.FileHandler(file_path, file_mode) 86 87 # Do not filter records in the handler because of the level 88 handler_default.setLevel(logging.NOTSET) 89 90 # format the message 91 handler_default.setFormatter( 92 logging.Formatter( 93 '%(asctime)s [{0}] [%(levelname)s] %(message)s' 94 .format(identifier) 95 )) 96 97 log.addHandler(handler_default) 98 99 INITIALISED = True 100 101 102class RsLogger(logging.getLoggerClass()): 103 '''Internal logging class. 104 105 This is an internal class to enhance the logging facility with the methods 106 "log_and_print" and "seek_to_end". 107 ''' 108 # pylint: disable=too-many-public-methods 109 110 def log_and_print(self, msg, level=logging.INFO): 111 '''Print "msg" to stdout and emit a log record. 112 113 Args: 114 msg: The message to emit. 115 level: The level to use. By default it is logging.INFO. 116 ''' 117 print(msg) 118 self.log(level, msg) 119 120 def seek_to_end(self): 121 '''Reset the cursor position to the end for all handlers that are 122 Text File managers.''' 123 for hndlr in self.handlers: 124 if isinstance(hndlr, logging.FileHandler): 125 hndlr.stream.seek(0, io.SEEK_END) 126 127 128def get_logger(): 129 '''Retrieves the Logger instance related to the testsuite. 130 131 Throws: 132 RuntimeError: If the logging facility has not been initialised with 133 "initialise" beforehand. 134 135 Returns: 136 An instance of logging.Logger to write the logs. 137 ''' 138 if not INITIALISED: 139 raise RuntimeError('Logging facility not initialised') 140 141 return logging.getLogger(NAMESPACE) 142