4cacbe8910f70fdf475764788e09d6058bc3c5f8
[logging-analytics.git] / pylog / onaplogging / mdcformatter.py
1 # Copyright 2018 ke liang <lokyse@163.com>.
2 #
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
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 import logging
16 from .markerFormatter import MarkerFormatter
17 from .utils import is_above_python_2_7, is_above_python_3_2
18
19
20 class MDCFormatter(MarkerFormatter):
21     """
22     A custom MDC formatter to prepare Mapped Diagnostic Context
23     to enrich log message.
24     """
25
26     def __init__(self, fmt=None, mdcfmt=None,
27                  datefmt=None, colorfmt=None, style="%"):
28         """
29         :param fmt: build-in format string contains standard
30                Python %-style mapping keys
31         :param mdcFmt: mdc format with '{}'-style mapping keys
32         :param datefmt: Date format to use
33         :param colorfmt: colored output with ANSI escape code on terminal
34         :param style: style mapping keys in python3
35         """
36         if is_above_python_3_2():
37             super(MDCFormatter, self).__init__(fmt=fmt,
38                                                datefmt=datefmt,
39                                                colorfmt=colorfmt,
40                                                style=style)
41         elif is_above_python_2_7():
42             super(MDCFormatter, self).__init__(fmt=fmt,
43                                                datefmt=datefmt,
44                                                colorfmt=colorfmt)
45         else:
46             MarkerFormatter.__init__(self, fmt, datefmt, colorfmt)
47
48         self._mdc_tag = "%(mdc)s"
49         if self.style == "{":
50             self._mdc_tag = "{mdc}"
51         elif self.style == "$":
52             self._mdc_tag = "${mdc}"
53
54         if mdcfmt:
55             self._mdcFmt = mdcfmt
56         else:
57             self._mdcFmt = '{reqeustID}'
58
59     def _mdcfmtKey(self):
60         """
61          maximum barce match algorithm to find the mdc key
62         :return: key in brace  and key not in brace,such as ({key}, key)
63         """
64
65         left = '{'
66         right = '}'
67         target = self._mdcFmt
68         st = []
69         keys = []
70         for index, v in enumerate(target):
71             if v == left:
72                 st.append(index)
73             elif v == right:
74
75                 if len(st) == 0:
76                     continue
77
78                 elif len(st) == 1:
79                     start = st.pop()
80                     end = index
81                     keys.append(target[start:end + 1])
82                 elif len(st) > 0:
83                     st.pop()
84
85         keys = list(filter(lambda x: x[1:-1].strip('\n \t  ') != "", keys))
86         words = None
87         if keys:
88             words = map(lambda x: x[1:-1], keys)
89
90         return keys, words
91
92     def _replaceStr(self, keys):
93
94         fmt = self._mdcFmt
95         for i in keys:
96             fmt = fmt.replace(i, i[1:-1] + "=" + i)
97
98         return fmt
99
100     def format(self, record):
101         """
102         Find mdcs in log record extra field, if key form mdcFmt dosen't
103         contains mdcs, the values will be empty.
104         :param record: the logging record instance
105         :return:  string
106         for example:
107             the mdcs dict in logging record is
108             {'key1':'value1','key2':'value2'}
109             the mdcFmt is" '{key1} {key3}'
110             the output of mdc message: 'key1=value1 key3='
111
112         """
113         mdcIndex = self._fmt.find(self._mdc_tag)
114         if mdcIndex == -1:
115             if is_above_python_2_7():
116                 return super(MDCFormatter, self).format(record)
117             else:
118                 return MarkerFormatter.format(self, record)
119
120         mdcFmtkeys, mdcFmtWords = self._mdcfmtKey()
121
122         if mdcFmtWords is None:
123             self._fmt = self._fmt.replace(self._mdc_tag, "")
124             if is_above_python_3_2():
125                 self._style = logging._STYLES[self.style][0](self._fmt)
126
127             if is_above_python_2_7():
128                 return super(MDCFormatter, self).format(record)
129             else:
130                 return MarkerFormatter.format(self, record)
131
132         mdc = record.__dict__.get('mdc', None)
133         res = {}
134         for i in mdcFmtWords:
135             if mdc and i in mdc:
136                 res[i] = mdc[i]
137             else:
138                 res[i] = ""
139
140         del mdc
141         try:
142             mdcstr = self._replaceStr(keys=mdcFmtkeys).format(**res)
143             self._fmt = self._fmt.replace(self._mdc_tag, mdcstr)
144
145             if is_above_python_3_2():
146                 self._style = logging._STYLES[self.style][0](self._fmt)
147
148             if is_above_python_2_7():
149                 return super(MDCFormatter, self).format(record)
150             else:
151                 return MarkerFormatter.format(self, record)
152
153         except KeyError as e:
154             print("The mdc key %s format is wrong" % str(e))
155         except Exception:
156             raise