impl

Enhance the default logger, print visual ascii effect for better readability.

vislog.impl.format_line(msg: str, indent: int = 0, tab: str = '  ', nest: int = 0, _pipes: list[str] | None = None) str[source]

Format message with indentation and nesting.

Parameters:
  • msg – the message to print.

  • indent – the number of tab to indent.

  • tab – the tab character.

  • nest – the current nesting level. when nest = 0, it means there’s no nesting.

Example:

>>> format_line("hello")
'[User] | hello'
>>> format_line("hello", indent=1)
'[User] |   hello'
>>> format_line("hello", nest=1)
'[User] | | hello'
>>> format_line("hello", indent=1, nest=1)
'[User] | |   hello'

Developer Note:

  • _pipes is a first in last out stack data structure that stores

    the list of pipe character for different level of nesting. When nest = 0, there should be only one pipe character in the list. When nest = 1, there should be two pipe characters in the list.

class vislog.impl.AlignEnum(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Enum for aligning text in ruler. See format_ruler().

vislog.impl.format_ruler(msg: str, char: str = '-', align: AlignEnum = AlignEnum.middle, length: int = 80, left_padding: int = 5, right_padding: int = 5, corner: str = '', nest: int = 0, _pipes: list[str] | None = None) str[source]

Format message to shape a horizontal ruler.

Parameters:
  • msg – the message to print

  • char – the character to use as ruler

  • align – left, middle, right alignment of the message

  • length – the total number of character of the ruler

  • left_padding – the number of ruler character to pad on the left

  • right_padding – the number of ruler character to pad on the right

  • corner – the character to use as corner

  • nest – the current nesting level. when nest = 0, it means there’s no nesting.

Example:

>>> format_ruler("Hello", length=40)
'---------------- Hello -----------------'

>>> format_ruler("Hello", length=20)
'------ Hello -------'

>>> format_ruler("Hello", char="=", length=40)
'================ Hello ================='

>>> format_ruler("Hello", corner="+", length=40)
'+--------------- Hello ----------------+'

>>> format_ruler("Hello", align=AlignEnum.left, length=40)
'----- Hello ----------------------------'

>>> format_ruler("Hello", align=AlignEnum.right, length=40)
'---------------------------- Hello -----'

>>> format_ruler("Hello", left_padding=3, align=AlignEnum.left, length=40)
'--- Hello ------------------------------'

>>> format_ruler("Hello", right_padding=3, align=AlignEnum.right, length=40)
'------------------------------ Hello ---'

>>> format_ruler("Hello", right_padding=3, align=AlignEnum.right, length=40, nest=1)
'| ------------------------------ Hello ---'

Developer Note:

  • _pipes is a first in last out stack data structure that stores

    the list of pipe character for different level of nesting. When nest = 0, the ruler should not use any pipe character, so that _pipes should be an empty list. When nest = 1, there should be one pipe characters in the list.

vislog.impl.decohints(decorator: Callable) Callable[source]

fix pycharm type hint bug for decorator.

class vislog.impl.VisLog(logger: Logger | None = None, name: str | None = None, level: int = 20, log_format: str = '[User %(asctime)s] %(message)s', datetime_format: str = '%Y-%m-%d %H:%m:%S', tab: str = '  ', pipe: str = '| ')[source]

A logger that supports nested logging.

Parameters:
  • logger – any logging.Logger() object or any object what support logger.debug("message here"), logger.info(...), logger.warning(...), logger.error(...), logger.critical(...). The visual logger will use this object to log the message. If not provided, a new logger will be created.

  • name – a unique name for the logger.

  • level – logging.INFO, logging.DEBUG, logging.WARNING, logging.ERROR, logging.CRITICAL.

  • log_format – the format of the log message, see https://docs.python.org/3/library/logging.html#formatter-objects for more information.

  • datetime_format – datetime format for the log message, default is %Y-%m-%d %H:%m:%S

  • tab – the indent string,

  • pipe – the pipe character for nested log block, it has to be single character. default is “| “.

pipe(pipe: str | None = None)[source]

Temporarily change the pipe character for nested log block.

Example:

logger.info("a")
with logger.pipe("*"):
    logger.info("b")
    logger.info("c")
logger.info("d")

The output looks like:

[User] | a
[User] * b
[User] * c
[User] | d
debug(msg: str, indent: int = 0, tab: str | None = None, pipe: str | None = None) str[source]

Todo: add docstring

info(msg: str, indent: int = 0, tab: str | None = None, pipe: str | None = None) str[source]

Todo: add docstring

warning(msg: str, indent: int = 0, tab: str | None = None, pipe: str | None = None) str[source]

Todo: add docstring

error(msg: str, indent: int = 0, tab: str | None = None, pipe: str | None = None) str[source]

Todo: add docstring

critical(msg: str, indent: int = 0, tab: str | None = None, pipe: str | None = None) str[source]

Todo: add docstring

ruler(msg: str, char: str = '-', align: AlignEnum = AlignEnum.left, length: int = 80, left_padding: int = 5, right_padding: int = 5, corner: str = '+', pipe: str | None = None, func: Callable | None = None) str[source]

Todo: add docstring

indent(level: int = 1)[source]

A context manager that temporarily increase the indentation level.

Example:

logger.ruler("start test indent")

logger.info("a")

with logger.indent():
    logger.info("b")

    with logger.indent():
        logger.info("c")

    logger.info("d")

logger.info("e")

logger.ruler("end test indent")

The output looks like:

[User] +----- start test indent -----------------------------------+
[User] | a
[User] |   b
[User] |     c
[User] |   d
[User] | e
[User] +----- end test indent -------------------------------------+
nested(pipe: str | None = None)[source]

A context manager that nest logging for one more level.

Example:

logger.ruler("section 1")
logger.info("hello 1")
with logger.nested():
    logger.ruler("section 1.1")
    logger.info("hello 1.1")
    with logger.nested():
        logger.ruler("section 1.1.1")
        logger.info("hello 1.1.1")
        logger.ruler("section 1.1.1")
    logger.ruler("section 1.1")
logger.ruler("section 1")

The output looks like:

[User] +----- section 1 -------------------------------------------+
[User] | hello 1
[User] | +----- section 1.1 ---------------------------------------+
[User] | | hello 1.1
[User] | | +----- section 1.1.1 -----------------------------------+
[User] | | | hello 1.1.1
[User] | | +----- section 1.1.1 -----------------------------------+
[User] | +----- section 1.1 ---------------------------------------+
[User] +----- section 1 -------------------------------------------+
pretty_log(start_msg: str = 'Start {func_name}()', error_msg: str = 'Error {func_name}(), elapsed = {elapsed:.2f} sec', end_msg: str = 'End {func_name}(), elapsed = {elapsed:.2f} sec', char: str = '-', align: AlignEnum = AlignEnum.left, length: int = 80, left_padding: int = 5, right_padding: int = 5, corner: str = '+', nest: int = 0, pipe: str | None = None)[source]

A decorator that pretty print ruler when a function start, error, end.

start_msg, error_msg and end_msg are string template. the {func_name} will become the function you are decorating, the {elapsed} will become the execution time of the function. You can use {elapsed:.2f} to set the precision to two digits. The execution time measurement are not accuracy, it is just an estimation up to three digits precision.

Example:

@nested_logger.pretty_log(nest=1)
def my_func2(name: str):
    time.sleep(1)
    nested_logger.info(f"{name} do something in my func 2")

@nested_logger.pretty_log()
def my_func1(name: str):
    time.sleep(1)
    nested_logger.info(f"{name} do something in my func 1")
    my_func2(name="bob")

my_func1(name="alice")

The output looks like:

[User] +----- Start my_func1() ------------------------------------+
[User] |
[User] | alice do something in my func 1
[User] | +----- Start my_func2() ----------------------------------+
[User] | |
[User] | | bob do something in my func 2
[User] | |
[User] | +----- End my_func2(), elapsed = 1.00 sec ----------------+
[User] |
[User] +----- End my_func1(), elapsed = 2.00 sec ------------------+
Returns:

a decorator that you can put on top of your function

start_and_end(msg: str, start_emoji: str = '🟢', error_emoji: str = '🔴', end_emoji: str = '🟢', pipe: str = '| ')[source]

A simplified version of the pretty_log decorator. Visually print the start and the end of a function.

Example:

@logger.start_and_end(
    msg="My Function 1",
    start_emoji="🟢",
    error_emoji="🔴",
    end_emoji="🟢",
    pipe="📦",
)
def my_func1(name: str):
    time.sleep(1)
    logger.info(f"{name} do something in my func 1")

my_func1(name="alice")

The output looks like:

[User] +----- 🕑 🟢 Start 'My Function 1' --------------------------+
[User] 📦
[User] 📦 alice do something in my func 1
[User] 📦
[User] +----- ⏰ 🟢 End 'My Function 1', elapsed = 1.01 sec --------+
Parameters:
  • msg – indicate the name of the function

  • start_emoji – custom emoji for the start message

  • end_emoji – custom emoji for the end message

  • pipe – custom pipe character

Returns:

a decorator that you can put on top of your function

emoji_block(msg: str, emoji: str)[source]

A simplified version of the start_and_end decorator. Use emoji to Visually print the function logic block

Example:

@logger.emoji_block(
    msg="Deploy app {app_name}",
    emoji="🚀",
)
def deploy_app(app_name: str):
    logger.info("working ...")
    logger.info("done")

deploy_app(app_name="my_app")

The output looks like:

[User] +----- 🕑 🚀 Start 'Deploy app my_app' ----------------------+
[User] 🚀
[User] 🚀 working ...
[User] 🚀 done
[User] 🚀
[User] +----- ⏰ ✅ 🚀 End 'Deploy app my_app', elapsed = 1.01 sec -+
Parameters:
  • msg – indicate the name of the function

  • emoji – custom emoji for the visual effect

Returns:

a decorator that you can put on top of your function

disabled(disable: bool = True)[source]

Temporarily disable the logger. This is useful when you want to disable the logger without manually remove the logger.debug(...) code. For example, you can use logger in your unit test for debug, and then use this context manager to disable the logger when you run the test in CI.

Example:

# content of test.py

def _test1():
    logger.info(...)

def _test2():
    logger.info(...)

def test_all():
    with logger.disabled(
        disable=True, # this will disable all log
        disable=False, # this will show log
    ):
        _test1()
        _test2()

Note

This method only works when your logger is automatically created by vislog, or it is a logging.Logger.