|  | 
| 1 |  | -# If you are not having memory issues, just delete this. | 
| 2 |  | -# This is primarily to prevent memory leaks | 
| 3 |  | -# Based on https://devcenter.heroku.com/articles/python-gunicorn | 
| 4 |  | -# Based on https://adamj.eu/tech/2019/09/19/working-around-memory-leaks-in-your-django-app/ | 
| 5 |  | -# https://docs.gunicorn.org/en/latest/settings.html#max-requests | 
| 6 |  | -# https://docs.gunicorn.org/en/latest/settings.html#max-requests-jitter | 
| 7 |  | -max_requests = 1200 | 
| 8 |  | -max_requests_jitter = 100 | 
|  | 1 | +# https://mattsegal.dev/django-gunicorn-nginx-logging.html | 
|  | 2 | +# https://albersdevelopment.net/2019/08/15/using-structlog-with-gunicorn/ | 
|  | 3 | + | 
|  | 4 | +import logging | 
|  | 5 | +import logging.config | 
|  | 6 | +import re | 
|  | 7 | + | 
|  | 8 | +import structlog | 
|  | 9 | + | 
|  | 10 | + | 
|  | 11 | +def combined_logformat(logger, name, event_dict): | 
|  | 12 | +    if event_dict.get("logger") == "gunicorn.access": | 
|  | 13 | +        message = event_dict["event"] | 
|  | 14 | + | 
|  | 15 | +        parts = [ | 
|  | 16 | +            r"(?P<host>\S+)",  # host %h | 
|  | 17 | +            r"\S+",  # indent %l (unused) | 
|  | 18 | +            r"(?P<user>\S+)",  # user %u | 
|  | 19 | +            r"\[(?P<time>.+)\]",  # time %t | 
|  | 20 | +            r'"(?P<request>.+)"',  # request "%r" | 
|  | 21 | +            r"(?P<status>[0-9]+)",  # status %>s | 
|  | 22 | +            r"(?P<size>\S+)",  # size %b (careful, can be '-') | 
|  | 23 | +            r'"(?P<referer>.*)"',  # referer "%{Referer}i" | 
|  | 24 | +            r'"(?P<agent>.*)"',  # user agent "%{User-agent}i" | 
|  | 25 | +        ] | 
|  | 26 | +        pattern = re.compile(r"\s+".join(parts) + r"\s*\Z") | 
|  | 27 | +        m = pattern.match(message) | 
|  | 28 | +        res = m.groupdict() | 
|  | 29 | + | 
|  | 30 | +        if res["user"] == "-": | 
|  | 31 | +            res["user"] = None | 
|  | 32 | + | 
|  | 33 | +        res["status"] = int(res["status"]) | 
|  | 34 | + | 
|  | 35 | +        if res["size"] == "-": | 
|  | 36 | +            res["size"] = 0 | 
|  | 37 | +        else: | 
|  | 38 | +            res["size"] = int(res["size"]) | 
|  | 39 | + | 
|  | 40 | +        if res["referer"] == "-": | 
|  | 41 | +            res["referer"] = None | 
|  | 42 | + | 
|  | 43 | +        event_dict.update(res) | 
|  | 44 | + | 
|  | 45 | +    return event_dict | 
|  | 46 | + | 
|  | 47 | + | 
|  | 48 | +timestamper = structlog.processors.TimeStamper(fmt="iso", utc=True) | 
|  | 49 | +pre_chain = [ | 
|  | 50 | +    # Add the log level and a timestamp to the event_dict if the log entry | 
|  | 51 | +    # is not from structlog. | 
|  | 52 | +    structlog.stdlib.add_log_level, | 
|  | 53 | +    structlog.stdlib.add_logger_name, | 
|  | 54 | +    timestamper, | 
|  | 55 | +    combined_logformat, | 
|  | 56 | +] | 
|  | 57 | + | 
|  | 58 | +CONFIG_DEFAULTS = { | 
|  | 59 | +    "version": 1, | 
|  | 60 | +    "disable_existing_loggers": False, | 
|  | 61 | +    "root": {"level": "INFO", "handlers": ["default"]}, | 
|  | 62 | +    "loggers": { | 
|  | 63 | +        "gunicorn.error": {"level": "INFO", "handlers": ["default"], "propagate": False, "qualname": "gunicorn.error"}, | 
|  | 64 | +        "gunicorn.access": { | 
|  | 65 | +            "level": "INFO", | 
|  | 66 | +            "handlers": ["default"], | 
|  | 67 | +            "propagate": False, | 
|  | 68 | +            "qualname": "gunicorn.access", | 
|  | 69 | +        }, | 
|  | 70 | +        "django_structlog": { | 
|  | 71 | +            "level": "INFO", | 
|  | 72 | +            "handlers": [], | 
|  | 73 | +            "propagate": False, | 
|  | 74 | +        }, | 
|  | 75 | +    }, | 
|  | 76 | +    "handlers": { | 
|  | 77 | +        "default": { | 
|  | 78 | +            "class": "logging.StreamHandler", | 
|  | 79 | +            "formatter": "json_formatter", | 
|  | 80 | +        }, | 
|  | 81 | +    }, | 
|  | 82 | +    "formatters": { | 
|  | 83 | +        "json_formatter": { | 
|  | 84 | +            "()": structlog.stdlib.ProcessorFormatter, | 
|  | 85 | +            "processor": structlog.processors.JSONRenderer(), | 
|  | 86 | +            "foreign_pre_chain": pre_chain, | 
|  | 87 | +        } | 
|  | 88 | +    }, | 
|  | 89 | +} | 
|  | 90 | + | 
|  | 91 | +logging.config.dictConfig(CONFIG_DEFAULTS) | 
0 commit comments