Logging helps you better understand the flow of a program and identify undesirable behavior that you might not anticipate during development. Logs provide insights into the scenarios your application encounters, and you can categorize this information by importance: debug, info, warning, and error. Unlike a stack trace, logs reveal the program’s state before it reaches the line of code where an error occurs, offering far richer context for troubleshooting.
Python includes a logging system in its standard library, making it easy to add logging to your application. However, a detailed log with information from multiple program runs and various modules is only useful if you can efficiently access and search its contents. This post introduces a web-based log browser I developed using Flask and Datatables.js. While I primarily use it with Flask applications, it’s straightforward to adapt for other frameworks like Tkinter — see my previous post on integrating Flask with Tkinter.
The code consists of three key parts: a logger setup function, a log file handling function, and log presentation logic. Let’s start with logger setup. Flask’s default logger is overly verbose and includes internal Flask messages, so I created a custom logger with more control.
def getLogger():
# create logger
logger = logging.getLogger("mylogger")
logger.setLevel(logging.DEBUG)
# create file handler and set level to debug
fh = logging.FileHandler("app.log")
# create formatter
formatter = logging.Formatter('%(asctime)s ; %(levelname)s ; %(message)s')
fh.setFormatter(formatter)
# add fh to logger
logger.addHandler(fh)
return(logger)
In the getLogger function, I define a custom logger named mylogger and use setLevel to set the granularity of captured information. I then specify the log file name and define a clear format for log messages.
The log handling function generates example log messages from hypothetical modules, loads the log file, and passes its contents to a Jinja template.
def index():
logger=getLogger()
logger.debug('debug message - X module')
logger.info('info message - Y module')
logger.warning('warn message - Z module')
logger.error('error message')
logger.critical('critical message')
lines=list()
with open("app.log", 'rt', encoding="utf8", errors="ignore") as csvfile:
rows= csv.reader(csvfile, delimiter=";")
for row in rows:
lines.append(row)
return render_template_string(jinja_template, lines=lines)
I used render_template_string, which is less than ideal for long Jinja templates, but it keeps all the code in a single, copy-pasteable file for quick testing. The Jinja template processes the log lines, with the most critical component being the integration of datatables.js. To use it, you need to:
The complete code is available here: