Coverage for gws-app/gws/core/log.py: 28%
116 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Logging facility."""
3import os
4import sys
5import traceback
8class Level:
9 CRITICAL = 50
10 ERROR = 40
11 WARN = 30
12 WARNING = 30
13 INFO = 20
14 DEBUG = 10
15 NOTSET = 0
16 ALL = 0
19def set_level(level: int | str):
20 global _current_level
21 if isinstance(level, int) or level.isdigit():
22 _current_level = int(level)
23 else:
24 _current_level = getattr(Level, level.upper())
27def get_level() -> str:
28 global _current_level
29 for k, n in vars(Level).items():
30 if n == _current_level:
31 return k
32 return 'ALL'
35def log(level: int, msg: str, *args, **kwargs):
36 _raw(level, msg, args, kwargs)
39def critical(msg: str, *args, **kwargs):
40 _raw(Level.CRITICAL, msg, args, kwargs)
43def error(msg: str, *args, **kwargs):
44 _raw(Level.ERROR, msg, args, kwargs)
47def warning(msg: str, *args, **kwargs):
48 _raw(Level.WARNING, msg, args, kwargs)
51def info(msg: str, *args, **kwargs):
52 _raw(Level.INFO, msg, args, kwargs)
55def debug(msg: str, *args, **kwargs):
56 _raw(Level.DEBUG, msg, args, kwargs)
59def exception(msg: str = '', *args, **kwargs):
60 _, exc, _ = sys.exc_info()
61 ls = exception_backtrace(exc)
62 _raw(Level.ERROR, msg or ls[0], args, kwargs)
63 for s in ls[1:]:
64 _raw(Level.ERROR, 'EXCEPTION :: ' + s)
67def if_debug(fn, *args):
68 """If debugging, apply the function to args and log the result."""
70 if Level.DEBUG < _current_level:
71 return
72 try:
73 msg = fn(*args)
74 except Exception as exc:
75 msg = repr(exc)
76 _raw(Level.DEBUG, msg)
79def exception_backtrace(exc: BaseException | None) -> list:
80 """Exception backtrace as a list of strings."""
82 head = _name(exc)
83 messages = []
85 lines = []
86 pfx = ''
88 while exc:
89 subhead = _name(exc)
90 msg = _message(exc)
91 if msg:
92 subhead += ': ' + msg
93 messages.append(msg)
94 if pfx:
95 subhead = pfx + ' ' + subhead
97 lines.append(subhead)
99 for f in traceback.extract_tb(exc.__traceback__, limit=100):
100 lines.append(f' in {f[2]} ({f[0]}:{f[1]})')
102 if exc.__cause__:
103 exc = exc.__cause__
104 pfx = 'caused by'
105 elif exc.__context__:
106 exc = exc.__context__
107 pfx = 'during handling of'
108 else:
109 break
111 if messages:
112 head += ': ' + messages[0]
113 if len(lines) > 1:
114 head += ' ' + lines[1].strip()
116 lines.insert(0, head)
117 return lines
120##
122def _name(exc):
123 typ = type(exc) or Exception
124 # if typ == Error:
125 # return 'Error'
126 name = getattr(typ, '__name__', '')
127 mod = getattr(typ, '__module__', '')
128 if mod in {'exceptions', 'builtins'}:
129 return name
130 return mod + '.' + name
133def _message(exc):
134 try:
135 return repr(exc.args[0])
136 except:
137 return ''
140##
143_current_level = Level.INFO
145_out_stream = sys.stdout
147_PREFIX = {
148 Level.CRITICAL: 'CRITICAL',
149 Level.ERROR: 'ERROR',
150 Level.WARNING: 'WARNING',
151 Level.INFO: 'INFO',
152 Level.DEBUG: 'DEBUG',
153}
156def _raw(level, msg, args=None, kwargs=None):
157 if level < _current_level:
158 return
160 if args:
161 if len(args) == 1 and args[0] and isinstance(args[0], dict):
162 args = args[0]
163 msg = msg % args
165 pid = os.getpid()
166 loc = ' '
167 if _current_level <= Level.DEBUG:
168 stacklevel = kwargs.get('stacklevel', 1) if kwargs else 1
169 loc = ' ' + _location(2 + stacklevel) + ' '
170 pfx = '[' + str(pid) + ']' + loc + _PREFIX[level] + ' :: '
172 try:
173 _out_stream.write(f'{pfx}{msg}\n')
174 except UnicodeEncodeError:
175 _out_stream.write(f'{pfx}{msg!r}\n')
177 _out_stream.flush()
180def _location(stacklevel):
181 frames = traceback.extract_stack()
182 for fname, line, func, text in reversed(frames):
183 if stacklevel == 0:
184 return f'{fname}:{line}'
185 stacklevel -= 1
186 return '???'