improve STDOUT/STDERR logging redirection.

Instead of faking STDIN/STDERR to a fake object that we close on reopen,
simply redirect them to a file and reopen this one when need.

Should fix most of the issues on #309
This commit is contained in:
benoitc 2013-04-21 11:31:58 +02:00
parent 9acafc25f0
commit 5e08c27495
3 changed files with 25 additions and 70 deletions

View File

@ -22,4 +22,5 @@ def app(environ, start_response):
("Test", "test тест"),
]
start_response(status, response_headers)
print("test")
return iter([data])

View File

@ -81,59 +81,6 @@ def loggers():
return [logging.getLogger(name) for name in existing]
class LazyWriter(object):
"""
File-like object that opens a file lazily when it is first written
to.
"""
def __init__(self, filename, mode='w'):
self.filename = filename
self.fileobj = None
self.lock = threading.Lock()
self.mode = mode
def open(self):
if self.fileobj is None:
self.lock.acquire()
try:
if self.fileobj is None:
self.fileobj = open(self.filename, self.mode)
finally:
self.lock.release()
return self.fileobj
def close(self):
if self.fileobj:
self.lock.acquire()
try:
if self.fileobj:
self.fileobj.close()
self.fileobj = None
finally:
self.lock.release()
def write(self, text):
fileobj = self.open()
fileobj.write(text)
fileobj.flush()
def writelines(self, text):
fileobj = self.open()
fileobj.writelines(text)
fileobj.flush()
def flush(self):
self.open().flush()
def isatty(self):
return bool(self.fileobj and self.fileobj.isatty())
def fileno():
return self.fileobj.fileno()
class SafeAtoms(dict):
def __init__(self, atoms):
@ -213,6 +160,8 @@ class Logger(object):
self.error_handlers = []
self.access_handlers = []
self.cfg = cfg
self.logfile = None
self.lock = threading.Lock()
self.setup(cfg)
def setup(self, cfg):
@ -224,7 +173,12 @@ class Logger(object):
if cfg.errorlog != "-":
# if an error log file is set redirect stdout & stderr to
# this log file.
sys.stdout = sys.stderr = LazyWriter(cfg.errorlog, 'a')
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())
# set gunicorn.error handler
self._set_handler(self.error_log, cfg.errorlog,
@ -330,9 +284,19 @@ class Logger(object):
def reopen_files(self):
if self.cfg.errorlog != "-":
# Close stderr & stdout if they are redirected to error log file
sys.stderr.close()
sys.stdout.close()
# if an error log file is set redirect stdout & stderr to
# this log file.
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):
@ -346,6 +310,9 @@ class Logger(object):
handler.release()
def close_on_exec(self):
for stream in sys.stdout, sys.stderr:
util.close_on_exec(stream.fileno())
for log in loggers():
for handler in log.handlers:
if isinstance(handler, logging.FileHandler):

View File

@ -1,13 +0,0 @@
import sys
from gunicorn.glogging import LazyWriter
def test_lazywriter_isatty():
orig = sys.stdout
sys.stdout = LazyWriter('test.log')
try:
sys.stdout.isatty()
except AttributeError:
raise AssertionError("LazyWriter has no attribute 'isatty'")
sys.stdout = orig