1 # Copyright 2018 ke liang <lokyse@163.com>.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
22 from marker import Marker
23 from marker import MARKER_TAG
25 __all__ = ['patch_loggingMDC', 'MDC']
27 _replace_func_name = ['info', 'critical', 'fatal', 'debug',
28 'error', 'warn', 'warning', 'log',
29 'handle', 'findCaller']
32 class MDCContext(threading.local):
34 A Thread local instance to storage mdc values
38 super(MDCContext, self).__init__()
43 return self._localDict.get(key, None)
45 def put(self, key, value):
47 self._localDict[key] = value
49 def remove(self, key):
51 if key in self.localDict:
52 del self._localDict[key]
56 self._localDict.clear()
60 return self._localDict
64 return self._localDict == {} or self._localDict is None
72 @functools.wraps(func)
73 def replace(*args, **kwargs):
74 kwargs['extra'] = _getmdcs(extra=kwargs.get('extra', None))
79 def _getmdcs(extra=None):
81 Put mdc dict in logging record extra filed with key 'mdc'
92 # make sure extra key dosen't override mdckey
93 if key in mdc or key == 'mdc':
94 raise KeyError("Attempt to overwrite %r in MDC" % key)
105 def info(self, msg, *args, **kwargs):
107 if self.isEnabledFor(logging.INFO):
108 self._log(logging.INFO, msg, args, **kwargs)
112 def debug(self, msg, *args, **kwargs):
113 if self.isEnabledFor(logging.DEBUG):
114 self._log(logging.DEBUG, msg, args, **kwargs)
118 def warning(self, msg, *args, **kwargs):
119 if self.isEnabledFor(logging.WARNING):
120 self._log(logging.WARNING, msg, args, **kwargs)
124 def exception(self, msg, *args, **kwargs):
126 kwargs['exc_info'] = 1
127 self.error(msg, *args, **kwargs)
131 def critical(self, msg, *args, **kwargs):
133 if self.isEnabledFor(logging.CRITICAL):
134 self._log(logging.CRITICAL, msg, args, **kwargs)
138 def error(self, msg, *args, **kwargs):
139 if self.isEnabledFor(logging.ERROR):
140 self._log(logging.ERROR, msg, args, **kwargs)
144 def log(self, level, msg, *args, **kwargs):
146 if not isinstance(level, int):
147 if logging.raiseExceptions:
148 raise TypeError("level must be an integer")
152 if self.isEnabledFor(level):
153 self._log(level, msg, args, **kwargs)
156 def handle(self, record):
158 cmarker = getattr(self, MARKER_TAG, None)
160 if isinstance(cmarker, Marker):
161 setattr(record, MARKER_TAG, cmarker)
163 if (not self.disabled) and self.filter(record):
164 self.callHandlers(record)
167 def findCaller(self, stack_info=False):
169 f = logging.currentframe()
172 rv = "(unkown file)", 0, "(unknow function)"
173 while hasattr(f, "f_code"):
175 filename = os.path.normcase(co.co_filename)
176 # jump through local 'replace' func frame
177 if filename == logging._srcfile or co.co_name == "replace":
180 if sys.version_info > (3, 2):
184 sio.write("Stack (most recent call last):\n")
185 traceback.print_stack(f, file=sio)
186 sinfo = sio.getvalue()
187 if sinfo[-1] == '\n':
190 rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
192 rv = (co.co_filename, f.f_lineno, co.co_name)
199 def patch_loggingMDC():
201 The patch to add MDC ability in logging Record instance at runtime
203 localModule = sys.modules[__name__]
204 for attr in dir(logging.Logger):
205 if attr in _replace_func_name:
206 newfunc = getattr(localModule, attr, None)
208 setattr(logging.Logger, attr, newfunc)