summaryrefslogtreecommitdiffstats
path: root/pylog/onaplogging/mdcContext.py
diff options
context:
space:
mode:
Diffstat (limited to 'pylog/onaplogging/mdcContext.py')
-rw-r--r--pylog/onaplogging/mdcContext.py166
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)