forked from Ultimaker/Uranium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLogger.py
113 lines (96 loc) · 4.75 KB
/
Logger.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# Copyright (c) 2018 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.
import threading
import traceback
import inspect
from typing import List
from UM.PluginObject import PluginObject
## Static class used for logging purposes. This class is only meant to be used as a static class.
class Logger:
__loggers = [] # type: List[Logger]
def __init__(self):
raise Exception("This class is static only")
## Add a logger to the list.
# \param logger \type{Logger}
@classmethod
def addLogger(cls, logger: "Logger"):
cls.__loggers.append(logger)
## Get all loggers
# \returns \type{list} List of Loggers
@classmethod
def getLoggers(cls) -> List["Logger"]:
return cls.__loggers
## Send a message of certain type to all loggers to be handled.
#
# This method supports placeholders in either str.format() style or % style. For more details see
# the respective Python documentation pages.
#
# Note that only str.format() supports keyword argument placeholders. Additionally, if str.format()
# makes any changes, % formatting will not be applied.
#
# \param log_type \type{string} Values must be; 'e' (error) , 'i'(info), 'd'(debug) or 'w'(warning).
# \param message \type{string} containing message to be logged
#
# \param *args \type{list} List of placeholder replacements that will be passed to str.format() or %.
# \param **kwargs \type{dict} List of placeholder replacements that will be passed to str.format().
@classmethod
def log(cls, log_type: str, message: str, *args, **kwargs):
current_frame = inspect.currentframe()
if current_frame is None: # Avoid crash if the inspect module returns None (it should never happen)
return
caller_frame = current_frame.f_back
frame_info = inspect.getframeinfo(caller_frame)
try:
if args or kwargs: # Only format the message if there are args
new_message = message.format(*args, **kwargs)
if new_message == message:
new_message = message % args # Replace all the %s with the variables. Python formatting is magic.
message = new_message
current_thread = threading.current_thread()
message = "[{thread}] {class_name}.{function} [{line}]: {message}".format(thread = current_thread.name,
class_name = caller_frame.f_globals["__name__"],
function = frame_info.function,
line = frame_info.lineno,
message = message)
for logger in cls.__loggers:
logger.log(log_type, message)
except Exception as e:
print("FAILED TO LOG: ", log_type, message, e)
if not cls.__loggers:
print(message)
## Logs that an exception occurs.
#
# It'll include the traceback of the exception in the log message. The
# traceback is obtained from the current execution state.
#
# \param log_type The importance level of the log (warning, info, etc.).
# \param message The message to go along with the exception.
@classmethod
def logException(cls, log_type: str, message: str, *args):
cls.log(log_type, "Exception: " + message, *args)
# The function traceback.format_exception gives a list of strings, but those are not properly split on newlines.
# traceback.format_exc only gives back a single string, but we can properly split that. It does add an extra newline at the end, so strip that.
for line in traceback.format_exc().rstrip().split("\n"):
cls.log(log_type, line)
## Abstract base class for log output classes.
class LogOutput(PluginObject):
## Create the log output.
#
# This is called during the plug-in loading stage.
def __init__(self):
super().__init__() # Call super to make multiple inheritance work.
self._name = type(self).__name__ # Set name of the logger to it's class name
## Log a message.
#
# The possible message types are:
# - "d", debug
# - "i", info
# - "w", warning
# - "e", error
# - "c", critical
#
# \param log_type \type{string} A value describing the type of message.
# \param message \type{string} The message to log.
# \exception NotImplementedError
def log(self, log_type: str, message: str):
raise NotImplementedError("Logger was not correctly implemented")