summaryrefslogtreecommitdiffstats
path: root/pylog/onaplogging/mdcformatter.py
blob: 4cacbe8910f70fdf475764788e09d6058bc3c5f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# Copyright 2018 ke liang <lokyse@163.com>.
#
# 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.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
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.
    """

    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
        """
        if is_above_python_3_2():
            super(MDCFormatter, self).__init__(fmt=fmt,
                                               datefmt=datefmt,
                                               colorfmt=colorfmt,
                                               style=style)
        elif is_above_python_2_7():
            super(MDCFormatter, self).__init__(fmt=fmt,
                                               datefmt=datefmt,
                                               colorfmt=colorfmt)
        else:
            MarkerFormatter.__init__(self, fmt, datefmt, colorfmt)

        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}'

    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 = 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):
        """
        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(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)

            if is_above_python_2_7():
                return super(MDCFormatter, self).format(record)
            else:
                return MarkerFormatter.format(self, 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(self._mdc_tag, mdcstr)

            if is_above_python_3_2():
                self._style = logging._STYLES[self.style][0](self._fmt)

            if is_above_python_2_7():
                return super(MDCFormatter, self).format(record)
            else:
                return MarkerFormatter.format(self, record)

        except KeyError as e:
            print("The mdc key %s format is wrong" % str(e))
        except Exception:
            raise