Make lib.App logging more customizable #55
3 changed files with 127 additions and 56 deletions
|
|
@ -9,7 +9,19 @@ from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
|
||||||
from typing import Any, cast, override, TYPE_CHECKING
|
from typing import Any, cast, override, TYPE_CHECKING
|
||||||
|
|
||||||
from .AsyncRunner import AsyncRunner
|
from .AsyncRunner import AsyncRunner
|
||||||
from .log import DEBUG, ERR, NOTICE, log, log_m, set_log_flags, set_log_level
|
from .log import (
|
||||||
|
DEBUG,
|
||||||
|
ERR,
|
||||||
|
NOTICE,
|
||||||
|
LogFlag,
|
||||||
|
log,
|
||||||
|
log_m,
|
||||||
|
parse_log_flags,
|
||||||
|
parse_log_level,
|
||||||
|
set_log_flags,
|
||||||
|
set_log_level
|
||||||
|
)
|
||||||
|
|
||||||
from .Types import LoadTypes
|
from .Types import LoadTypes
|
||||||
from .util import pretty_cmd
|
from .util import pretty_cmd
|
||||||
|
|
||||||
|
|
@ -43,6 +55,30 @@ class App: # export
|
||||||
default = None,
|
default = None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _default_log_flags_env(self) -> str:
|
||||||
|
return 'JW_DEFAULT_LOG_FLAGS'
|
||||||
|
|
||||||
|
def _default_log_flags(self, default: LogFlag) -> LogFlag:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _default_log_level_env(self) -> str:
|
||||||
|
return 'JW_DEFAULT_LOG_LEVEL'
|
||||||
|
|
||||||
|
def _default_log_level(self, default: int) -> int:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _default_log_file_env(self) -> str:
|
||||||
|
return 'JW_DEFAULT_LOG_FILE'
|
||||||
|
|
||||||
|
def _default_log_file(self, default: str | None) -> str | None:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _default_show_backtrace_env(self) -> str:
|
||||||
|
return 'JW_DEFAULT_SHOW_BACKTRACE'
|
||||||
|
|
||||||
|
def _default_show_backtrace(self, default: bool) -> bool:
|
||||||
|
return default
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
description: str = '',
|
description: str = '',
|
||||||
|
|
@ -113,20 +149,25 @@ class App: # export
|
||||||
|
|
||||||
self.__args: Namespace | None = None
|
self.__args: Namespace | None = None
|
||||||
self.__cmdline: str | None = None
|
self.__cmdline: str | None = None
|
||||||
self.__default_log_flags: str = os.getenv(
|
|
||||||
'JW_DEFAULT_LOG_FLAGS', default = 'stderr,position,prio,color'
|
self.__default_log_flags = self._default_log_flags(
|
||||||
|
LogFlag.STDERR | LogFlag.POSITION | LogFlag.PRIO | LogFlag.COLOR
|
||||||
)
|
)
|
||||||
self.__default_log_level: str | int | None = os.getenv(
|
if (env := os.getenv(self._default_log_flags_env(), None)) is not None:
|
||||||
'JW_DEFAULT_LOG_LEVEL', default = NOTICE
|
self.__default_log_flags = parse_log_flags(env)
|
||||||
)
|
|
||||||
self.__default_log_file: str | None = os.getenv(
|
self.__default_log_level = self._default_log_level(NOTICE)
|
||||||
'JW_DEFAULT_LOG_FILE', default = None
|
if (env := os.getenv(self._default_log_level_env(), None)) is not None:
|
||||||
)
|
self.__default_log_level = parse_log_level(env)
|
||||||
backtrace: str | bool = os.getenv('JW_DEFAULT_SHOW_BACKTRACE', False)
|
|
||||||
self.__back_trace = isinstance(backtrace, str) and backtrace.lower() in {
|
self.__default_log_file = self._default_log_file(None)
|
||||||
'1',
|
if (env := os.getenv(self._default_log_file_env(), None)) is not None:
|
||||||
'true',
|
self.__default_log_file = env
|
||||||
}
|
|
||||||
|
self.__back_trace = self._default_show_backtrace(False)
|
||||||
|
if (env := os.getenv(self._default_show_backtrace_env())) is not None:
|
||||||
|
self.__back_trace = env.lower() in ['1', 'true']
|
||||||
|
|
||||||
set_log_flags(self.__default_log_flags)
|
set_log_flags(self.__default_log_flags)
|
||||||
set_log_level(self.__default_log_level)
|
set_log_level(self.__default_log_level)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import syslog
|
import syslog
|
||||||
|
|
||||||
|
from enum import Flag, auto
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast, override
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import io
|
import io
|
||||||
|
|
@ -81,16 +83,44 @@ CONSOLE_FONT_WHITE = '\033[37m'
|
||||||
CONSOLE_FONT_BLINK = '\033[5m'
|
CONSOLE_FONT_BLINK = '\033[5m'
|
||||||
CONSOLE_FONT_OFF = '\033[m'
|
CONSOLE_FONT_OFF = '\033[m'
|
||||||
|
|
||||||
f_position = 'position'
|
# -- ytools compatible log flags
|
||||||
f_module = 'module'
|
#syslog
|
||||||
f_date = 'date'
|
#console
|
||||||
f_stderr = 'stderr'
|
#color
|
||||||
f_stdout = 'stdout'
|
#prio
|
||||||
f_prio = 'prio'
|
#position
|
||||||
f_color = 'color'
|
#ide
|
||||||
f_default = [ f_position, f_stderr, f_prio, f_color ]
|
#trace_rename_thread_to_shorter
|
||||||
|
#trace_rename_thread_to_longer
|
||||||
|
#trace_inout
|
||||||
|
#skip_openlog
|
||||||
|
#id
|
||||||
|
#date
|
||||||
|
#pid
|
||||||
|
#highlight_first_error
|
||||||
|
|
||||||
|
class LogFlag(Flag):
|
||||||
|
POSITION = auto()
|
||||||
|
MODULE = auto()
|
||||||
|
DATE = auto()
|
||||||
|
STDERR = auto()
|
||||||
|
STDOUT = auto()
|
||||||
|
PRIO = auto()
|
||||||
|
COLOR = auto()
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
def _get_name(f: LogFlag) -> str:
|
||||||
|
return f.name or ''
|
||||||
|
|
||||||
|
return ','.join(
|
||||||
|
f.name.lower()
|
||||||
|
for f in sorted(self, key = _get_name)
|
||||||
|
if f.name
|
||||||
|
)
|
||||||
|
|
||||||
|
_flags = LogFlag.POSITION | LogFlag.STDERR | LogFlag.PRIO | LogFlag.COLOR
|
||||||
|
|
||||||
_flags = set(f_default)
|
|
||||||
_log_prefix = ''
|
_log_prefix = ''
|
||||||
_clean_log_prefix = ''
|
_clean_log_prefix = ''
|
||||||
_file_name_len = 20
|
_file_name_len = 20
|
||||||
|
|
@ -181,6 +211,8 @@ def log_m(prio: int, *args: Any, **kwargs: Any) -> None: # export
|
||||||
if isinstance(a, list):
|
if isinstance(a, list):
|
||||||
margs += '\n'.join([str(elem) for elem in a])
|
margs += '\n'.join([str(elem) for elem in a])
|
||||||
continue
|
continue
|
||||||
|
if a == '':
|
||||||
|
continue
|
||||||
margs += ' ' + str(a)
|
margs += ' ' + str(a)
|
||||||
if 'caller' not in kwargs:
|
if 'caller' not in kwargs:
|
||||||
caller = get_caller_pos(1)
|
caller = get_caller_pos(1)
|
||||||
|
|
@ -204,43 +236,44 @@ def log( # export
|
||||||
color_on = ''
|
color_on = ''
|
||||||
color_off = ''
|
color_off = ''
|
||||||
|
|
||||||
if f_date in _flags:
|
if LogFlag.DATE & _flags:
|
||||||
msg += datetime.now().strftime("%b %d %H:%M:%S.%f ")
|
msg += datetime.now().strftime("%b %d %H:%M:%S.%f") + ' '
|
||||||
|
|
||||||
if f_prio in _flags:
|
if LogFlag.PRIO & _flags:
|
||||||
msg += _short_prio_str[prio] + ' '
|
msg += _short_prio_str[prio] + ' '
|
||||||
|
|
||||||
if f_position in _flags:
|
if LogFlag.POSITION & _flags:
|
||||||
|
|
||||||
if 'caller' in kwargs:
|
if 'caller' in kwargs:
|
||||||
mod, name, line = kwargs['caller']
|
mod, name, line = kwargs['caller']
|
||||||
else:
|
else:
|
||||||
mod, name, line = get_caller_pos(1)
|
mod, name, line = get_caller_pos(1)
|
||||||
|
|
||||||
if f_module in _flags:
|
if LogFlag.MODULE & _flags:
|
||||||
msg += pad(mod, _module_name_len)
|
msg += pad(mod, _module_name_len)
|
||||||
|
|
||||||
msg += pad(name, _file_name_len) + '[' + pad(str(line), 4, True) + ']'
|
msg += pad(name, _file_name_len) + '[' + pad(str(line), 4, True) + ']'
|
||||||
|
|
||||||
if f_color in _flags:
|
if LogFlag.COLOR & _flags:
|
||||||
color_on, color_off = console_color_chars(prio)
|
color_on, color_off = console_color_chars(prio)
|
||||||
|
|
||||||
margs = ''
|
margs = ''
|
||||||
if len(args):
|
if len(args):
|
||||||
for a in args:
|
margs = ' '.join(str(a) for a in args)
|
||||||
margs += ' ' + str(a)
|
|
||||||
if only_printable:
|
if only_printable:
|
||||||
margs = _special_char_regex.sub(
|
margs = _special_char_regex.sub(
|
||||||
lambda mo: _special_chars[mo.string[mo.start():mo.end()]], margs
|
lambda mo: _special_chars[mo.string[mo.start():mo.end()]], margs
|
||||||
)
|
)
|
||||||
margs = re.sub('[\x01-\x1f]', '.', margs)
|
margs = re.sub('[\x01-\x1f]', '.', margs)
|
||||||
|
if msg and not msg.endswith(' '):
|
||||||
|
margs = ' ' + margs
|
||||||
|
|
||||||
for file in _log_file_streams:
|
for file in _log_file_streams:
|
||||||
print(msg + _clean_log_prefix + margs, file = file)
|
print(msg + _clean_log_prefix + margs, file = file)
|
||||||
|
|
||||||
msg += _log_prefix
|
msg += _log_prefix
|
||||||
|
|
||||||
if not len(msg):
|
if not len(msg) and not len(margs):
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(margs):
|
if len(margs):
|
||||||
|
|
@ -252,10 +285,10 @@ def log( # export
|
||||||
elif _streams:
|
elif _streams:
|
||||||
files = [s.stream for s in _streams.values()]
|
files = [s.stream for s in _streams.values()]
|
||||||
else:
|
else:
|
||||||
if f_stdout in _flags:
|
if LogFlag.STDOUT & _flags:
|
||||||
files.append(sys.stdout)
|
files.append(sys.stdout)
|
||||||
|
|
||||||
if f_stderr in _flags:
|
if LogFlag.STDERR & _flags:
|
||||||
files.append(sys.stderr)
|
files.append(sys.stderr)
|
||||||
|
|
||||||
if not len(files):
|
if not len(files):
|
||||||
|
|
@ -307,30 +340,27 @@ def set_log_level(level: str | int | None = None) -> int:
|
||||||
def set_level(level: str | int | None = None) -> int: # export
|
def set_level(level: str | int | None = None) -> int: # export
|
||||||
return set_log_level(level)
|
return set_log_level(level)
|
||||||
|
|
||||||
def set_flags(flags: str | None) -> str: # export
|
def parse_flags(flags: str) -> LogFlag:
|
||||||
global _flags
|
ret: LogFlag = LogFlag(0)
|
||||||
ret = ','.join(_flags)
|
s = set(flags.split(','))
|
||||||
if flags is not None:
|
for f in LogFlag:
|
||||||
_flags = set(flags.split(','))
|
assert f.name is not None
|
||||||
|
if f.name.lower() in s:
|
||||||
|
ret |= f
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def set_log_flags(flags: str) -> str:
|
parse_log_flags = parse_flags
|
||||||
return set_flags(flags)
|
|
||||||
|
|
||||||
#syslog
|
def set_flags(flags: str | LogFlag | None) -> LogFlag: # export
|
||||||
#console
|
global _flags
|
||||||
#color
|
if isinstance(flags, str):
|
||||||
#prio
|
flags = parse_flags(flags)
|
||||||
#position
|
ret = _flags
|
||||||
#ide
|
if flags is not None:
|
||||||
#trace_rename_thread_to_shorter
|
_flags = flags
|
||||||
#trace_rename_thread_to_longer
|
return ret
|
||||||
#trace_inout
|
|
||||||
#skip_openlog
|
set_log_flags = set_flags
|
||||||
#id
|
|
||||||
#date
|
|
||||||
#pid
|
|
||||||
#highlight_first_error
|
|
||||||
|
|
||||||
def append_to_prefix(prefix: str) -> str: # export
|
def append_to_prefix(prefix: str) -> str: # export
|
||||||
global _log_prefix
|
global _log_prefix
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ jw-pkg swiss army knife
|
||||||
|
|
||||||
options:
|
options:
|
||||||
--log-flags LOG_FLAGS
|
--log-flags LOG_FLAGS
|
||||||
Log flags (default: stderr,position,prio,color)
|
Log flags (default: color,position,prio,stderr)
|
||||||
--log-level LOG_LEVEL
|
--log-level LOG_LEVEL
|
||||||
Log level (default: 5)
|
Log level (default: 5)
|
||||||
--log-file LOG_FILE Log file (default: None)
|
--log-file LOG_FILE Log file (default: None)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue