master
py 198 lines 5.26 KB
Raw
1 # -*- coding: utf-8 -*-
2 # Description:
3 # Author: Ilya Mashchenko (ilyam8)
4 # SPDX-License-Identifier: GPL-3.0-or-later
5
6 import logging
7 import os
8 import stat
9 import traceback
10
11 from sys import exc_info
12
13 try:
14 from time import monotonic as time
15 except ImportError:
16 from time import time
17
18 from bases.collection import on_try_except_finally, unicode_str
19
20 LOGGING_LEVELS = {
21 'DISABLE': 99,
22 'ERROR': 40,
23 'WARNING': 30,
24 'INFO': 20,
25 'DEBUG': 10,
26 'NOTSET': 0,
27 }
28
29
30 def is_stderr_connected_to_journal():
31 journal_stream = os.environ.get("JOURNAL_STREAM")
32 if not journal_stream:
33 return False
34
35 colon_index = journal_stream.find(":")
36 if colon_index <= 0:
37 return False
38
39 device, inode = journal_stream[:colon_index], journal_stream[colon_index + 1:]
40
41 try:
42 device_number, inode_number = os.fstat(2)[stat.ST_DEV], os.fstat(2)[stat.ST_INO]
43 except OSError:
44 return False
45
46 return str(device_number) == device and str(inode_number) == inode
47
48
49 is_journal = is_stderr_connected_to_journal()
50
51 DEFAULT_LOG_LINE_FORMAT = '%(asctime)s: %(name)s %(levelname)s : %(message)s'
52 PYTHON_D_LOG_LINE_FORMAT = '%(asctime)s: %(name)s %(levelname)s: %(module_name)s[%(job_name)s] : %(message)s'
53
54 if is_journal:
55 DEFAULT_LOG_LINE_FORMAT = '%(name)s %(levelname)s : %(message)s'
56 PYTHON_D_LOG_LINE_FORMAT = '%(name)s %(levelname)s: %(module_name)s[%(job_name)s] : %(message)s '
57
58 DEFAULT_LOG_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
59 PYTHON_D_LOG_NAME = 'python.d'
60
61
62 def add_traceback(func):
63 def on_call(*args):
64 self = args[0]
65
66 if not self.log_traceback:
67 func(*args)
68 else:
69 if exc_info()[0]:
70 func(*args)
71 func(self, traceback.format_exc())
72 else:
73 func(*args)
74
75 return on_call
76
77
78 class BaseLogger(object):
79 def __init__(
80 self,
81 logger_name,
82 log_fmt=DEFAULT_LOG_LINE_FORMAT,
83 date_fmt=DEFAULT_LOG_TIME_FORMAT,
84 handler=logging.StreamHandler,
85 ):
86 self.logger = logging.getLogger(logger_name)
87 self._muted = False
88 if not self.has_handlers():
89 self.severity = 'INFO'
90 self.logger.addHandler(handler())
91 self.set_formatter(fmt=log_fmt, date_fmt=date_fmt)
92
93 def __repr__(self):
94 return '<Logger: {name})>'.format(name=self.logger.name)
95
96 def set_formatter(self, fmt, date_fmt=DEFAULT_LOG_TIME_FORMAT):
97 if self.has_handlers():
98 self.logger.handlers[0].setFormatter(logging.Formatter(fmt=fmt, datefmt=date_fmt))
99
100 def has_handlers(self):
101 return self.logger.handlers
102
103 @property
104 def severity(self):
105 return self.logger.getEffectiveLevel()
106
107 @severity.setter
108 def severity(self, level):
109 if level in LOGGING_LEVELS:
110 self.logger.setLevel(LOGGING_LEVELS[level])
111
112 def _log(self, level, *msg, **kwargs):
113 if not self._muted:
114 self.logger.log(level, ' '.join(map(unicode_str, msg)), **kwargs)
115
116 def debug(self, *msg, **kwargs):
117 self._log(logging.DEBUG, *msg, **kwargs)
118
119 def info(self, *msg, **kwargs):
120 self._log(logging.INFO, *msg, **kwargs)
121
122 def warning(self, *msg, **kwargs):
123 self._log(logging.WARN, *msg, **kwargs)
124
125 def error(self, *msg, **kwargs):
126 self._log(logging.ERROR, *msg, **kwargs)
127
128 def alert(self, *msg, **kwargs):
129 self._log(logging.CRITICAL, *msg, **kwargs)
130
131 @on_try_except_finally(on_finally=(exit, 1))
132 def fatal(self, *msg, **kwargs):
133 self._log(logging.CRITICAL, *msg, **kwargs)
134
135 def mute(self):
136 self._muted = True
137
138 def unmute(self):
139 self._muted = False
140
141
142 class PythonDLogger(object):
143 def __init__(
144 self,
145 logger_name=PYTHON_D_LOG_NAME,
146 log_fmt=PYTHON_D_LOG_LINE_FORMAT,
147 ):
148 self.logger = BaseLogger(logger_name, log_fmt=log_fmt)
149 self.module_name = 'plugin'
150 self.job_name = 'main'
151
152 _LOG_TRACEBACK = False
153
154 @property
155 def log_traceback(self):
156 return PythonDLogger._LOG_TRACEBACK
157
158 @log_traceback.setter
159 def log_traceback(self, value):
160 PythonDLogger._LOG_TRACEBACK = value
161
162 def debug(self, *msg):
163 self.logger.debug(*msg, extra={
164 'module_name': self.module_name,
165 'job_name': self.job_name or self.module_name,
166 })
167
168 def info(self, *msg):
169 self.logger.info(*msg, extra={
170 'module_name': self.module_name,
171 'job_name': self.job_name or self.module_name,
172 })
173
174 def warning(self, *msg):
175 self.logger.warning(*msg, extra={
176 'module_name': self.module_name,
177 'job_name': self.job_name or self.module_name,
178 })
179
180 @add_traceback
181 def error(self, *msg):
182 self.logger.error(*msg, extra={
183 'module_name': self.module_name,
184 'job_name': self.job_name or self.module_name,
185 })
186
187 @add_traceback
188 def alert(self, *msg):
189 self.logger.alert(*msg, extra={
190 'module_name': self.module_name,
191 'job_name': self.job_name or self.module_name,
192 })
193
194 def fatal(self, *msg):
195 self.logger.fatal(*msg, extra={
196 'module_name': self.module_name,
197 'job_name': self.job_name or self.module_name,
198 })