diff options
Diffstat (limited to 'pylog/onaplogging/mdcContext.py')
-rw-r--r-- | pylog/onaplogging/mdcContext.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/pylog/onaplogging/mdcContext.py b/pylog/onaplogging/mdcContext.py new file mode 100644 index 0000000..8162b50 --- /dev/null +++ b/pylog/onaplogging/mdcContext.py @@ -0,0 +1,166 @@ +# Copyright (c) 2018 VMware, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + + +import logging +import threading +import os +import sys +import functools + + +__all__ = ['patch_loggingMDC', 'MDC'] + +_replace_func_name = ['info', 'critical', 'fatal', 'debug', + 'error', 'warn', 'warning', 'findCaller'] + + +class MDCContext(threading.local): + """ + A Thread local instance to storage mdc values + """ + def __init__(self): + + super(MDCContext, self).__init__() + self._localDict = {} + + def get(self, key): + + return self._localDict.get(key, None) + + def put(self, key, value): + + self._localDict[key] = value + + def remove(self, key): + + if key in self.localDict: + del self._localDict[key] + + def clear(self): + + self._localDict.clear() + + def result(self): + + return self._localDict + + def isEmpty(self): + + return self._localDict == {} or self._localDict is None + + +MDC = MDCContext() + + +def fetchkeys(func): + + @functools.wraps(func) + def replace(*args, **kwargs): + kwargs['extra'] = _getmdcs(extra=kwargs.get('extra', None)) + func(*args, **kwargs) + return replace + + +def _getmdcs(extra=None): + """ + Put mdc dict in logging record extra filed with key 'mdc' + :param extra: dict + :return: mdc dict + """ + if MDC.isEmpty(): + return + + mdc = MDC.result() + + if extra is not None: + for key in extra: + # make sure extra key dosen't override mdckey + if key in mdc or key == 'mdc': + raise KeyError("Attempt to overwrite %r in MDC" % key) + else: + extra = {} + + extra['mdc'] = mdc + del mdc + return extra + + +@fetchkeys +def info(self, msg, *args, **kwargs): + + if self.isEnabledFor(logging.INFO): + self._log(logging.INFO, msg, args, **kwargs) + + +@fetchkeys +def debug(self, msg, *args, **kwargs): + + if self.isEnabledFor(logging.DEBUG): + self._log(logging.DEBUG, msg, args, **kwargs) + + +@fetchkeys +def warning(self, msg, *args, **kwargs): + if self.isEnabledFor(logging.WARNING): + self._log(logging.WARNING, msg, args, **kwargs) + + +@fetchkeys +def exception(self, msg, *args, **kwargs): + + kwargs['exc_info'] = 1 + self.error(msg, *args, **kwargs) + + +@fetchkeys +def critical(self, msg, *args, **kwargs): + + if self.isEnabledFor(logging.CRITICAL): + self._log(logging.CRITICAL, msg, args, **kwargs) + + +@fetchkeys +def error(self, msg, *args, **kwargs): + if self.isEnabledFor(logging.ERROR): + self._log(logging.ERROR, msg, args, **kwargs) + + +def findCaller(self): + + f = logging.currentframe() + if f is not None: + f = f.f_back + rv = "(unkown file)", 0, "(unknow function)" + while hasattr(f, "f_code"): + co = f.f_code + filename = os.path.normcase(co.co_filename) + # jump through local 'replace' func frame + if filename == logging._srcfile or co.co_name == "replace": + f = f.f_back + continue + rv = (co.co_filename, f.f_lineno, co.co_name) + break + + return rv + + +def patch_loggingMDC(): + """ + The patch to add MDC ability in logging Record instance at runtime + """ + localModule = sys.modules[__name__] + for attr in dir(logging.Logger): + if attr in _replace_func_name: + newfunc = getattr(localModule, attr, None) + if newfunc: + setattr(logging.Logger, attr, newfunc) |