mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-29 06:44:21 +00:00
feat(backend): PRWLR-4030 Implement Gunicorn as Django WSGI (#9)
* feat: PRWLR-4030 implement Gunicorn server and logging * build: PRWLR-4030 update docker files * docs: PRWLR-4030 update README * chore: PRWLR-4030 change binding address to 0.0.0.0 by default
This commit is contained in:
1 parent
457801f752
commit
f0a276773e
8 files changed
+270
-119
No files matched your search
@@ -6,6 +6,9 @@ from django_guid.log_filters import CorrelationId
|
||||
|
||||
|
||||
class BackendLogger(StrEnum):
|
||||
GUNICORN = "gunicorn"
|
||||
GUNICORN_ACCESS = "gunicorn.access"
|
||||
GUNICORN_ERROR = "gunicorn.error"
|
||||
DJANGO = "django"
|
||||
SECURITY = "django.security"
|
||||
DB = "django.db"
|
||||
@@ -36,7 +39,7 @@ class NDJSONFormatter(logging.Formatter):
|
||||
"thread": record.thread,
|
||||
"transaction_id": record.transaction_id
|
||||
if hasattr(record, "transaction_id")
|
||||
else "N/A",
|
||||
else None,
|
||||
}
|
||||
|
||||
# Add REST API extra fields
|
||||
@@ -57,6 +60,44 @@ class NDJSONFormatter(logging.Formatter):
|
||||
return json.dumps(log_record)
|
||||
|
||||
|
||||
class HumanReadableFormatter(logging.Formatter):
|
||||
"""Human-readable custom formatter for logging messages.
|
||||
|
||||
If available, it will include all kinds of API request metadata.
|
||||
"""
|
||||
|
||||
def format(self, record):
|
||||
log_components = [
|
||||
f"{self.formatTime(record, self.datefmt)}",
|
||||
f"[{record.name}]",
|
||||
f"{record.levelname}:",
|
||||
f"({record.module})",
|
||||
f"[module={record.module}",
|
||||
f"path={record.pathname}",
|
||||
f"line={record.lineno}",
|
||||
f"function={record.funcName}",
|
||||
f"process={record.process}",
|
||||
f"thread={record.thread}",
|
||||
f"transaction-id={record.transaction_id if hasattr(record, "transaction_id") else None}]",
|
||||
f"{record.getMessage()}",
|
||||
]
|
||||
|
||||
# Add REST API extra fields
|
||||
if hasattr(record, "method"):
|
||||
log_components.append(f'"{record.method} {record.path}"')
|
||||
if hasattr(record, "query_params"):
|
||||
log_components.append(f"with parameters {record.query_params}")
|
||||
if hasattr(record, "duration"):
|
||||
log_components.append(f"done in {record.duration}s:")
|
||||
if hasattr(record, "status_code"):
|
||||
log_components.append(f"{record.status_code}")
|
||||
|
||||
if record.exc_info:
|
||||
log_components.append(self.formatException(record.exc_info))
|
||||
|
||||
return " ".join(log_components)
|
||||
|
||||
|
||||
# Filters
|
||||
|
||||
|
||||
@@ -83,6 +124,10 @@ LOGGING = {
|
||||
"()": NDJSONFormatter,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"human_readable": {
|
||||
"()": HumanReadableFormatter,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"django": {
|
||||
"format": "{asctime} [{name}] {levelname}: ({module}) {message}",
|
||||
"style": "{",
|
||||
@@ -106,46 +151,67 @@ LOGGING = {
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"gunicorn_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
"django_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "ndjson",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
"api_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "ndjson",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
"db_console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "ndjson",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
"security_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "ndjson",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
"tasks_console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "ndjson",
|
||||
"formatter": "human_readable",
|
||||
"filters": ["transaction_id"],
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
BackendLogger.GUNICORN: {
|
||||
"handlers": ["gunicorn_console"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
BackendLogger.GUNICORN_ACCESS: {
|
||||
"handlers": ["gunicorn_console"],
|
||||
"level": "CRITICAL",
|
||||
"propagate": False,
|
||||
},
|
||||
BackendLogger.GUNICORN_ERROR: {
|
||||
"handlers": ["gunicorn_console"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
BackendLogger.DJANGO: {
|
||||
"handlers": ["django_console"],
|
||||
"level": "INFO",
|
||||
"level": "WARNING",
|
||||
"propagate": True,
|
||||
},
|
||||
BackendLogger.DB: {
|
||||
"handlers": ["db_console"],
|
||||
"level": "DEBUG",
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
BackendLogger.SECURITY: {
|
||||
@@ -164,4 +230,9 @@ LOGGING = {
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
# Gunicorn required configuration
|
||||
"root": {
|
||||
"level": "ERROR",
|
||||
"handlers": ["gunicorn_console"],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
# Ensure the environment variable for Django settings is set
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings")
|
||||
|
||||
# Import Django and set it up before accessing settings
|
||||
import django # noqa: E402
|
||||
|
||||
django.setup()
|
||||
from backend.settings import LOGGING as DJANGO_LOGGERS # noqa: E402
|
||||
from backend.custom_logging import BackendLogger # noqa: E402
|
||||
|
||||
# Server settings
|
||||
bind = ["0.0.0.0:8000"]
|
||||
# Calculate the number of workers based on CPU count. Refactor when adding config from env vars
|
||||
workers = multiprocessing.cpu_count() * 2 + 1
|
||||
reload = True
|
||||
|
||||
# Logging
|
||||
logconfig_dict = DJANGO_LOGGERS
|
||||
gunicorn_logger = logging.getLogger(BackendLogger.GUNICORN)
|
||||
|
||||
|
||||
# Hooks
|
||||
def on_starting(_):
|
||||
gunicorn_logger.info(f"Starting gunicorn server with {workers} workers")
|
||||
if reload:
|
||||
gunicorn_logger.warning("Reload settings enabled (dev mode)")
|
||||
|
||||
|
||||
def on_reload(_):
|
||||
gunicorn_logger.warning("Gunicorn server has reloaded")
|
||||
|
||||
|
||||
def when_ready(_):
|
||||
gunicorn_logger.info("Gunicorn server is ready")
|
||||
@@ -15,7 +15,7 @@ SECRET_KEY = "django-insecure-9ut_dim+ma+r=ys094hoiwns#g1t@0*_&-*d!g+h)y%lye$0_^
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
Reference in new issue
Block a user