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.

Logs browser in Flask
Logs browser in Flask

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:

  1. Add the required JS and CSS dependencies
  2. Create an HTML table
  3. Call the ready() function from the datatables.js library
  4. Pass the table’s identifier to ready()
The ready() function automatically formats the table, adds sortable column headers, and includes a search box. The final result is shown in the figure above.

The complete code is available here: