Gunicorn logging stderr/stdout

Stdout pyramid

Sometimes it is necessary to write Gunicorn logs to the correct std-output.

By default, info logs are written to the stderr console, which can cause issues, as the logs may be classified as errors instead of info logs.

I am using Gunicorn, for example, with the Pyramid framework (tested).

Just create a log_filters.py

import logging


class MessageIsNormal(logging.Filter):
    def filter(self, record):
        return record.levelname in ["DEBUG", "INFO", "WARNING"]


class MessageIsError(logging.Filter):
    def filter(self, record):
        return record.levelname in ["ERROR", "CRITICAL"]


class OpenAPIFilter(logging.Filter):
    def filter(self, record):
        record.msg = "OpenAPI validation failed"
        return True

And

gunicorn_logger_config.py

import sys

logconfig_dict = {
    "formatters": {
        "simple": {
            "format": "%(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s"
        },  # add  %(name)s for logger chanel
    },
    "filters": {
        "require_message_is_normal": {
            "()": "log_filters.MessageIsNormal",
        },
        "require_message_is_error": {
            "()": "log_filters.MessageIsError",
        },
        "openapi_filter": {"()": "log_filters.OpenAPIFilter"},
    },
    "handlers": {
        "console_stdout": {
            "level": "DEBUG",
            "filters": ["require_message_is_normal"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
            "stream": sys.stdout,
        },
        "console_stderr": {
            "level": "DEBUG",
            "filters": ["require_message_is_error"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
            "stream": sys.stderr,
        },
    },
    "loggers": {
        "": {
            "handlers": ["console_stdout", "console_stderr"],
            "level": "INFO",
        },
        "gunicorn": {
            "handlers": ["console_stdout"],
            "level": "INFO",
        },
        "gunicorn.access": {
            "handlers": [],  # disabled on GCP
            "level": "DEBUG",
        },
        "gunicorn.error": {
            "handlers": ["console_stdout", "console_stderr"],
            "level": "INFO",
        },
        "pyramid_openapi3": {"filters": ["openapi_filter"], "level": "WARN"},
    },
    "root": {
        "level": "INFO",
        "handlers": ["console_stdout", "console_stderr"],
    },
}

Gunicorn needs to be start with a config

gunicorn --paste dev.ini --timeout 180 --config gunicorn_logger_config.py -b 0.0.0.0:6543

The result shoul looks like

Correct Stdout pyramid

Also, your logs collected somewhere in the cloud should be correctly classified as error, info, etc.