Submit python logging library seed code
[logging-analytics.git] / pylog / onaplogging / mdcContext.py
diff --git a/pylog/onaplogging/mdcContext.py b/pylog/onaplogging/mdcContext.py
new file mode 100644 (file)
index 0000000..8162b50
--- /dev/null
@@ -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)