/
logger.py
168 lines (123 loc) · 3.95 KB
/
logger.py
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
157
158
159
160
161
162
163
164
165
166
167
168
"""The Devito logger."""
import logging
import sys
from contextlib import contextmanager
__all__ = ('set_log_level', 'set_log_noperf', 'is_log_enabled_for', 'switch_log_level',
'log', 'warning', 'error', 'perf', 'hint',
'RED', 'GREEN', 'BLUE')
logger = logging.getLogger('Devito')
stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)
# Add extra logging levels (note: INFO has value=20, WARNING has value=30)
DEBUG = logging.DEBUG
PERF = 19
INFO = logging.INFO
WARNING = logging.WARNING
ERROR = logging.ERROR
CRITICAL = logging.CRITICAL
logging.addLevelName(PERF, "PERF")
logger_registry = {
'DEBUG': DEBUG,
'PERF': PERF,
'INFO': INFO,
'WARNING': WARNING,
'ERROR': ERROR,
'CRITICAL': CRITICAL
}
NOCOLOR = '%s'
RED = '\033[1;37;31m%s\033[0m'
BLUE = '\033[1;37;34m%s\033[0m'
GREEN = '\033[1;37;32m%s\033[0m'
COLORS = {
DEBUG: NOCOLOR,
PERF: GREEN,
INFO: NOCOLOR,
WARNING: BLUE,
ERROR: RED,
CRITICAL: RED
}
def _set_log_level(level):
"""
Set the level of the Devito logger.
"""
if level not in logger_registry:
raise ValueError("Illegal logging level %s" % level)
logger.setLevel(level)
return level
def set_log_level(level, comm=None):
"""
Set the level of the Devito logger.
Parameters
----------
level : int
The logging level. Accepted values are: ``DEBUG, PERF, INFO, WARNING,
ERROR, CRITICAL``.
comm : MPI communicator, optional
An MPI communicator the logger should be collective over. If provided, only
rank-0 on that communicator will write to the registered handlers, other
ranks will use a `logging.NullHandler`. By default, ``comm`` is set
to ``None``, so all ranks will use the default handlers. This could be
used, for example, if one wants to log to one file per rank.
"""
from devito import configuration
if comm is not None and configuration['mpi']:
if comm.rank != 0:
logger.removeHandler(stream_handler)
logger.addHandler(logging.NullHandler())
else:
logger.addHandler(stream_handler)
# Triggers a callback to `_set_log_level`
configuration['log-level'] = level
class switch_log_level(object):
"""
A context manager to temporarily change MPI logging.
"""
def __init__(self, comm):
from devito import configuration
self.level = configuration['log-level']
self.comm = comm
def __enter__(self):
# Limit logging to rank 0
set_log_level(self.level, self.comm)
def __exit__(self, *args):
set_log_level(self.level)
def set_log_noperf():
"""Do not print performance-related messages."""
logger.setLevel(WARNING)
def is_log_enabled_for(level):
"""
Wrapper around `logging.isEnabledFor`. Indicates if a message of severity
level would be processed by this logger.
"""
return logger.isEnabledFor(logger_registry[level])
def log(msg, level=INFO, *args, **kwargs):
"""
Wrapper of the main Python's logging function. Print 'msg % args' with
the severity 'level'.
Parameters
----------
msg : str
The message to be printed.
level : int
The logging level. Accepted values are: ``DEBUG, PERF, INFO, WARNING,
ERROR, CRITICAL``.
"""
color = COLORS[level] if sys.stdout.isatty() and sys.stderr.isatty() else '%s'
logger.log(level, color % msg, *args, **kwargs)
def info(msg, *args, **kwargs):
log(msg, INFO, *args, **kwargs)
def perf(msg, *args, **kwargs):
log(msg, PERF, *args, **kwargs)
def hint(msg, *args, **kwargs):
log("Hint: %s" % msg, PERF, *args, **kwargs)
def warning(msg, *args, **kwargs):
log(msg, WARNING, *args, **kwargs)
def error(msg, *args, **kwargs):
log(msg, ERROR, *args, **kwargs)
def debug(msg, *args, **kwargs):
log(msg, DEBUG, *args, **kwargs)
@contextmanager
def bar():
log('='*89, INFO)
yield
log('='*89, INFO)