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