1 # Copyright 2018 ke liang <lokyse@163.com>.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 from logging import LogRecord
17 from typing import Mapping, List, Dict, Callable
18 from deprecated import deprecated
20 from onaplogging.utils.system import is_above_python_2_7, is_above_python_3_2
21 from onaplogging.utils.styles import MDC_OPTIONS
23 from .markerFormatter import MarkerFormatter
26 class MDCFormatter(MarkerFormatter):
27 """A custom MDC formatter.
29 Prepares Mapped Diagnostic Context to enrich log message. If `fmt` is not
30 supplied, the `style` is used.
35 fmt : Built-in format string containing standard Python
36 %-style mapping keys in human-readable format.
37 mdcFmt : MDC format with '{}'-style mapping keys.
38 datefmt : Date format.
39 colorfmt : colored output with an ANSI terminal escape code.
40 style : style mapping keys in Python 3.x.
54 def mdc_tag(self, value):
59 def mdcfmt(self, value):
65 mdcfmt=None, # type: str
66 datefmt=None, # type: str
67 colorfmt=None, # type: str
68 style="%"): # type: str
70 if is_above_python_3_2():
71 super(MDCFormatter, self).__init__(fmt=fmt,
75 elif is_above_python_2_7():
76 super(MDCFormatter, self).__init__(fmt=fmt,
81 __init__(self, fmt, datefmt, colorfmt) # noqa: E122
83 self._mdc_tag = MDC_OPTIONS[self.style]
84 self._mdcFmt = mdcfmt if mdcfmt else '{reqeustID}'
86 def format(self, record):
87 # type: (LogRecord) -> str
89 Find MDCs in a log record's extra field. If the key from mdcFmt
90 doesn't contain MDC, the values will be empty.
93 The MDC dict in a logging record is {'key1':'value1','key2':'value2'}.
94 The mdcFmt is '{key1} {key3}'.
95 The output of MDC message is 'key1=value1 key3='.
98 record : an instance of a logged event.
100 str : "colored" text (formatted text).
103 mdc_index = self._fmt.find(self._mdc_tag)
105 return self._parent_format(record)
107 mdc_format_keys, mdc_format_words = self._mdc_format_key()
109 if mdc_format_words is None:
110 self._fmt = self._replace_mdc_tag_str("")
111 self._apply_styling()
113 return self._parent_format(record)
115 res = self._apply_mdc(record, mdc_format_words)
118 mdc_string = self._replaceStr(keys=mdc_format_keys).format(**res)
119 self._fmt = self._replace_mdc_tag_str(mdc_string)
120 self._apply_styling()
122 return self._parent_format(record)
124 except KeyError as e:
125 # is there a need for print?
126 print("The mdc key %s format is wrong" % str(e))
131 def _mdc_format_key(self):
132 # type: () -> (List, Mapping[str, str])
133 """Maximum (balanced) parantehses matching algorithm for MDC keys.
135 Extracts and strips keys and words from a MDC format string. Use this
136 method to find the MDC key.
140 map object : keys with and without brace, such as ({key}, key).
145 target = self._mdcFmt
149 for index, v in enumerate(target):
157 elif len(stack) == 1:
160 keys.append(target[start:end + 1])
164 keys = list(filter(lambda x: x[1:-1].strip('\n \t ') != "", keys))
168 words = map(lambda x: x[1:-1], keys)
172 def _replace_string(self, keys):
173 # type: (List[str]) -> str
175 Removes the first and last characters from each key and assigns not
180 fmt = fmt.replace(key, key[1:-1] + "=" + key)
183 def _parent_format(self, record):
184 # type: (LogRecord) -> str
185 """Call super class's format based on Python version."""
186 if is_above_python_2_7():
187 return super(MDCFormatter, self).format(record)
189 return MarkerFormatter.format(self, record)
191 def _apply_mdc(self, record, mdc_format_words):
192 # type: (LogRecord, Mapping[Callable[[str], str], List]) -> Dict
193 """Apply MDC pamming to the LogRecord record."""
194 mdc = record.__dict__.get('mdc', None)
197 for i in mdc_format_words:
205 def _apply_styling(self):
207 """Apply styling to the formatter if using Python 3.2+"""
208 if is_above_python_3_2():
209 StylingClass = logging._STYLES[self.style][0](self._fmt)
210 self._style = StylingClass
212 def _replace_mdc_tag_str(self, replacement):
214 """Replace MDC tag in the format string."""
215 return self._fmt.replace(self._mdc_tag, replacement)
217 @deprecated(reason="Will be replaced. Use _mdc_format_key() instead.")
218 def _mdcfmtKey(self):
219 """See _mdc_format_key()."""
220 return self._mdc_format_key()
222 @deprecated(reason="Will be replaced. Use _replace_string(keys) instead.")
223 def _replaceStr(self, keys):
224 """See _replace_string(keys)."""
225 return self._replace_string(keys)