Skip to content

Commit

Permalink
Resolve lack of logging.
Browse files Browse the repository at this point in the history
  • Loading branch information
toddbirchard committed Oct 24, 2024
1 parent c8d652e commit 84d9e45
Show file tree
Hide file tree
Showing 12 changed files with 405 additions and 170 deletions.
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
select = E9,F63,F7,F82
exclude = .git,.github,__pycache__,.pytest_cache,.venv,logs,creds
max-line-length = 120
5 changes: 5 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Flask configuration variables."""

from os import environ, path

from dotenv import load_dotenv
Expand All @@ -11,6 +12,7 @@ class Config:
"""Set Flask configuration from .env file."""

# General Config
APPLICATION_NAME = "flaskassets"
ENVIRONMENT = environ.get("ENVIRONMENT")

# Flask Config
Expand All @@ -27,3 +29,6 @@ class Config:
STATIC_FOLDER = "static"
TEMPLATES_FOLDER = "templates"
COMPRESSOR_DEBUG = True


settings = Config()
1 change: 1 addition & 0 deletions flask_assets_tutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Initialize Flask app."""

from flask import Flask
from flask_assets import Environment

Expand Down
3 changes: 3 additions & 0 deletions flask_assets_tutorial/admin/routes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"""Routes for logged-in account pages."""

from flask import Blueprint, render_template
from log import LOGGER

admin_blueprint = Blueprint("admin_blueprint", __name__, template_folder="templates", static_folder="static")


@admin_blueprint.route("/dashboard", methods=["GET"])
def dashboard():
"""Admin dashboard route."""
LOGGER.info("Admin dashboard rendered by user.")
return render_template(
"dashboard.jinja2",
title="Admin Dashboard",
Expand Down
1 change: 1 addition & 0 deletions flask_assets_tutorial/assets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Compile static assets."""

from flask import current_app as app
from flask_assets import Bundle, Environment

Expand Down
5 changes: 5 additions & 0 deletions flask_assets_tutorial/main/routes.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"""Routes for main pages."""

from flask import Blueprint, render_template
from log import LOGGER

main_blueprint = Blueprint("main_blueprint", __name__, template_folder="templates", static_folder="static")


@main_blueprint.route("/", methods=["GET"])
def home():
"""Home page route."""
LOGGER.info("Home page rendered by user.")
return render_template(
"index.jinja2",
title="Home",
Expand All @@ -19,6 +22,7 @@ def home():
@main_blueprint.route("/about", methods=["GET"])
def about():
"""About page route."""
LOGGER.info("About page rendered by user.")
return render_template(
"index.jinja2",
title="About",
Expand All @@ -31,6 +35,7 @@ def about():
@main_blueprint.route("/etc", methods=["GET"])
def etc():
"""Etc page route."""
LOGGER.info("Etc. page rendered by user.")
return render_template(
"index.jinja2",
title="Etc.",
Expand Down
5 changes: 3 additions & 2 deletions gunicorn.conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Gunicorn configuration file."""

import socket
from os import environ, path

Expand All @@ -23,7 +24,7 @@

if ENVIRONMENT == "production":
daemon = True
accesslog = "/var/log/flaskassets/access.log"
accesslog = "/var/log/flaskassets/info.log"
errorlog = "/var/log/flaskassets/error.log"
loglevel = "trace"
dogstatsd_tags = "env:prod,service:flaskassets,language:python"
dogstatsd_tags = "env:prod,service:flaskassets,language:python,type:tutorial"
122 changes: 122 additions & 0 deletions log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Custom logger."""

import json
from sys import stdout

from loguru import logger

from config import settings


def json_formatter(record: dict) -> str:
"""
Pass raw log to be serialized.
:param dict record: Dictionary containing logged message with metadata.
:returns: str
"""

def serialize(log: dict) -> str:
"""
Parse log message into Datadog JSON format.
:param dict log: Dictionary containing logged message with metadata.
:returns: str
"""
subset = {
"time": log["time"].strftime("%m/%d/%Y, %H:%M:%S"),
"message": log["message"],
"level": log["level"].name,
"function": log.get("function"),
"module": log.get("name"),
}
if log.get("exception", None):
subset.update({"exception": log["exception"]})
return json.dumps(subset)

record["extra"]["serialized"] = serialize(record)
return "{extra[serialized]},\n"


def log_formatter(record: dict) -> str:
"""
Formatter for .log records
:param dict record: Key/value object containing log message & metadata.
:returns: str
"""
if record["level"].name == "TRACE":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #d2eaff>{level}</fg #d2eaff>: <light-white>{message}</light-white>\n"
if record["level"].name == "INFO":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #98bedf>{level}</fg #98bedf>: <light-white>{message}</light-white>\n"
if record["level"].name == "WARNING":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #b09057>{level}</fg #b09057>: <light-white>{message}</light-white>\n"
if record["level"].name == "SUCCESS":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #6dac77>{level}</fg #6dac77>: <light-white>{message}</light-white>\n"
if record["level"].name == "ERROR":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #a35252>{level}</fg #a35252>: <light-white>{message}</light-white>\n"
if record["level"].name == "CRITICAL":
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #521010>{level}</fg #521010>: <light-white>{message}</light-white>\n"
return "<fg #5278a3>{time:MM-DD-YYYY HH:mm:ss}</fg #5278a3> | <fg #98bedf>{level}</fg #98bedf>: <light-white>{message}</light-white>\n"


def create_logger() -> logger:
"""
Configure custom logger.
:returns: logger
"""
logger.remove()
logger.add(
stdout,
colorize=True,
catch=True,
level="TRACE",
format=log_formatter,
)
if settings.ENVIRONMENT == "production":
# Datadog JSON logs
logger.add(
f"/var/log/{settings.APPLICATION_NAME}/info.json",
format=json_formatter,
rotation="200 MB",
level="TRACE",
compression="zip",
)
# Readable logs
logger.add(
f"/var/log/{settings.APPLICATION_NAME}/info.log",
colorize=True,
catch=True,
level="TRACE",
format=log_formatter,
rotation="200 MB",
compression="zip",
)
else:
logger.add(
"./logs/info.log",
colorize=True,
catch=True,
format=log_formatter,
rotation="200 MB",
compression="zip",
level="INFO",
)
logger.add(
"./logs/error.log",
colorize=True,
catch=True,
format=log_formatter,
rotation="200 MB",
compression="zip",
level="ERROR",
)
return logger


# Custom logger
LOGGER = create_logger()
Loading

0 comments on commit 84d9e45

Please sign in to comment.