|
@@ -0,0 +1,113 @@
|
|
1
|
+#-*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+import logging, logging.handlers
|
|
4
|
+import os.path
|
|
5
|
+from lodel.settings import Settings
|
|
6
|
+
|
|
7
|
+# Variables & constants definitions
|
|
8
|
+default_format = '%(asctime)-15s %(levelname)s %(_pathname)s:%(_lineno)s:%(_funcName)s() : %(message)s'
|
|
9
|
+simple_format = '%(asctime)-15s %(levelname)s : %(message)s'
|
|
10
|
+SECURITY_LOGLEVEL = 35
|
|
11
|
+logging.addLevelName(SECURITY_LOGLEVEL, 'SECURITY')
|
|
12
|
+handlers = dict() # Handlers list (generated from settings)
|
|
13
|
+
|
|
14
|
+# Fetching default root logger
|
|
15
|
+logger = logging.getLogger()
|
|
16
|
+
|
|
17
|
+# Setting options from Lodel.settings.Settings.logging
|
|
18
|
+def __init_from_settings():
|
|
19
|
+ # Disabled, because the custom format raises error (enable to give the _ preffixed arguments to logger resulting into a KeyError exception )
|
|
20
|
+ #logging.captureWarnings(True) # Log warnings
|
|
21
|
+
|
|
22
|
+ logger.setLevel(logging.DEBUG)
|
|
23
|
+ for name, logging_opt in Settings.logging.items():
|
|
24
|
+ add_handler(name, logging_opt)
|
|
25
|
+
|
|
26
|
+## @brief Add an handler, identified by a name, to a given logger
|
|
27
|
+#
|
|
28
|
+# logging_opt is a dict with logger option. Allowed keys are :
|
|
29
|
+# - filename : take a filepath as value and cause the use of a logging.handlers.RotatingFileHandler
|
|
30
|
+# - level : the minimum logging level for a logger, takes values [ 'DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL' ]
|
|
31
|
+# - format : DONT USE THIS OPTION (or if you use it be sure to includes %(_pathname)s %(_lineno)s %(_funcName)s format variables in format string
|
|
32
|
+# - context : boolean, if True include the context (module:lineno:function_name) in the log format
|
|
33
|
+# @todo Move the logging_opt documentation somewhere related with settings
|
|
34
|
+#
|
|
35
|
+# @param name str : The handler name
|
|
36
|
+# @param logging_opt dict : dict containing options ( see above )
|
|
37
|
+def add_handler(name, logging_opt):
|
|
38
|
+ logger = logging.getLogger()
|
|
39
|
+ if name in handlers:
|
|
40
|
+ raise KeyError("A handler named '%s' allready exists")
|
|
41
|
+
|
|
42
|
+ if 'filename' in logging_opt:
|
|
43
|
+ maxBytes = (1024 * 10) if 'maxBytes' not in logging_opt else logging_opt['maxBytes']
|
|
44
|
+ backupCount = 10 if 'backupCount' not in logging_opt else logging_opt['backupCount']
|
|
45
|
+
|
|
46
|
+ handler = logging.handlers.RotatingFileHandler(
|
|
47
|
+ logging_opt['filename'],
|
|
48
|
+ maxBytes = maxBytes,
|
|
49
|
+ backupCount = backupCount,
|
|
50
|
+ encoding = 'utf-8')
|
|
51
|
+ else:
|
|
52
|
+ handler = logging.StreamHandler()
|
|
53
|
+
|
|
54
|
+ if 'level' in logging_opt:
|
|
55
|
+ handler.setLevel(getattr(logging, logging_opt['level'].upper()))
|
|
56
|
+
|
|
57
|
+ if 'format' in logging_opt:
|
|
58
|
+ formatter = logging.Formatter(logging_opt['format'])
|
|
59
|
+ else:
|
|
60
|
+ if 'context' in logging_opt and not logging_opt['context']:
|
|
61
|
+ formatter = logging.Formatter(simple_format)
|
|
62
|
+ else:
|
|
63
|
+ formatter = logging.Formatter(default_format)
|
|
64
|
+
|
|
65
|
+ handler.setFormatter(formatter)
|
|
66
|
+ handlers[name] = handler
|
|
67
|
+ logger.addHandler(handler)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+## @brief Remove an handler generated from configuration (runtime logger configuration)
|
|
71
|
+# @param name str : handler name
|
|
72
|
+def remove_handler(name):
|
|
73
|
+ if name in handlers:
|
|
74
|
+ logger.removeHandler(handlers[name])
|
|
75
|
+ # else: can we do anything ?
|
|
76
|
+
|
|
77
|
+## @brief Utility function that disable unconditionnaly handlers that implies console output
|
|
78
|
+# @note In fact, this function disables handlers generated from settings wich are instances of logging.StreamHandler
|
|
79
|
+def remove_console_handlers():
|
|
80
|
+ for name, handler in handlers.items():
|
|
81
|
+ if isinstance(handler, logging.StreamHandler):
|
|
82
|
+ remove_handler(name)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+# Utility functions
|
|
86
|
+
|
|
87
|
+## @brief Generic logging function
|
|
88
|
+# @param lvl int : Log severity
|
|
89
|
+# @param msg str : log message
|
|
90
|
+# @param *args : additional positionnal arguments
|
|
91
|
+# @param **kwargs : additional named arguments
|
|
92
|
+def log(lvl, msg, *args, **kwargs):
|
|
93
|
+ caller = logger.findCaller() # Opti warning : small overhead
|
|
94
|
+ extra = {
|
|
95
|
+ '_pathname': os.path.relpath(
|
|
96
|
+ caller[0],
|
|
97
|
+ start=Settings.lodel2_lib_path
|
|
98
|
+ ), # os.path.relpath add another small overhead
|
|
99
|
+ '_lineno': caller[1],
|
|
100
|
+ '_funcName': caller[2],
|
|
101
|
+ }
|
|
102
|
+ logger.log(lvl, msg, extra = extra, *args, **kwargs)
|
|
103
|
+
|
|
104
|
+def debug(msg, *args, **kwargs): log(logging.DEBUG, msg, *args, **kwargs)
|
|
105
|
+def info(msg, *args, **kwargs): log(logging.INFO, msg, *args, **kwargs)
|
|
106
|
+def warning(msg, *args, **kwargs): log(logging.WARNING, msg, *args, **kwargs)
|
|
107
|
+def security(msg, *args, **kwargs): log(SECURITY_LOGLEVEL, msg, *args, **kwargs)
|
|
108
|
+def error(msg, *args, **kwargs): log(logging.ERROR, msg, *args, **kwargs)
|
|
109
|
+def critical(msg, *args, **kwargs): log(logging.CRITICAL, msg, *args, **kwargs)
|
|
110
|
+
|
|
111
|
+# Initialisation triggering
|
|
112
|
+if len(handlers) == 0:
|
|
113
|
+ __init_from_settings()
|