diff --git a/gunicorn/config.py b/gunicorn/config.py index 0f9a2822..b0227ccc 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -917,6 +917,7 @@ class WorkerTmpDir(Setting): If not set, the default temporary directory will be used. """ + class User(Setting): name = "user" section = "Server Mechanics" @@ -1121,6 +1122,20 @@ class Loglevel(Setting): """ +class CaptureOutput(Setting): + name = "capture_output" + section = "Logging" + cli = ["--capture-output"] + validator = validate_bool + action = 'store_true' + default = False + desc = """\ + Redirect stdout/stderr to Error log. + + .. versionadded:: 19.6 + """ + + class LoggerClass(Setting): name = "logger_class" section = "Logging" diff --git a/gunicorn/glogging.py b/gunicorn/glogging.py index e26175fc..c33eeb25 100644 --- a/gunicorn/glogging.py +++ b/gunicorn/glogging.py @@ -12,6 +12,7 @@ from logging.config import fileConfig import os import socket import sys +import threading import traceback from gunicorn import util @@ -186,6 +187,8 @@ class Logger(object): self.access_log.propagate = False self.error_handlers = [] self.access_handlers = [] + self.logfile = None + self.lock = threading.Lock() self.cfg = cfg self.setup(cfg) @@ -195,8 +198,16 @@ class Logger(object): self.access_log.setLevel(logging.INFO) # set gunicorn.error handler + if self.cfg.capture_output and cfg.errorlog != "-": + for stream in sys.stdout, sys.stderr: + stream.flush() + + self.logfile = open(cfg.errorlog, 'a+') + os.dup2(self.logfile.fileno(), sys.stdout.fileno()) + os.dup2(self.logfile.fileno(), sys.stderr.fileno()) + self._set_handler(self.error_log, cfg.errorlog, - logging.Formatter(self.error_fmt, self.datefmt)) + logging.Formatter(self.error_fmt, self.datefmt)) # set gunicorn.access handler if cfg.accesslog is not None: @@ -318,6 +329,18 @@ class Logger(object): return time.strftime('[%d/%b/%Y:%H:%M:%S %z]') def reopen_files(self): + if self.cfg.capture_output and self.cfg.errorlog != "-": + for stream in sys.stdout, sys.stderr: + stream.flush() + + with self.lock: + if self.logfile is not None: + self.logfile.close() + self.logfile = open(self.cfg.errorlog, 'a+') + os.dup2(self.logfile.fileno(), sys.stdout.fileno()) + os.dup2(self.logfile.fileno(), sys.stderr.fileno()) + + for log in loggers(): for handler in log.handlers: if isinstance(handler, logging.FileHandler):