1import logging, os, sys, time
2
3# set up a simple catchall configuration for use during import time.  some code
4# may log messages at import time and we don't want those to get completely
5# thrown away.  we'll clear this out when actual configuration takes place.
6logging.basicConfig(level=logging.DEBUG)
7
8class AllowBelowSeverity(logging.Filter):
9    """
10    Allows only records less severe than a given level (the opposite of what
11    the normal logging level filtering does.
12    """
13    def __init__(self, level):
14        self.level = level
15
16
17    def filter(self, record):
18        return record.levelno < self.level
19
20
21class LoggingConfig(object):
22    global_level = logging.DEBUG
23    stdout_level = logging.INFO
24    stderr_level = logging.ERROR
25
26    FILE_FORMAT = ('%(asctime)s.%(msecs)03d %(levelname)-5.5s|%(module)18.18s:'
27                   '%(lineno)4.4d| %(message)s')
28
29    file_formatter = logging.Formatter(
30        fmt=FILE_FORMAT, datefmt='%m/%d %H:%M:%S')
31
32    CONSOLE_FORMAT = '%(asctime)s %(levelname)-5.5s| %(message)s'
33
34    console_formatter = logging.Formatter(
35            fmt=CONSOLE_FORMAT, datefmt='%H:%M:%S')
36
37    def __init__(self, use_console=True):
38        self.logger = logging.getLogger()
39        self.global_level = logging.DEBUG
40        self.use_console = use_console
41
42
43    @classmethod
44    def get_timestamped_log_name(cls, base_name):
45        return '%s.log.%s' % (base_name, time.strftime('%Y-%m-%d-%H.%M.%S'))
46
47
48    @classmethod
49    def get_autotest_root(cls):
50        common_lib_dir = os.path.dirname(__file__)
51        return os.path.abspath(os.path.join(common_lib_dir, '..', '..'))
52
53
54    @classmethod
55    def get_server_log_dir(cls):
56        return os.path.join(cls.get_autotest_root(), 'logs')
57
58
59    def add_stream_handler(self, stream, level=logging.DEBUG, datefmt=None):
60        handler = logging.StreamHandler(stream)
61        handler.setLevel(level)
62        formatter = self.console_formatter
63        if datefmt:
64            formatter = logging.Formatter(fmt=self.CONSOLE_FORMAT,
65                                          datefmt=datefmt)
66        handler.setFormatter(formatter)
67        self.logger.addHandler(handler)
68        return handler
69
70
71    def add_console_handlers(self, datefmt=None):
72        stdout_handler = self.add_stream_handler(
73                sys.stdout, level=self.stdout_level, datefmt=datefmt)
74        # only pass records *below* STDERR_LEVEL to stdout, to avoid duplication
75        stdout_handler.addFilter(AllowBelowSeverity(self.stderr_level))
76
77        self.add_stream_handler(sys.stderr, self.stderr_level, datefmt)
78
79
80    def add_file_handler(self, file_path, level=logging.DEBUG, log_dir=None,
81                         datefmt=None):
82        if log_dir:
83            file_path = os.path.join(log_dir, file_path)
84        handler = logging.FileHandler(file_path)
85        handler.setLevel(level)
86        formatter = self.file_formatter
87        if datefmt:
88            formatter = logging.Formatter(fmt=self.FILE_FORMAT, datefmt=datefmt)
89        handler.setFormatter(formatter)
90        self.logger.addHandler(handler)
91        return handler
92
93
94    def _add_file_handlers_for_all_levels(self, log_dir, log_name):
95        for level in (logging.DEBUG, logging.INFO, logging.WARNING,
96                      logging.ERROR):
97            file_name = '%s.%s' % (log_name, logging.getLevelName(level))
98            self.add_file_handler(file_name, level=level, log_dir=log_dir)
99
100
101    def add_debug_file_handlers(self, log_dir, log_name=None):
102        raise NotImplementedError
103
104
105    def _clear_all_handlers(self):
106        for handler in list(self.logger.handlers):
107            self.logger.removeHandler(handler)
108            # Attempt to close the handler. If it's already closed a KeyError
109            # will be generated. http://bugs.python.org/issue8581
110            try:
111                handler.close()
112            except KeyError:
113                pass
114
115
116    def configure_logging(self, use_console=True, verbose=False, datefmt=None):
117        self._clear_all_handlers() # see comment at top of file
118        self.logger.setLevel(self.global_level)
119
120        if verbose:
121            self.stdout_level = logging.DEBUG
122        if use_console:
123            self.add_console_handlers(datefmt)
124
125
126class TestingConfig(LoggingConfig):
127    def add_stream_handler(self, *args, **kwargs):
128        pass
129
130
131    def add_file_handler(self, *args, **kwargs):
132        pass
133
134
135    def configure_logging(self, **kwargs):
136        pass
137