Completed
Pull Request — master (#2409)
by
unknown
01:55
created

LogPrinter.__getstate__()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
dl 0
loc 5
rs 9.4285
c 1
b 0
f 0
1
import copy
2
import sys
3
import traceback
4
import logging
5
import logging.config
6
from colorama import Style
7
8
from pyprint.Printer import Printer
0 ignored issues
show
Unused Code introduced by
Unused Printer imported from pyprint.Printer
Loading history...
9
from pyprint.ColorPrinter import ColorPrinter
10
11
from coalib.output.printers.LOG_LEVEL import LOG_LEVEL
12
from coalib.output.printers.LOG_LEVEL import LOG_LEVEL_COLORS
13
from coalib.processes.communication.LogMessage import LogMessage
14
15
16
class PrinterHandler(logging.StreamHandler):
17
    """
18
    Handler for logging which uses printer to print messages and falls back to
19
    writing to stdout if no printer is provided.
20
    """
21
22
    def emit(self, record):
23
        msg = self.format(record)
24
        sys.stdout.write(msg + "\n")
25
26
27
class ColoredFormatter(logging.Formatter):
28
    """
29
    Formatter for logging which adds colors to the output by inserting special
30
    symbols where appropriate.
31
    """
32
33
    def __init__(self, *args, **kwargs):
34
        """
35
        :param use_color:  If True highlights records levelname with respective
36
                           color.
37
        """
38
        self.use_color = kwargs.pop('use_color', False)
39
        super().__init__(*args, **kwargs)
40
41
    def format(self, record, *args, **kwargs):
42
        new_record = copy.copy(record)
43
        if new_record.levelno in LOG_LEVEL_COLORS and self.use_color:
44
            color_begin = LOG_LEVEL_COLORS[new_record.levelno]
45
            color_end = Style.RESET_ALL
46
        else:
47
            color_begin = color_end = ''
48
        new_record.levelname = '{cb}[{levelname}]{ce}'.format(
49
            cb=color_begin,
50
            ce=color_end,
51
            levelname=new_record.levelname)
52
        return super().format(new_record, *args, **kwargs)
53
54
55
class LogPrinter:
56
    """
57
    The LogPrinter class allows to print log messages to an underlying Printer.
58
59
    This class is an adapter, means you can create a LogPrinter from every
60
    existing Printer instance.
61
    """
62
    _loggers = {}
63
64
    @staticmethod
65
    def _get_logger(logger_name, time_fmt=None):
66
        """
67
        Returns existing or newly created and configured logger.
68
        :param logger_name: name of the logger to be returned.
69
        :param printer:     printer to initialize logger with if it doesn't
70
                            exist, yet.
71
        :param time_fmt:    format to be used for time output. No time is
72
                            pinter if None.
73
        """
74
        if not LogPrinter._loggers.get(logger_name):
75
            if time_fmt:
76
                format_str = '%(levelname)s[%(asctime)s] %(message)s'
77
            else:
78
                format_str = '%(levelname)s %(message)s'
79
80
            use_color = logger_name == 'coala.colored'
81
            formatter = ColoredFormatter(format_str,
82
                                         time_fmt,
83
                                         use_color=use_color)
84
85
            handler = PrinterHandler()
86
            handler.setFormatter(formatter)
87
88
            logger = logging.getLogger(logger_name)
89
            # logger.addHandler(handler)
90
91
            LogPrinter._loggers[logger_name] = logger
92
        return LogPrinter._loggers[logger_name]
93
94
    def __init__(self,
95
                 printer=None,
96
                 log_level=LOG_LEVEL.INFO,
97
                 timestamp_format="%X"):
98
        """
99
        This class is deprecated and will be soon removed. To get logger use
100
        loggin.getLogger('coala.colored') for colored logger and
101
        logging.getLogger('coala.raw') for not colored logger.
102
103
        Creates a new log printer from an existing Printer or returns existing
104
        one if it already exist.
105
        Note: log_level changes on every logger instantiation.
106
107
        :param printer:          The underlying Printer where log messages
108
                                 shall be written to. If you inherit from
109
                                 Printer, set it to self.
110
        :param log_level:        The minimum log level, everything below will
111
                                 not be logged.
112
        :param timestamp_format: The format string for the
113
                                 datetime.today().strftime(format) method.
114
        """
115
        if isinstance(printer, ColorPrinter):
116
            self.logger_name = 'coala.colored'
117
        else:
118
            self.logger_name = 'coala.raw'
119
        self.logger = logging.getLogger(self.logger_name)
120
        # LogPrinter._get_logger(self.logger_name, timestamp_format)
121
122
        self._printer = printer
123
        self.log_level = log_level
124
        self.timestamp_format = timestamp_format
125
126
    @property
127
    def log_level(self):
128
        """
129
        Returns current log_level used is logger.
130
        """
131
        return self._log_level
132
133
    @log_level.setter
134
    def log_level(self, log_level):
135
        """
136
        Sets log_level for logger.
137
        """
138
        self._log_level = log_level
139
        self.logger.setLevel(log_level)
140
141
    @property
142
    def printer(self):
143
        """
144
        Returns the underlying printer where logs are printed to.
145
        """
146
        return self._printer
147
148
    def debug(self, *messages, delimiter=" ", timestamp=None, **kwargs):
149
        self.log_message(LogMessage(LOG_LEVEL.DEBUG,
150
                                    *messages,
151
                                    delimiter=delimiter,
152
                                    timestamp=timestamp),
153
                         **kwargs)
154
155
    def info(self, *messages, delimiter=" ", timestamp=None, **kwargs):
156
        self.log_message(LogMessage(LOG_LEVEL.INFO,
157
                                    *messages,
158
                                    delimiter=delimiter,
159
                                    timestamp=timestamp),
160
                         **kwargs)
161
162
    def warn(self, *messages, delimiter=" ", timestamp=None, **kwargs):
163
        self.log_message(LogMessage(LOG_LEVEL.WARNING,
164
                                    *messages,
165
                                    delimiter=delimiter,
166
                                    timestamp=timestamp),
167
                         **kwargs)
168
169
    def err(self, *messages, delimiter=" ", timestamp=None, **kwargs):
170
        self.log_message(LogMessage(LOG_LEVEL.ERROR,
171
                                    *messages,
172
                                    delimiter=delimiter,
173
                                    timestamp=timestamp),
174
                         **kwargs)
175
176
    def log(self, log_level, message, timestamp=None, **kwargs):
177
        self.log_message(LogMessage(log_level,
178
                                    message,
179
                                    timestamp=timestamp),
180
                         **kwargs)
181
182
    def log_exception(self,
183
                      message,
184
                      exception,
185
                      log_level=LOG_LEVEL.ERROR,
186
                      timestamp=None,
187
                      **kwargs):
188
        """
189
        If the log_level of the printer is greater than DEBUG, it prints
190
        only the message. If it is DEBUG or lower, it shows the message
191
        along with the traceback of the exception.
192
193
        :param message:   The message to print.
194
        :param exception: The exception to print.
195
        :param log_level: The log_level of this message (not used when
196
                          logging the traceback. Tracebacks always have
197
                          a level of DEBUG).
198
        :param timestamp: The time at which this log occured. Defaults to
199
                          the current time.
200
        :param kwargs:    Keyword arguments to be passed when logging the
201
                          message (not used when logging the traceback).
202
        """
203
        if not isinstance(exception, BaseException):
204
            raise TypeError("log_exception can only log derivatives of "
205
                            "BaseException.")
206
207
        traceback_str = "\n".join(
208
            traceback.format_exception(type(exception),
209
                                       exception,
210
                                       exception.__traceback__))
211
212
        self.log(log_level, message, timestamp=timestamp, **kwargs)
213
        self.log_message(
214
            LogMessage(LOG_LEVEL.DEBUG,
215
                       "Exception was:" + "\n" + traceback_str,
216
                       timestamp=timestamp),
217
            **kwargs)
218
219
    def log_message(self, log_message, **kwargs):
220
        if not isinstance(log_message, LogMessage):
221
            raise TypeError("log_message should be of type LogMessage.")
222
        self.logger.log(log_message.log_level, log_message.message)
223
224
    def __getstate__(self):
225
        # on Windows there are problems with serializing loggers, so omit it
226
        oldict = self.__dict__.copy()
227
        del oldict['logger']
228
        return oldict
229
230
    def __setstate__(self, newdict):
231
        self.__dict__.update(newdict)
232
        # restore logger by name
233
        self.logger = logging.getLogger(self.logger_name)
234