Excerpt
A very useful log module to allow redirecting logs to another file in runtime.
KB4IT needs this feature to redirect logs to the right file when a new instance is executed.
Features
- Log redirection: when KB4IT is executed, it doesn't know which application is going to be executed, so the first logs are sent to a temporary log directory. Once the backend is aware about which app (or project) is started, temporary logs are copied to the right destination and the log module redirects the logging there. In this way, the user have the complete trace.
- Distinct levels for console and file handlers: Console handler (the output displayed in the workflow) is set by default to level INFO (minimum). For troubleshooting purposes, the whole trace with level DEBUG is sent to the log file.
Run kb4it -L INFO build config/blog.json -w 8
INFO | 99 | Workflow | 11/01/2026 12:38:45.751 | Building a website for repository 't00mterías'
INFO | 100 | Workflow | 11/01/2026 12:38:45.751 | Using theme 'blog'
INFO | 101 | Workflow | 11/01/2026 12:38:45.751 | Check environment
INFO | 103 | Workflow | 11/01/2026 12:38:45.760 | Allow theme to generate sources
INFO | 106 | Workflow | 11/01/2026 12:38:45.760 | Get source documents
WARNING | 352 | Backend | 11/01/2026 12:38:45.760 | - Added missing 'About App' to your sources
INFO | 108 | Workflow | 11/01/2026 12:38:45.761 | Preprocessing
INFO | 110 | Workflow | 11/01/2026 12:38:45.844 | Backend processing
INFO | 112 | Workflow | 11/01/2026 12:38:45.877 | Theme processing
INFO | 114 | Workflow | 11/01/2026 12:38:45.927 | Compilation
INFO | 116 | Workflow | 11/01/2026 12:38:52.129 | Clean up target
INFO | 118 | Workflow | 11/01/2026 12:38:52.161 | Refresh target
INFO | 120 | Workflow | 11/01/2026 12:38:52.306 | Theme post activities
INFO | 123 | Workflow | 11/01/2026 12:38:52.467 | Repository website built
INFO | 124 | Workflow | 11/01/2026 12:38:52.467 | URL: /home/runner/work/blog/blog/docs/index.html
INFO | 125 | Workflow | 11/01/2026 12:38:52.467 | Full log: /home/runner/work/blog/blog/var/log/homerunnerworkblogblogsource.log
INFO | 126 | Workflow | 11/01/2026 12:38:52.467 | The End
Code
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Log module.
File: log.py
Author: Tomás Vírseda
License: GPL v3
"""
import logging
_PATTERN = (
"%(levelname)10s | %(lineno)4d | %(name)-20s | "
"%(asctime)s.%(msecs)03d | %(message)s"
)
_DATEFMT = "%d/%m/%Y %H:%M:%S"
def setup_logging(
level: str = "INFO",
logfile: str | None = None,
):
"""
Configure root logger once.
"""
if level is not None:
level_dict = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL
}
severity = level_dict.get(level, logging.DEBUG)
else:
severity = logging.INFO
root = logging.getLogger()
root.setLevel(logging.DEBUG)
if root.handlers:
return # Already configured
formatter = logging.Formatter(_PATTERN, datefmt=_DATEFMT)
# Console handler
console = logging.StreamHandler()
console.setFormatter(formatter)
console.setLevel(severity)
root.addHandler(console)
# File handler
file_handler = logging.FileHandler(logfile, mode="w")
file_handler.setFormatter(formatter)
root.addHandler(file_handler)
def get_logger(name: str) -> logging.Logger:
"""
Return a named logger.
"""
return logging.getLogger(name)
def redirect_logs(logfile: str):
"""
Redirect logging to a new file at runtime.
"""
root = logging.getLogger()
formatter = logging.Formatter(_PATTERN, datefmt=_DATEFMT)
# Remove only existing FileHandlers
for handler in root.handlers[:]:
if isinstance(handler, logging.FileHandler):
handler.flush()
handler.close()
root.removeHandler(handler)
# Add new file handler
file_handler = logging.FileHandler(logfile, mode="a")
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)
root.addHandler(file_handler)