File: logging.py

package info (click to toggle)
tryton-server 7.0.40-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,748 kB
  • sloc: python: 53,502; xml: 5,194; sh: 803; sql: 217; makefile: 28
file content (143 lines) | stat: -rw-r--r-- 4,115 bytes parent folder | download
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
from collections.abc import Iterable, Mapping
from itertools import islice

_MAX_ARGUMENTS = 5
_MAX_ITEMS = 5
_ELLIPSE = '...'
_MAX_STR_LENGTH = 20 - len(_ELLIPSE)
_SENSITIVE_KEYS = {'password', 'token', 'secret'}


def is_sensitive_key(name):
    return any(key in str(name) for key in _SENSITIVE_KEYS)


def items_redacted(mapping):
    for key, value in mapping.items():
        if is_sensitive_key(key):
            yield key, Ellipse()
        else:
            yield key, value


class Ellipse:

    __slots__ = ('text',)

    def __init__(self, text=_ELLIPSE):
        self.text = text

    def __str__(self):
        return self.text

    __repr__ = __str__


class EllipseDict:

    __slots__ = ('items',)

    def __init__(self, items):
        self.items = items

    def __str__(self):
        strings = []
        ellipse = False
        for key, value in self.items:
            if isinstance(key, Ellipse):
                ellipse = True
                break
            strings.append(f'{key!r}: {value!r}')
        if ellipse:
            strings.append(_ELLIPSE)

        s = ', '.join(strings)
        return '{' + s + '}'

    __repr__ = __str__


class format_args:

    __slots__ = ('args', 'kwargs', 'verbose', 'max_args', 'max_items')

    def __init__(self, args, kwargs, verbose=False,
            max_args=_MAX_ARGUMENTS, max_items=_MAX_ITEMS):
        self.args = args
        self.kwargs = kwargs
        self.verbose = verbose
        self.max_args = max_args
        self.max_items = max_items

    def __str__(self):
        _nb_args = self.max_args
        _nb_items = self.max_items

        def _shorten_sequence(value):
            nonlocal _nb_items

            stop = self.max_items + 1 if not self.verbose else None
            for v in islice(value, None, stop):
                if not self.verbose and not _nb_items:
                    yield Ellipse()
                    break
                yield v
                _nb_items -= 1

        def _log_repr(value):
            if isinstance(value, bytes):
                if self.verbose:
                    return value
                return Ellipse(f'<{len(value)} bytes>')
            elif isinstance(value, str):
                if len(value) <= _MAX_STR_LENGTH or self.verbose:
                    return value
                return (value[:_MAX_STR_LENGTH]
                    + (_ELLIPSE if len(value) > _MAX_STR_LENGTH else ''))
            elif isinstance(value, Mapping):
                def shorten(value):
                    for items in _shorten_sequence(items_redacted(value)):
                        if isinstance(items, Ellipse):
                            yield Ellipse(), Ellipse()
                            break
                        key, value = items
                        yield _log_repr(key), _log_repr(value)

                return EllipseDict(shorten(value))
            elif isinstance(value, Iterable):
                return type(value)(_log_repr(v)
                    for v in _shorten_sequence(value))
            else:
                return value

        s = '('

        logged_args = []
        for args in self.args:
            if not _nb_args and not self.verbose:
                logged_args.append(Ellipse())
                break
            _nb_items = self.max_items
            logged_args.append(_log_repr(args))
            _nb_args -= 1
        s += ', '.join(repr(a) for a in logged_args)

        if self.kwargs and (not logged_args
                or not isinstance(logged_args[-1], Ellipse)):
            s += ', ' if self.args and self.kwargs else ''
            logged_kwargs = []
            for key, value in items_redacted(self.kwargs):
                if not _nb_args and not self.verbose:
                    logged_kwargs.append(repr(Ellipse()))
                    break
                _nb_items = self.max_items
                logged_kwargs.append(
                    f'{_log_repr(key)}={_log_repr(value)!r}')
                _nb_args -= 1
            s += ', '.join(logged_kwargs)

        s += ')'
        return s


__all__ = [format_args]