Skip to content

Commit

Permalink
fix: show app logs when running on gunicorn and change format
Browse files Browse the repository at this point in the history
Set a nice log formatting to all logs, and make them work the same
whether the app is running with gunicorn or the default flask server.

This was not an easy task ^^'. Among the pitfalls:

- the app loggers need to be cleared since the app is created before
  the custom logging config is applied (to avoid duplicates)
- PYTHONUNBUFFERED need to be set in the Docker container since we
  are streaming logs in stdout
- setting the config directly in python doesn't work well when running
  with gunicorn: the first worker uses the new config, but the workers
  spawned afterwards will use the default config. We thus need
  to use the gunicorn.conf.py !
  • Loading branch information
derlin committed Dec 12, 2022
1 parent 92a855e commit 2df4ebf
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ FROM python:3.11-alpine3.17 as final
# number of gunicorn workers to use
ARG WORKERS=1
ENV WORKERS=${WORKERS}
# the logging config outputs to stdout, so we need unbuffered !
ENV PYTHONUNBUFFERED=TRUE

EXPOSE 8080
WORKDIR /app
Expand All @@ -54,6 +56,7 @@ ENV PATH="/app/.venv/bin:$PATH"
COPY --chown=app --from=venv /app/.venv .venv

# gunicorn will automatically find modules in $PWD, and all deps are in venv
COPY gunicorn.conf.py gunicorn.conf.py
COPY rickroll rickroll

# do not use curl, as it would have to be installed only for healthcheck...
Expand Down
8 changes: 8 additions & 0 deletions gunicorn.conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rickroll.log import logging_config
from os import getenv

# Apply custom log configuration when using gunicorn
# Note that if this is done from the app itself, it won't be
# applied to all workers...

logconfig_dict = logging_config(getenv("APP_LOGGING_LEVEL", "INFO").upper())
4 changes: 4 additions & 0 deletions rickroll/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
from .rickroller import RickRoller

app = Flask(__name__, static_folder="assets")
# If we do not clear the default handlers, we will have duplicate logs
# This is because we are in __init__.py, so neither main nor gunicorn
# have run yet (they will set the logging config later)
app.logger.handlers.clear()


@app.errorhandler(Exception)
Expand Down
11 changes: 5 additions & 6 deletions rickroll/__main__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import argparse

from rickroll import app
from rickroll.log import logging_config
from logging.config import dictConfig

if __name__ == "__main__":
# apply logging config manually when not running with gunicorn
dictConfig(logging_config("DEBUG"))

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--port", default="8080")
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("-d", "--debug", action="store_true")
args = parser.parse_args()

app.run(host=args.host, port=args.port, debug=args.debug)


if __name__ == "__main__":
main()
51 changes: 51 additions & 0 deletions rickroll/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This logging configuration should be applied in gunicorn (see gunicorn.conf.py)
# and when running flask directly (see __main__.py).
# NOTES:
# - this requires PYTHONUNBUFFERED=True as we stream to stdout
# - since the app is created in __init__.py (which runs before anything else),
# the default handlers need to be cleared to avoid duplicate logs (app.logger.handlers.clear())


def logging_config(app_log_level):
return {
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"default": {"format": "%(asctime)s %(levelname)8s | %(message)s"},
"app": {
"format": "%(asctime)s %(levelname)8s > %(message)s (%(filename)s:%(lineno)s)"
},
},
"handlers": {
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "default",
"stream": "ext://sys.stdout",
},
"console_app": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "app",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"gunicorn.error": {
"handlers": ["console"],
"level": "INFO",
"propagate": False,
},
"gunicorn.access": {
"handlers": ["console"],
"level": "INFO",
"propagate": False,
},
"rickroll": {
"handlers": ["console_app"],
"level": app_log_level,
"propagate": False,
},
},
"root": {"level": "INFO", "handlers": ["console"]},
}

0 comments on commit 2df4ebf

Please sign in to comment.