Gunicorn logging stderr/stdout¶

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

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