Submit python logging library seed code
[logging-analytics.git] / pylog / onaplogging / mdcformatter.py
diff --git a/pylog/onaplogging/mdcformatter.py b/pylog/onaplogging/mdcformatter.py
new file mode 100644 (file)
index 0000000..f63ec94
--- /dev/null
@@ -0,0 +1,123 @@
+# 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
+
+
+class MDCFormatter(logging.Formatter):
+    """
+    A custom MDC formatter to prepare Mapped Diagnostic Context
+    to enrich log message.
+    """
+
+    def __init__(self, fmt=None, mdcfmt=None, datefmt=None):
+        """
+        :param fmt: build-in format string contains standard
+               Python %-style mapping keys
+        :param mdcFmt: mdc format with '{}'-style mapping keys
+        :param datefmt: Date format to use
+        """
+
+        super(MDCFormatter, self).__init__(fmt=fmt, datefmt=datefmt)
+        self._tmpfmt = self._fmt
+        if mdcfmt:
+            self._mdcFmt = mdcfmt
+        else:
+            self._mdcFmt = '{reqeustID}'
+
+    def _mdcfmtKey(self):
+        """
+         maximum barce match algorithm to find the mdc key
+        :return: key in brace  and key not in brace,such as ({key}, key)
+        """
+
+        left = '{'
+        right = '}'
+        target = self._mdcFmt
+        st = []
+        keys = []
+        for index, v in enumerate(target):
+            if v == left:
+                st.append(index)
+            elif v == right:
+
+                if len(st) == 0:
+                    continue
+
+                elif len(st) == 1:
+                    start = st.pop()
+                    end = index
+                    keys.append(target[start:end + 1])
+                elif len(st) > 0:
+                    st.pop()
+
+        keys = filter(lambda x: x[1:-1].strip('\n \t  ') != "", keys)
+        words = None
+        if keys:
+            words = map(lambda x: x[1:-1], keys)
+
+        return keys, words
+
+    def _replaceStr(self, keys):
+
+        fmt = self._mdcFmt
+        for i in keys:
+            fmt = fmt.replace(i, i[1:-1] + "=" + i)
+
+        return fmt
+
+    def format(self, record):
+        """
+        Find mdcs in log record extra field, if key form mdcFmt dosen't
+        contains mdcs, the values will be empty.
+        :param record: the logging record instance
+        :return:  string
+        for example:
+            the mdcs dict in logging record is
+            {'key1':'value1','key2':'value2'}
+            the mdcFmt is" '{key1} {key3}'
+            the output of mdc message: 'key1=value1 key3='
+
+        """
+        mdcIndex = self._fmt.find('%(mdc)s')
+        if mdcIndex == -1:
+            return super(MDCFormatter, self).format(record)
+
+        mdcFmtkeys, mdcFmtWords = self._mdcfmtKey()
+        if mdcFmtWords is None:
+
+            self._fmt = self._fmt.replace("%(mdc)s", "")
+            return super(MDCFormatter, self).format(record)
+
+        mdc = record.__dict__.get('mdc', None)
+        res = {}
+        for i in mdcFmtWords:
+            if mdc and i in mdc:
+                res[i] = mdc[i]
+            else:
+                res[i] = ""
+
+        del mdc
+        try:
+            mdcstr = self._replaceStr(keys=mdcFmtkeys).format(**res)
+            self._fmt = self._fmt.replace("%(mdc)s", mdcstr)
+            s = super(MDCFormatter, self).format(record)
+            return s
+
+        except KeyError as e:
+            print ("The mdc key %s format is wrong" % e.message)
+        except Exception:
+            raise
+
+        finally:
+            # reset fmt format
+            self._fmt = self._tmpfmt