aboutsummaryrefslogtreecommitdiffstats
path: root/pylog/onaplogging/mdcformatter.py
diff options
context:
space:
mode:
authorEli Halych <illia.halych@t-mobile.pl>2020-09-02 13:10:35 +0000
committerEli Halych <illia.halych@t-mobile.pl>2020-09-07 08:52:24 +0000
commit7035aed700a463fd171807526475baf84c1434e7 (patch)
tree9d0e7c4c8252f431ac3e12c0f5086c1c8b625f79 /pylog/onaplogging/mdcformatter.py
parent314ee85cf7c8a98dd21d5d12e4013b9f742b1012 (diff)
onaplogging: Docstrings, refactor, type hinting
Identify and document functionalities. Describe parameters and their types, exception descriptions and types, extensions, return types and descriptions. Preserve Python 2.7 and 3.x compatibility. Add Python 2.7 to Tox testing. Extract code to utility files. Add properties for readability and maintainability, fix naming conventions. Deprecate old methods and attributes. Issue-ID: REQ-420 Signed-off-by: Eli Halych <illia.halych@t-mobile.pl> Change-Id: I19297e40fad743ec68aa04612ecbb11f61f2abec
Diffstat (limited to 'pylog/onaplogging/mdcformatter.py')
-rw-r--r--pylog/onaplogging/mdcformatter.py241
1 files changed, 155 insertions, 86 deletions
diff --git a/pylog/onaplogging/mdcformatter.py b/pylog/onaplogging/mdcformatter.py
index 4cacbe8..442f0ad 100644
--- a/pylog/onaplogging/mdcformatter.py
+++ b/pylog/onaplogging/mdcformatter.py
@@ -13,26 +13,60 @@
# limitations under the License.
import logging
+from logging import LogRecord
+from typing import Mapping, List, Dict, Callable
+from deprecated import deprecated
+
+from onaplogging.utils.system import is_above_python_2_7, is_above_python_3_2
+from onaplogging.utils.styles import MDC_OPTIONS
+
from .markerFormatter import MarkerFormatter
-from .utils import is_above_python_2_7, is_above_python_3_2
class MDCFormatter(MarkerFormatter):
- """
- A custom MDC formatter to prepare Mapped Diagnostic Context
- to enrich log message.
+ """A custom MDC formatter.
+
+ Prepares Mapped Diagnostic Context to enrich log message. If `fmt` is not
+ supplied, the `style` is used.
+
+ Extends:
+ MarkerFormatter
+ Args:
+ fmt : Built-in format string containing standard Python
+ %-style mapping keys in human-readable format.
+ mdcFmt : MDC format with '{}'-style mapping keys.
+ datefmt : Date format.
+ colorfmt : colored output with an ANSI terminal escape code.
+ style : style mapping keys in Python 3.x.
"""
- def __init__(self, fmt=None, mdcfmt=None,
- datefmt=None, colorfmt=None, style="%"):
- """
- :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
- :param colorfmt: colored output with ANSI escape code on terminal
- :param style: style mapping keys in python3
- """
+ @property
+ def mdc_tag(self):
+ # type: () -> str
+ return self._mdc_tag
+
+ @property
+ def mdcfmt(self):
+ # type: () -> str
+ return self._mdcFmt
+
+ @mdc_tag.setter
+ def mdc_tag(self, value):
+ # type: (str) -> str
+ self._mdc_tag = value
+
+ @mdcfmt.setter
+ def mdcfmt(self, value):
+ # type: (str) -> str
+ self._mdc_tag = value
+
+ def __init__(self,
+ fmt=None, # type: str
+ mdcfmt=None, # type: str
+ datefmt=None, # type: str
+ colorfmt=None, # type: str
+ style="%"): # type: str
+
if is_above_python_3_2():
super(MDCFormatter, self).__init__(fmt=fmt,
datefmt=datefmt,
@@ -43,114 +77,149 @@ class MDCFormatter(MarkerFormatter):
datefmt=datefmt,
colorfmt=colorfmt)
else:
- MarkerFormatter.__init__(self, fmt, datefmt, colorfmt)
+ MarkerFormatter.\
+ __init__(self, fmt, datefmt, colorfmt) # noqa: E122
- self._mdc_tag = "%(mdc)s"
- if self.style == "{":
- self._mdc_tag = "{mdc}"
- elif self.style == "$":
- self._mdc_tag = "${mdc}"
-
- if mdcfmt:
- self._mdcFmt = mdcfmt
- else:
- self._mdcFmt = '{reqeustID}'
+ self._mdc_tag = MDC_OPTIONS[self.style]
+ self._mdcFmt = mdcfmt if mdcfmt else '{reqeustID}'
- def _mdcfmtKey(self):
+ def format(self, record):
+ # type: (LogRecord) -> str
"""
- maximum barce match algorithm to find the mdc key
- :return: key in brace and key not in brace,such as ({key}, key)
+ Find MDCs in a log record's extra field. If the key from mdcFmt
+ doesn't contain MDC, the values will be empty.
+
+ For example:
+ The MDC dict in a logging record is {'key1':'value1','key2':'value2'}.
+ The mdcFmt is '{key1} {key3}'.
+ The output of MDC message is 'key1=value1 key3='.
+
+ Args:
+ record : an instance of a logged event.
+ Returns:
+ str : "colored" text (formatted text).
+ """
+
+ mdc_index = self._fmt.find(self._mdc_tag)
+ if mdc_index == -1:
+ return self._parent_format(record)
+
+ mdc_format_keys, mdc_format_words = self._mdc_format_key()
+
+ if mdc_format_words is None:
+ self._fmt = self._replace_mdc_tag_str("")
+ self._apply_styling()
+
+ return self._parent_format(record)
+
+ res = self._apply_mdc(record, mdc_format_words)
+
+ try:
+ mdc_string = self._replaceStr(keys=mdc_format_keys).format(**res)
+ self._fmt = self._replace_mdc_tag_str(mdc_string)
+ self._apply_styling()
+
+ return self._parent_format(record)
+
+ except KeyError as e:
+ # is there a need for print?
+ print("The mdc key %s format is wrong" % str(e))
+
+ except Exception:
+ raise
+
+ def _mdc_format_key(self):
+ # type: () -> (List, Mapping[str, str])
+ """Maximum (balanced) parantehses matching algorithm for MDC keys.
+
+ Extracts and strips keys and words from a MDC format string. Use this
+ method to find the MDC key.
+
+ Returns:
+ list : list of keys.
+ map object : keys with and without brace, such as ({key}, key).
"""
left = '{'
right = '}'
target = self._mdcFmt
- st = []
+ stack = []
keys = []
+
for index, v in enumerate(target):
if v == left:
- st.append(index)
+ stack.append(index)
elif v == right:
- if len(st) == 0:
+ if len(stack) == 0:
continue
- elif len(st) == 1:
- start = st.pop()
+ elif len(stack) == 1:
+ start = stack.pop()
end = index
keys.append(target[start:end + 1])
- elif len(st) > 0:
- st.pop()
+ elif len(stack) > 0:
+ stack.pop()
keys = list(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):
+ def _replace_string(self, keys):
+ # type: (List[str]) -> str
"""
- 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='
-
+ Removes the first and last characters from each key and assigns not
+ stripped keys.
"""
- mdcIndex = self._fmt.find(self._mdc_tag)
- if mdcIndex == -1:
- if is_above_python_2_7():
- return super(MDCFormatter, self).format(record)
- else:
- return MarkerFormatter.format(self, record)
-
- mdcFmtkeys, mdcFmtWords = self._mdcfmtKey()
-
- if mdcFmtWords is None:
- self._fmt = self._fmt.replace(self._mdc_tag, "")
- if is_above_python_3_2():
- self._style = logging._STYLES[self.style][0](self._fmt)
+ fmt = self._mdcFmt
+ for key in keys:
+ fmt = fmt.replace(key, key[1:-1] + "=" + key)
+ return fmt
- if is_above_python_2_7():
- return super(MDCFormatter, self).format(record)
- else:
- return MarkerFormatter.format(self, record)
+ def _parent_format(self, record):
+ # type: (LogRecord) -> str
+ """Call super class's format based on Python version."""
+ if is_above_python_2_7():
+ return super(MDCFormatter, self).format(record)
+ else:
+ return MarkerFormatter.format(self, record)
+ def _apply_mdc(self, record, mdc_format_words):
+ # type: (LogRecord, Mapping[Callable[[str], str], List]) -> Dict
+ """Apply MDC pamming to the LogRecord record."""
mdc = record.__dict__.get('mdc', None)
res = {}
- for i in mdcFmtWords:
+
+ for i in mdc_format_words:
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(self._mdc_tag, mdcstr)
+ return res
- if is_above_python_3_2():
- self._style = logging._STYLES[self.style][0](self._fmt)
+ def _apply_styling(self):
+ # type: () -> None
+ """Apply styling to the formatter if using Python 3.2+"""
+ if is_above_python_3_2():
+ StylingClass = logging._STYLES[self.style][0](self._fmt)
+ self._style = StylingClass
- if is_above_python_2_7():
- return super(MDCFormatter, self).format(record)
- else:
- return MarkerFormatter.format(self, record)
+ def _replace_mdc_tag_str(self, replacement):
+ # type: (str) -> str
+ """Replace MDC tag in the format string."""
+ return self._fmt.replace(self._mdc_tag, replacement)
- except KeyError as e:
- print("The mdc key %s format is wrong" % str(e))
- except Exception:
- raise
+ @deprecated(reason="Will be replaced. Use _mdc_format_key() instead.")
+ def _mdcfmtKey(self):
+ """See _mdc_format_key()."""
+ return self._mdc_format_key()
+
+ @deprecated(reason="Will be replaced. Use _replace_string(keys) instead.")
+ def _replaceStr(self, keys):
+ """See _replace_string(keys)."""
+ return self._replace_string(keys)