From 3ef8b9b3c64efa7554d344b9db63123152f34088 Mon Sep 17 00:00:00 2001 From: Daniel Grams Date: Sun, 14 Feb 2021 09:57:57 +0100 Subject: [PATCH] Gunicorn logging #111 --- README.md | 28 ++++++++++++++-------------- entrypoint.sh | 3 +-- gunicorn.conf.py | 19 ++++++++++++++++++- project/__init__.py | 12 ++++++++++-- project/views/root.py | 2 +- project/views/utils.py | 8 ++++---- 6 files changed, 48 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 7e5dab0..e55f979 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ source venv/bin/activate (venv) pip install -r requirements.txt (venv) export DATABASE_URL='postgresql://postgres@localhost/gsevpt' (venv) flask db upgrade -(venv) gunicorn -c gunicorn.conf.py --bind 0.0.0.0:5000 project:app +(venv) gunicorn -c gunicorn.conf.py project:app ``` ## Scheduled/Cron jobs @@ -62,27 +62,27 @@ Create `.env` file in the root directory or pass as environment variables. ### Security -| Variable | Function | -| --- | --- | -| SECRET_KEY | A secret key for verifying the integrity of signed cookies. Generate a nice key using `python3 -c "import secrets; print(secrets.token_urlsafe())"`. | +| Variable | Function | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SECRET_KEY | A secret key for verifying the integrity of signed cookies. Generate a nice key using `python3 -c "import secrets; print(secrets.token_urlsafe())"`. | | SECURITY_PASSWORD_HASH | Bcrypt is set as default SECURITY_PASSWORD_HASH, which requires a salt. Generate a good salt using: `python3 -c "import secrets; print(secrets.SystemRandom().getrandbits(128))"`. | ### Send notifications via Mail -| Variable | Function | -| --- | --- | +| Variable | Function | +| ------------------- | ------------------------------------------ | | MAIL_DEFAULT_SENDER | see | -| MAIL_PASSWORD | " | -| MAIL_PORT | " | -| MAIL_SERVER | " | -| MAIL_USERNAME | " | +| MAIL_PASSWORD | " | +| MAIL_PORT | " | +| MAIL_SERVER | " | +| MAIL_USERNAME | " | ### Misc -| Variable | Function | -| --- | --- | -| CACHE_PATH | Absolute or relative path to root directory for dump and image caching. Default: project/tmp | -| GOOGLE_MAPS_API_KEY | Resolve addresses with Google Maps: API Key with Places API enabled | +| Variable | Function | +| ------------------- | -------------------------------------------------------------------------------------------- | +| CACHE_PATH | Absolute or relative path to root directory for dump and image caching. Default: project/tmp | +| GOOGLE_MAPS_API_KEY | Resolve addresses with Google Maps: API Key with Places API enabled | ## Development diff --git a/entrypoint.sh b/entrypoint.sh index f140dc5..317c0e2 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,5 +6,4 @@ do sleep 2 done -BIND_PORT=${PORT:-5000} -gunicorn -c gunicorn.conf.py --bind 0.0.0.0:$BIND_PORT project:app +gunicorn -c gunicorn.conf.py project:app diff --git a/gunicorn.conf.py b/gunicorn.conf.py index 30d3f97..10125da 100644 --- a/gunicorn.conf.py +++ b/gunicorn.conf.py @@ -1,5 +1,22 @@ import multiprocessing +import os +# Bind +port = os.getenv( + "PORT", "5000" +) # No Prefix here because some hosting provider use 'PORT' +bind = os.getenv("GUNICORN_BIND", f"0.0.0.0:{port}") + +# Workers workers = multiprocessing.cpu_count() * 2 + 1 + +# Logging capture_output = True -errorlog = "-" +accesslog = os.getenv("GUNICORN_ACCESS_LOG", None) +access_log_format = os.getenv( + "GUNICORN_ACCESS_LOG_FORMAT", + '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"', +) +errorlog = os.getenv("GUNICORN_ERROR_LOG", "-") +loglevel = os.getenv("GUNICORN_LOG_LEVEL", "info") +logconfig = os.getenv("GUNICORN_LOG_CONFIG", None) diff --git a/project/__init__.py b/project/__init__.py index 7a92135..02e36a5 100644 --- a/project/__init__.py +++ b/project/__init__.py @@ -1,3 +1,4 @@ +import logging import os from flask import Flask, jsonify, redirect, request, url_for @@ -36,6 +37,13 @@ app.config["SECURITY_PASSWORD_SALT"] = os.environ.get( "SECURITY_PASSWORD_SALT", "146585145368132386173505678016728509634" ) +# Gunicorn logging +if __name__ != "__main__": + gunicorn_logger = logging.getLogger("gunicorn.error") + if gunicorn_logger.hasHandlers(): + app.logger.handlers = gunicorn_logger.handlers + app.logger.setLevel(gunicorn_logger.level) + # Gzip gzip = Gzip(app) @@ -76,8 +84,8 @@ mail = Mail(app) if app.config["MAIL_SUPPRESS_SEND"]: def log_message(message, app): - print(message.subject) - print(message.body) + app.logger.info(message.subject) + app.logger.info(message.body) email_dispatched.connect(log_message) diff --git a/project/views/root.py b/project/views/root.py index 2f3a695..1e588ab 100644 --- a/project/views/root.py +++ b/project/views/root.py @@ -68,6 +68,6 @@ def developer(): "ctime": os.path.getctime(all_path), } else: - print("No file at %s" % all_path) + app.logger.info("No file at %s" % all_path) return render_template("developer/read.html", dump_file=dump_file) diff --git a/project/views/utils.py b/project/views/utils.py index 59be1e7..00c35c1 100644 --- a/project/views/utils.py +++ b/project/views/utils.py @@ -4,7 +4,7 @@ from flask_mail import Message from psycopg2.errorcodes import UNIQUE_VIOLATION from sqlalchemy.exc import SQLAlchemyError -from project import db, mail +from project import app, db, mail from project.models import Analytics @@ -91,9 +91,9 @@ def send_mails(recipients, subject, template, **context): msg.html = render_template("email/%s.html" % template, **context) if not mail.default_sender: - print(",".join(msg.recipients)) - print(msg.subject) - print(msg.body) + app.logger.info(",".join(msg.recipients)) + app.logger.info(msg.subject) + app.logger.info(msg.body) return mail.send(msg)