piker/piker/log.py

151 lines
3.7 KiB
Python
Raw Normal View History

2020-11-06 17:23:14 +00:00
# piker: trading gear for hackers
# Copyright (C) 2018-present Tyler Goodlet (in stewardship of piker0)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2018-01-20 18:21:06 +00:00
"""
Log like a forester!
"""
import logging
import json
import reprlib
from typing import (
Callable,
)
import tractor
from pygments import (
highlight,
lexers,
formatters,
)
2018-01-20 18:21:06 +00:00
2020-09-23 00:57:37 +00:00
# Makes it so we only see the full module name when using ``__name__``
# without the extra "piker." prefix.
2022-05-21 15:45:16 +00:00
_proj_name: str = 'piker'
2018-01-20 18:21:06 +00:00
2022-05-21 15:45:16 +00:00
def get_logger(
name: str|None = None,
**tractor_log_kwargs,
2022-05-21 15:45:16 +00:00
) -> logging.Logger:
'''
Return the package log or a sub-logger if a `name=` is provided,
which defaults to the calling module's pkg-namespace path.
See `tractor.log.get_logger()` for details.
2018-01-20 18:21:06 +00:00
'''
pkg_name: str = _proj_name
if (
name
and
pkg_name in name
):
name: str = name.lstrip(f'{_proj_name}.')
return tractor.log.get_logger(
name=name,
pkg_name=pkg_name,
**tractor_log_kwargs,
)
2018-01-20 18:21:06 +00:00
def get_console_log(
level: str|None = None,
name: str|None = None,
pkg_name: str|None = None,
with_tractor_log: bool = False,
**tractor_log_kwargs,
) -> logging.Logger:
'''
Get the package logger and enable a handler which writes to
stderr.
2018-01-20 18:21:06 +00:00
Yeah yeah, i know we can use `DictConfig`.
You do it.. Bp
2018-01-20 18:21:06 +00:00
'''
pkg_name: str = _proj_name
if (
name
and
pkg_name in name
):
name: str = name.lstrip(f'{_proj_name}.')
if with_tractor_log:
t_log = tractor.log.get_console_log(
level=level,
name='tractor', # <- XXX, force root tractor log!
**tractor_log_kwargs,
)
# TODO/ allow only enabling certain tractor sub-logs?
assert t_log.name == 'tractor'
return tractor.log.get_console_log(
level=level,
name=name,
pkg_name=pkg_name,
**tractor_log_kwargs,
)
def colorize_json(
data: dict,
style='algol_nu',
):
'''
Colorize json output using ``pygments``.
'''
formatted_json = json.dumps(
data,
sort_keys=True,
indent=4,
)
return highlight(
formatted_json,
lexers.JsonLexer(),
# likeable styles: algol_nu, tango, monokai
formatters.TerminalTrueColorFormatter(style=style)
)
# TODO, eventually defer to the version in `modden` once
# it becomes a dep!
def mk_repr(
**repr_kws,
) -> Callable[[str], str]:
'''
Allocate and deliver a `repr.Repr` instance with provided input
settings using the std-lib's `reprlib` mod,
* https://docs.python.org/3/library/reprlib.html
------ Ex. ------
An up to 6-layer-nested `dict` as multi-line:
- https://stackoverflow.com/a/79102479
- https://docs.python.org/3/library/reprlib.html#reprlib.Repr.maxlevel
'''
def_kws: dict[str, int] = dict(
indent=2,
maxlevel=6, # recursion levels
maxstring=66, # match editor line-len limit
)
def_kws |= repr_kws
reprr = reprlib.Repr(**def_kws)
return reprr.repr