Usage Example¶
Vislog (Visual Logger) adds a visual effect to standard logging. It is useful for logging in a function with multiple nested blocks and can help you understand the program’s execution flow.
Create Visual Logger¶
[1]:
import json
import time
from vislog import VisLog, AlignEnum
logger = VisLog(name="vislog_demo")
See VisLog for how to constructing a new logger.
Just Logging¶
Just call .debug("message here"), .info(...), …, .critical(...) to log some message. The | pipe character is for nested log block.
[2]:
logger.info("hello");
[User 2024-06-16 15:06:18] | hello
Change the default pipe character
[3]:
logger.info("hello", pipe="+");
[User 2024-06-16 15:06:18] + hello
Ruler¶
Horizontal ruler, by default it is left aligned
[4]:
logger.ruler("start");
[User 2024-06-16 15:06:18] +----- start ------------------------------------------------------------------+
Ruler can be center aligned
[5]:
logger.ruler("start", align=AlignEnum.middle);
[User 2024-06-16 15:06:18] +----------------------------------- start ------------------------------------+
Ruler can be right aligned
[6]:
logger.ruler("start", align=AlignEnum.right);
[User 2024-06-16 15:06:18] +------------------------------------------------------------------ start -----+
Ruler has many option, you can customize the ruler character.
[7]:
logger.ruler("start", char="=");
[User 2024-06-16 15:06:18] +===== start ==================================================================+
You can customize corner character too.
[8]:
logger.ruler("start", corner="#");
[User 2024-06-16 15:06:19] #----- start ------------------------------------------------------------------#
Multiline Message¶
If the log message across multiple lines, it still looks nice.
[9]:
data = {
"a": {
"b": {
"c": 3
}
}
}
logger.info(json.dumps(data, indent=4));
[User 2024-06-16 15:06:19] | {
[User 2024-06-16 15:06:19] | "a": {
[User 2024-06-16 15:06:19] | "b": {
[User 2024-06-16 15:06:19] | "c": 3
[User 2024-06-16 15:06:19] | }
[User 2024-06-16 15:06:19] | }
[User 2024-06-16 15:06:19] | }
Nested Log Block¶
You can put logic blocks nesting each other to improve readability.
[10]:
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");
[User 2024-06-16 15:06:20] +----- section 1 --------------------------------------------------------------+
[User 2024-06-16 15:06:20] | hello 1
[User 2024-06-16 15:06:20] | +----- section 1.1 ----------------------------------------------------------+
[User 2024-06-16 15:06:20] | | hello 1.1
[User 2024-06-16 15:06:20] | | +----- section 1.1.1 ------------------------------------------------------+
[User 2024-06-16 15:06:20] | | | hello 1.1.1
[User 2024-06-16 15:06:20] | | +----- section 1.1.1 ------------------------------------------------------+
[User 2024-06-16 15:06:20] | +----- section 1.1 ----------------------------------------------------------+
[User 2024-06-16 15:06:20] +----- section 1 --------------------------------------------------------------+
pretty_log Decorator¶
You don’t have to explicitly construct the nested log block. A decorator can help you. It will log a ruler when you enter and exit the function automatically. You can use pretty log decorator to wrap a function to provide this visual effect easily.
[11]:
@logger.pretty_log()
def func1():
time.sleep(1)
logger.info("run func1")
func1()
[User 2024-06-16 15:06:20] +----- Start func1() ----------------------------------------------------------+
[User 2024-06-16 15:06:20] |
[User 2024-06-16 15:06:21] | run func1
[User 2024-06-16 15:06:21] |
[User 2024-06-16 15:06:21] +----- End func1(), elapsed = 1.01 sec ----------------------------------------+
The default enter and exit ruler use the following string template "Start {func_name}()" and "Error {func_name}(), elapsed = {elapsed:.2f} sec", you can customize this.
[12]:
@logger.pretty_log(
start_msg="Start {func_name}(name={name})",
end_msg="End {func_name}(), elapsed = {elapsed:.2f} sec",
)
def func1(name: str):
time.sleep(1)
logger.info(f"run func1: name = {name}")
func1(name="Alice")
[User 2024-06-16 15:06:21] +----- Start func1(name=Alice) ------------------------------------------------+
[User 2024-06-16 15:06:21] |
[User 2024-06-16 15:06:22] | run func1: name = Alice
[User 2024-06-16 15:06:22] |
[User 2024-06-16 15:06:22] +----- End func1(), elapsed = 1.01 sec ----------------------------------------+
pretty_log decorator also automatically add a ruler when raises an error. You can customize the error ruler too.
[14]:
@logger.pretty_log(
error_msg="OPS!",
)
def func1():
time.sleep(1)
raise ValueError("something wrong!")
func1()
[User 2024-06-16 15:06:34] +----- Start func1() ----------------------------------------------------------+
[User 2024-06-16 15:06:34] |
[User 2024-06-16 15:06:35] |
[User 2024-06-16 15:06:35] +----- OPS! -------------------------------------------------------------------+
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[14], line 8
5 time.sleep(1)
6 raise ValueError("something wrong!")
----> 8 func1()
File ~/Documents/GitHub/vislog-project/vislog/impl.py:655, in VisLog.pretty_log.<locals>.deco.<locals>.wrapper(*args, **kwargs)
653 if nest == 0 and (pipe is not None):
654 self._pipe_end(pipe, last_pipe)
--> 655 raise e
657 et = datetime.utcnow()
658 elapsed = (et - st).total_seconds()
File ~/Documents/GitHub/vislog-project/vislog/impl.py:632, in VisLog.pretty_log.<locals>.deco.<locals>.wrapper(*args, **kwargs)
629 self.info("")
631 try:
--> 632 result = func(*args, **kwargs)
633 except Exception as e:
634 et = datetime.utcnow()
Cell In[14], line 6, in func1()
1 @logger.pretty_log(
2 error_msg="OPS!",
3 )
4 def func1():
5 time.sleep(1)
----> 6 raise ValueError("something wrong!")
ValueError: something wrong!
Nest Pretty Log Block.¶
You can nest one function in another too! It automatically gives you a human friendly visual effect.
[15]:
@logger.pretty_log()
def func2():
time.sleep(1)
logger.info("run func2")
@logger.pretty_log()
def func3():
time.sleep(1)
logger.info("run func3")
with logger.nested():
func2()
func3()
[User 2024-06-16 15:06:38] +----- Start func3() ----------------------------------------------------------+
[User 2024-06-16 15:06:38] |
[User 2024-06-16 15:06:39] | run func3
[User 2024-06-16 15:06:39] | +----- Start func2() --------------------------------------------------------+
[User 2024-06-16 15:06:39] | |
[User 2024-06-16 15:06:40] | | run func2
[User 2024-06-16 15:06:40] | |
[User 2024-06-16 15:06:40] | +----- End func2(), elapsed = 1.00 sec --------------------------------------+
[User 2024-06-16 15:06:40] |
[User 2024-06-16 15:06:40] +----- End func3(), elapsed = 2.02 sec ----------------------------------------+
You can customize the pipe character to create a semantic visual effect.
[16]:
@logger.pretty_log(pipe="🏭")
def run_build():
time.sleep(1)
logger.info("run build")
@logger.pretty_log(pipe="🧪")
def run_test():
time.sleep(1)
logger.info("run test")
with logger.nested():
run_build()
@logger.pretty_log(pipe="🚀")
def run_deploy():
time.sleep(1)
logger.info("run deploy")
with logger.nested():
run_test()
run_deploy()
[User 2024-06-16 15:06:40] +----- Start run_deploy() -----------------------------------------------------+
[User 2024-06-16 15:06:40] 🚀
[User 2024-06-16 15:06:41] 🚀 run deploy
[User 2024-06-16 15:06:41] 🚀 +----- Start run_test() -----------------------------------------------------+
[User 2024-06-16 15:06:41] 🚀 🧪
[User 2024-06-16 15:06:42] 🚀 🧪 run test
[User 2024-06-16 15:06:42] 🚀 🧪 +----- Start run_build() --------------------------------------------------+
[User 2024-06-16 15:06:42] 🚀 🧪 🏭
[User 2024-06-16 15:06:43] 🚀 🧪 🏭 run build
[User 2024-06-16 15:06:43] 🚀 🧪 🏭
[User 2024-06-16 15:06:43] 🚀 🧪 +----- End run_build(), elapsed = 1.00 sec --------------------------------+
[User 2024-06-16 15:06:43] 🚀 🧪
[User 2024-06-16 15:06:43] 🚀 +----- End run_test(), elapsed = 2.01 sec -----------------------------------+
[User 2024-06-16 15:06:43] 🚀
[User 2024-06-16 15:06:43] +----- End run_deploy(), elapsed = 3.02 sec -----------------------------------+
start_and_end Decorator¶
start_and_end decorator is just a wrapper of the pretty_log decorator, it provide nice looking emoji for start, end and error. start_and_end is basically a equavilent of:
@pretty_log(
start_emoji="🕑 🟢 Start",
error_emoji="⏰ 🔴 Error",
end_emoji="⏰ 🔴 End",
)
def your_function():
...
[17]:
@logger.start_and_end(
msg="My Function 1",
start_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")
[User 2024-06-16 15:06:43] +----- 🕑 🟢 Start 'My Function 1' ----------------------------------------------+
[User 2024-06-16 15:06:43] 📦
[User 2024-06-16 15:06:44] 📦 alice do something in my func 1
[User 2024-06-16 15:06:44] 📦
[User 2024-06-16 15:06:44] +----- ⏰ 🔴 End 'My Function 1', elapsed = 1.01 sec ----------------------------+
emoji_block Decorator¶
emoji_block decorator is just a wrapper of the start_and_end decorator.
Use
🕑 {emoji}at begin.Use
⏰ ✅ {emoji}at the end if exit successfully.Use
⏰ ❌ {emoji}at the end if raises an error.
[18]:
@logger.emoji_block(
msg="Deploy app {app_name}", # set a semantic title
emoji="🚀",
)
def deploy_app(app_name: str):
logger.info("working ...")
logger.info("done")
deploy_app(app_name="my_app")
[User 2024-06-16 15:06:44] +----- 🕑 🚀 Start 'Deploy app my_app' ------------------------------------------+
[User 2024-06-16 15:06:44] 🚀
[User 2024-06-16 15:06:44] 🚀 working ...
[User 2024-06-16 15:06:44] 🚀 done
[User 2024-06-16 15:06:44] 🚀
[User 2024-06-16 15:06:44] +----- ⏰ ✅ 🚀 End 'Deploy app my_app', elapsed = 0.01 sec ----------------------+
[19]:
@logger.emoji_block(msg="build", emoji="🏭")
def run_build():
time.sleep(1)
logger.info("run build")
@logger.emoji_block(msg="test", emoji="🧪")
def run_test():
time.sleep(1)
logger.info("run test")
with logger.nested():
run_build()
@logger.emoji_block(msg="deploy", emoji="🚀")
def run_deploy():
time.sleep(1)
logger.info("run deploy")
with logger.nested():
run_test()
run_deploy()
[User 2024-06-16 15:06:44] +----- 🕑 🚀 Start 'deploy' -----------------------------------------------------+
[User 2024-06-16 15:06:44] 🚀
[User 2024-06-16 15:06:45] 🚀 run deploy
[User 2024-06-16 15:06:45] 🚀 +----- 🕑 🧪 Start 'test' -----------------------------------------------------+
[User 2024-06-16 15:06:45] 🚀 🧪
[User 2024-06-16 15:06:46] 🚀 🧪 run test
[User 2024-06-16 15:06:46] 🚀 🧪 +----- 🕑 🏭 Start 'build' --------------------------------------------------+
[User 2024-06-16 15:06:46] 🚀 🧪 🏭
[User 2024-06-16 15:06:47] 🚀 🧪 🏭 run build
[User 2024-06-16 15:06:47] 🚀 🧪 🏭
[User 2024-06-16 15:06:47] 🚀 🧪 +----- ⏰ ✅ 🏭 End 'build', elapsed = 1.01 sec ------------------------------+
[User 2024-06-16 15:06:47] 🚀 🧪
[User 2024-06-16 15:06:47] 🚀 +----- ⏰ ✅ 🧪 End 'test', elapsed = 2.02 sec ---------------------------------+
[User 2024-06-16 15:06:47] 🚀
[User 2024-06-16 15:06:47] +----- ⏰ ✅ 🚀 End 'deploy', elapsed = 3.03 sec ---------------------------------+
Integrate with Other Logging Library¶
You can use vislog library with any of your favorite logging library. You just need to set the logger as a custom 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 by default.
[20]:
# use loguru: https://github.com/Delgan/loguru
from loguru import logger as loguru_logger
logger = VisLog(logger=loguru_logger, name="vislog_demo")
@logger.emoji_block(msg="build", emoji="🏭")
def run_build():
time.sleep(1)
logger.info("run build")
@logger.emoji_block(msg="test", emoji="🧪")
def run_test():
time.sleep(1)
logger.info("run test")
with logger.nested():
run_build()
@logger.emoji_block(msg="deploy", emoji="🚀")
def run_deploy():
time.sleep(1)
logger.info("run deploy")
with logger.nested():
run_test()
run_deploy()
2024-06-16 15:57:47.639 | INFO | vislog.impl:ruler:441 - +----- 🕑 🚀 Start 'deploy' -----------------------------------------------------+
2024-06-16 15:57:47.640 | INFO | vislog.impl:_log:318 - 🚀
2024-06-16 15:57:48.645 | INFO | vislog.impl:_log:318 - 🚀 run deploy
2024-06-16 15:57:48.648 | INFO | vislog.impl:ruler:441 - 🚀 +----- 🕑 🧪 Start 'test' -----------------------------------------------------+
2024-06-16 15:57:48.650 | INFO | vislog.impl:_log:318 - 🚀 🧪
2024-06-16 15:57:49.654 | INFO | vislog.impl:_log:318 - 🚀 🧪 run test
2024-06-16 15:57:49.657 | INFO | vislog.impl:ruler:441 - 🚀 🧪 +----- 🕑 🏭 Start 'build' --------------------------------------------------+
2024-06-16 15:57:49.659 | INFO | vislog.impl:_log:318 - 🚀 🧪 🏭
2024-06-16 15:57:50.663 | INFO | vislog.impl:_log:318 - 🚀 🧪 🏭 run build
2024-06-16 15:57:50.666 | INFO | vislog.impl:_log:318 - 🚀 🧪 🏭
2024-06-16 15:57:50.668 | INFO | vislog.impl:ruler:441 - 🚀 🧪 +----- ⏰ ✅ 🏭 End 'build', elapsed = 1.01 sec ------------------------------+
2024-06-16 15:57:50.669 | INFO | vislog.impl:_log:318 - 🚀 🧪
2024-06-16 15:57:50.670 | INFO | vislog.impl:ruler:441 - 🚀 +----- ⏰ ✅ 🧪 End 'test', elapsed = 2.02 sec ---------------------------------+
2024-06-16 15:57:50.671 | INFO | vislog.impl:_log:318 - 🚀
2024-06-16 15:57:50.673 | INFO | vislog.impl:ruler:441 - +----- ⏰ ✅ 🚀 End 'deploy', elapsed = 3.03 sec ---------------------------------+