Passed
Push — master ( 5bafa5...cb0f94 )
by Konstantin
02:20
created

ocrd_utils.logging.disableLogging()   A

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nop 0
1
"""
2
Logging setup
3
4
By default: Log with lastResort logger, usually STDERR.
5
6
Logging can be overridden either programmatically in code using the library or by creating one or more of
7
8
- /etc/ocrd_logging.py
9
- $HOME/ocrd_logging.py
10
- $PWD/ocrd_logging.py
11
12
These files will be executed in the context of ocrd/ocrd_logging.py, with `logging` global set.
13
"""
14
# pylint: disable=no-member
15
16
from __future__ import absolute_import
17
from traceback import format_stack
18
19
import logging
20
import logging.config
21
import os
22
23
from .constants import LOG_FORMAT, LOG_TIMEFMT
24
25
__all__ = [
26
    'disableLogging',
27
    'getLevelName',
28
    'getLogger',
29
    'initLogging',
30
    'logging',
31
    'setOverrideLogLevel',
32
]
33
34
_initialized_flag = False
35
_overrideLogLevel = None
36
37
_ocrdLevel2pythonLevel = {
38
    'TRACE': 'DEBUG',
39
    'OFF': 'CRITICAL',
40
    'FATAL': 'ERROR',
41
}
42
43
class PropagationShyLogger(logging.Logger):
44
45
    def addHandler(self, hdlr):
46
        super().addHandler(hdlr)
47
        self.propagate = not self.handlers
48
49
    def removeHandler(self, hdlr):
50
        super().removeHandler(hdlr)
51
        self.propagate = not self.handlers
52
53
logging.setLoggerClass(PropagationShyLogger)
54
logging.getLogger().propagate = False
55
56
def getLevelName(lvl):
57
    """
58
    Get (string) python logging level for (string) spec-defined log level name.
59
    """
60
    lvl = _ocrdLevel2pythonLevel.get(lvl, lvl)
61
    return logging.getLevelName(lvl)
62
63
def setOverrideLogLevel(lvl, silent=False):
64
    """
65
    Override all logger filter levels to include lvl and above.
66
67
68
    - Set root logger level
69
    - iterates all existing loggers and sets their log level to ``NOTSET``.
70
71
    Args:
72
        lvl (string): Log level name.
73
        silent (boolean): Whether to log the override call
74
    """
75
    if lvl is None:
76
        return
77
    root_logger = logging.getLogger('')
78
    if not silent:
79
        root_logger.info('Overriding log level globally to %s', lvl)
80
    lvl = getLevelName(lvl)
81
    global _overrideLogLevel # pylint: disable=global-statement
82
    _overrideLogLevel = lvl
83
    for loggerName in logging.Logger.manager.loggerDict:
84
        logger = logging.Logger.manager.loggerDict[loggerName]
85
        if isinstance(logger, logging.PlaceHolder):
86
            continue
87
        logger.setLevel(logging.NOTSET)
88
    root_logger.setLevel(lvl)
89
90
def getLogger(*args, **kwargs):
91
    """
92
    Wrapper around ``logging.getLogger`` that respects `overrideLogLevel <#setOverrideLogLevel>`_.
93
    """
94
    if not _initialized_flag:
95
        initLogging()
96
        logging.getLogger('').critical('getLogger was called before initLogging. Source of the call:')
97
        for line in [x for x in format_stack(limit=2)[0].split('\n') if x]:
98
            logging.getLogger('').critical(line)
99
    name = args[0]
100
    logger = logging.getLogger(*args, **kwargs)
101
    if _overrideLogLevel and name:
102
        logger.setLevel(logging.NOTSET)
103
    return logger
104
105
def initLogging():
106
    """
107
    Reset root logger, read logging configuration if exists, otherwise use basicConfig
108
    """
109
    global _initialized_flag # pylint: disable=global-statement
110
    if _initialized_flag:
111
        logging.getLogger('').critical('initLogging was called multiple times. Source of latest call:')
112
        for line in [x for x in format_stack(limit=2)[0].split('\n') if x]:
113
            logging.getLogger('').critical(line)
114
115
    logging.disable(logging.NOTSET)
116
    for handler in logging.root.handlers[:]:
117
        logging.root.removeHandler(handler)
118
119
    CONFIG_PATHS = [
120
        os.path.curdir,
121
        os.path.join(os.path.expanduser('~')),
122
        '/etc',
123
    ]
124
    config_file = next((f for f \
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable f does not seem to be defined.
Loading history...
125
            in [os.path.join(p, 'ocrd_logging.conf') for p in CONFIG_PATHS] \
126
            if os.path.exists(f)),
127
            None)
128
    if config_file:
129
        logging.config.fileConfig(config_file)
130
        logging.getLogger('ocrd.logging').debug("Picked up logging config at %s" % config_file)
131
    else:
132
        # Default logging config
133
        logging.basicConfig(level=logging.INFO, format=LOG_FORMAT, datefmt=LOG_TIMEFMT)
134
        logging.getLogger('').setLevel(logging.INFO)
135
        #  logging.getLogger('ocrd.resolver').setLevel(logging.INFO)
136
        #  logging.getLogger('ocrd.resolver.download_to_directory').setLevel(logging.INFO)
137
        #  logging.getLogger('ocrd.resolver.add_files_to_mets').setLevel(logging.INFO)
138
        logging.getLogger('PIL').setLevel(logging.INFO)
139
        # To cut back on the `Self-intersection at or near point` INFO messages
140
        logging.getLogger('shapely.geos').setLevel(logging.ERROR)
141
        logging.getLogger('tensorflow').setLevel(logging.ERROR)
142
143
    if _overrideLogLevel:
144
        logging.getLogger('').setLevel(_overrideLogLevel)
145
146
    _initialized_flag = True
147
148
def disableLogging():
149
    global _initialized_flag # pylint: disable=global-statement
150
    _initialized_flag = False
151
    global _overrideLogLevel # pylint: disable=global-statement
152
    _overrideLogLevel = None
153
    logging.basicConfig(level=logging.CRITICAL)
154
    logging.disable(logging.ERROR)
155
156
# Initializing stream handlers at module level
157
# would cause message output in all runtime contexts,
158
# including those which are already run for std output
159
# (--dump-json, --version, ocrd-tool, bashlib etc).
160
# So this needs to be an opt-in from the CLIs/decorators:
161
#initLogging()
162
# Also, we even have to block log output for libraries
163
# (like matplotlib/tensorflow) which set up logging
164
# themselves already:
165
disableLogging()
166