The Boring Stuff — Flask Logging

Steve Nixon
Tenable TechBlog
Published in
3 min readJun 13, 2019
Photo by Markus Spiske on Unsplash

“Man I hate those blog posts where they make you read all the way through and drop a GitHub link at the bottom”

I hear you.

https://github.com/tenable/flask-logging-demo

Read on for the discussion.

Flask is a great, flexible way to build a web app. Unfortunately, (and fortunately) it is a lot less “batteries included” than its heavier-weight cousin Django. Over the course of a few projects here at Tenable, we’ve developed some tools (The Boring Stuff™) that we’ve found useful and want to share with you.

“Logging in Python is a pain.”
Steve Nixon

There, I’ve said it. It’s a pain and I’m putting it out there at the start. Flask is no different, the default loggers give you minute detail about what Werkzeug is doing but inserting your actual application logs into the stream is a chore, and when you have internal requirements such as separating your application stream from your request stream and logging to a specific format, and making sure the whole thing takes its configuration at start, it gets all the more complicated.

# Default settings - meh[2019-06-12 08:46:44,202] ERROR in app: <Lightsaber noises>
[2019-06-12 08:46:44,202] WARNING in app: General Kenobi
127.0.0.1 - - [12/Jun/2019 08:46:44] "GET / HTTP/1.1" 200 -

We run our web apps in Docker containers so we want our modules to be able to accept configuration from environment variables. Additionally we want to support multiple output modes, and share a common log format.

Enter Flask-Logs, which is our name for the module I’m introducing here. Its features include:

  • Different log streams for requests and application logs
  • Configurable via environment variables (or Flask’s config objects)
  • Support for Stream output for sending logs to stdout
  • Support for WatchedFile and size based rotation of files
  • Support for the Application Factory Flask pattern

Flask-Logs on GitHub

Traceable logs! Less Meh.

Let’s dive into an example. Off the bat, we have two required settings, LOG_TYPE and LOG_LEVEL

“That’s great…but I can already emit logs to stdout, what else you got?”

With the addition of a few more parameters: LOG_DIR APP_LOG_NAME and WWW_LOG_NAME we can start up a watched file handler, which supports logrotate style file rotation.

“OK, fine. But you said you had request logs for me, what happened to those?”

I’m glad you asked.

OK, I’m convinced and willing to subscribe to your ‘zine, but I’m using an Application Factory pattern for my Flask apps, and these examples don’t seem to support me.

Fear not, gentle reader, we have something for you as well.

App Factory Pattern on GitHub

# Keeping the following directory structure in mindlog_demo_factory/
|____> __init__.py
|____> app.py # Factory lives here
|____> extensions.py # Instantiate all our plugins
|____> flask_logs.py # Our logging module
|____> settings.py # Here we configure the app
|____> public/ # Blueprints, views oh my!
| |____> __init__.py
| |____> views.py # Logs happen down here
App Factory Pattern app.py

Now we have a lot more setup up-front, but take a look at our “Public” Blueprint:

Now the app handles our requests in the format we want. Also, our views handling logging through the current_app proxy.

“Hang on a second, you’re hiding something from me”

True. Here’s the settings file for the Application Factory. We send this to the app in the create_app function of app.py.

That’s all there is to it. There are probably a dozen ways to do this same exact thing. Here’s one we settled on, I hope you find it helpful.

Full code available here: https://github.com/tenable/flask-logging-demo

--

--