Merge pull request #2705 from cisagov/ms/2561-alter-log-format

Issue #2561: format logs as json (on getgov-ms)
This commit is contained in:
Matt-Spence 2024-09-12 13:02:03 -05:00 committed by GitHub
commit 00e784e1ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 51 additions and 8 deletions

View file

@ -1,7 +1,7 @@
import os import os
import tempfile import tempfile
from django.conf import settings from django.conf import settings # type: ignore
class Cert: class Cert:
@ -12,7 +12,7 @@ class Cert:
variable but Python's ssl library requires a file. variable but Python's ssl library requires a file.
""" """
def __init__(self, data=settings.SECRET_REGISTRY_CERT) -> None: def __init__(self, data=settings.SECRET_REGISTRY_CERT) -> None: # type: ignore
self.filename = self._write(data) self.filename = self._write(data)
def __del__(self): def __del__(self):
@ -31,4 +31,4 @@ class Key(Cert):
"""Location of private key as written to disk.""" """Location of private key as written to disk."""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__(data=settings.SECRET_REGISTRY_KEY) super().__init__(data=settings.SECRET_REGISTRY_KEY) # type: ignore

View file

@ -23,6 +23,9 @@ from cfenv import AppEnv # type: ignore
from pathlib import Path from pathlib import Path
from typing import Final from typing import Final
from botocore.config import Config from botocore.config import Config
import json
import logging
from django.utils.log import ServerFormatter
# # # ### # # # ###
# Setup code goes here # # Setup code goes here #
@ -57,7 +60,7 @@ env_db_url = env.dj_db_url("DATABASE_URL")
env_debug = env.bool("DJANGO_DEBUG", default=False) env_debug = env.bool("DJANGO_DEBUG", default=False)
env_is_production = env.bool("IS_PRODUCTION", default=False) env_is_production = env.bool("IS_PRODUCTION", default=False)
env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG") env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")
env_base_url = env.str("DJANGO_BASE_URL") env_base_url: str = env.str("DJANGO_BASE_URL")
env_getgov_public_site_url = env.str("GETGOV_PUBLIC_SITE_URL", "") env_getgov_public_site_url = env.str("GETGOV_PUBLIC_SITE_URL", "")
env_oidc_active_provider = env.str("OIDC_ACTIVE_PROVIDER", "identity sandbox") env_oidc_active_provider = env.str("OIDC_ACTIVE_PROVIDER", "identity sandbox")
@ -192,7 +195,7 @@ MIDDLEWARE = [
"registrar.registrar_middleware.CheckPortfolioMiddleware", "registrar.registrar_middleware.CheckPortfolioMiddleware",
] ]
# application object used by Djangos built-in servers (e.g. `runserver`) # application object used by Django's built-in servers (e.g. `runserver`)
WSGI_APPLICATION = "registrar.config.wsgi.application" WSGI_APPLICATION = "registrar.config.wsgi.application"
# endregion # endregion
@ -415,7 +418,7 @@ LANGUAGE_COOKIE_SECURE = True
# and to interpret datetimes entered in forms # and to interpret datetimes entered in forms
TIME_ZONE = "UTC" TIME_ZONE = "UTC"
# enable Djangos translation system # enable Django's translation system
USE_I18N = True USE_I18N = True
# enable localized formatting of numbers and dates # enable localized formatting of numbers and dates
@ -450,6 +453,40 @@ PHONENUMBER_DEFAULT_REGION = "US"
# logger.error("Can't do this important task. Something is very wrong.") # logger.error("Can't do this important task. Something is very wrong.")
# logger.critical("Going to crash now.") # logger.critical("Going to crash now.")
class JsonFormatter(logging.Formatter):
"""Formats logs into JSON for better parsing"""
def __init__(self):
super().__init__(datefmt="%d/%b/%Y %H:%M:%S")
def format(self, record):
log_record = {
"timestamp": self.formatTime(record, self.datefmt),
"level": record.levelname,
"name": record.name,
"lineno": record.lineno,
"message": record.getMessage(),
}
return json.dumps(log_record)
class JsonServerFormatter(ServerFormatter):
"""Formats server logs into JSON for better parsing"""
def format(self, record):
formatted_record = super().format(record)
log_entry = {"server_time": record.server_time, "level": record.levelname, "message": formatted_record}
return json.dumps(log_entry)
# default to json formatted logs
server_formatter, console_formatter = "json.server", "json"
# don't use json format locally, it makes logs hard to read in console
if "localhost" in env_base_url:
server_formatter, console_formatter = "django.server", "verbose"
LOGGING = { LOGGING = {
"version": 1, "version": 1,
# Don't import Django's existing loggers # Don't import Django's existing loggers
@ -469,6 +506,12 @@ LOGGING = {
"format": "[{server_time}] {message}", "format": "[{server_time}] {message}",
"style": "{", "style": "{",
}, },
"json.server": {
"()": JsonServerFormatter,
},
"json": {
"()": JsonFormatter,
},
}, },
# define where log messages will be sent; # define where log messages will be sent;
# each logger can have one or more handlers # each logger can have one or more handlers
@ -476,12 +519,12 @@ LOGGING = {
"console": { "console": {
"level": env_log_level, "level": env_log_level,
"class": "logging.StreamHandler", "class": "logging.StreamHandler",
"formatter": "verbose", "formatter": console_formatter,
}, },
"django.server": { "django.server": {
"level": "INFO", "level": "INFO",
"class": "logging.StreamHandler", "class": "logging.StreamHandler",
"formatter": "django.server", "formatter": server_formatter,
}, },
# No file logger is configured, # No file logger is configured,
# because containerized apps # because containerized apps