diff --git a/examples/logging.conf b/examples/logging.conf index 286f4a39..48f1328f 100644 --- a/examples/logging.conf +++ b/examples/logging.conf @@ -1,33 +1,48 @@ [loggers] -keys=root, gunicorn +keys=root, gunicorn_error, gunicorn_access [handlers] -keys=console, file +keys=console, error_file, access_file [formatters] -keys=generic +keys=generic, access [logger_root] level=INFO handlers=console -[logger_gunicorn] -level=DEBUG -handlers=file +[logger_gunicorn_error] +level=INFO +handlers=error_file propagate=1 -qualname=gunicorn +qualname=gunicorn.error + +[logger_gunicorn_access] +level=INFO +handlers=access_file +propagate=1 +qualname=gunicorn.access [handler_console] class=StreamHandler formatter=generic args=(sys.stdout, ) -[handler_file] +[handler_error_file] class=logging.FileHandler formatter=generic -args=('/tmp/test.log',) +args=('/tmp/gunicorn.error.log',) + +[handler_access_file] +class=logging.FileHandler +formatter=access +args=('/tmp/gunicorn.access.log',) [formatter_generic] -format="%(asctime)s [%(process)d] [%(levelname)s] %(message)s" -datefmt="%Y-%m-%d %H:%M:%S" +format=%(asctime)s [%(process)d] [%(levelname)s] %(message)s +datefmt=%Y-%m-%d %H:%M:%S +class=logging.Formatter + +[formatter_access] +format=%(message)s class=logging.Formatter diff --git a/gunicorn/config.py b/gunicorn/config.py index cef15f35..9b717cd0 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -686,6 +686,20 @@ class LoggerClass(Setting): with `egg:gunicorn#simple` """ + +class LogConfig(Setting): + name = "logconfig" + section = "Logging" + cli = ["--log-config"] + meta = "FILE" + validator = validate_string + default = None + desc = """\ +The log config file to use. +Gunicorn uses the standard Python logging module's Configuration +file format. +""" + class Procname(Setting): name = "proc_name" section = "Process Naming" diff --git a/gunicorn/glogging.py b/gunicorn/glogging.py index c2303e25..1edc7af5 100644 --- a/gunicorn/glogging.py +++ b/gunicorn/glogging.py @@ -6,10 +6,16 @@ import datetime import logging logging.Logger.manager.emittedNoHandlerWarning = 1 +import os import sys import traceback import threading +try: + from logging.config import fileConfig +except ImportError: + from gunicorn.logging_config import fileConfig + from gunicorn import util class LazyWriter(object): @@ -68,32 +74,37 @@ class Logger(object): self.access_log = logging.getLogger("gunicorn.access") self.error_handlers = [] self.access_handlers = [] - + self.cfg = cfg self.setup(cfg) def setup(self, cfg): - self.cfg = cfg + if not cfg.logconfig: + loglevel = self.LOG_LEVELS.get(cfg.loglevel.lower(), logging.INFO) + self.error_log.setLevel(loglevel) + self.access_log.setLevel(logging.INFO) - loglevel = self.LOG_LEVELS.get(cfg.loglevel.lower(), logging.INFO) - if cfg.errorlog != "-": - # if an error log file is set redirect stdout & stderr to - # this log file. - stdout_log = LazyWriter(cfg.errorlog, 'a') - sys.stdout = stdout_log - sys.stderr = stdout_log + if cfg.errorlog != "-": + # if an error log file is set redirect stdout & stderr to + # this log file. + stdout_log = LazyWriter(cfg.errorlog, 'a') + sys.stdout = stdout_log + sys.stderr = stdout_log - self.error_log.setLevel(loglevel) + # set gunicorn.error handler + self._set_handler(self.error_log, cfg.errorlog, + logging.Formatter(self.error_fmt, self.datefmt)) - # always info in access log - self.access_log.setLevel(logging.INFO) - - self._set_handler(self.error_log, cfg.errorlog, - logging.Formatter(self.error_fmt, self.datefmt)) - - if cfg.accesslog is not None: - self._set_handler(self.access_log, cfg.accesslog, - fmt=logging.Formatter(self.access_fmt)) + # set gunicorn.access handler + if cfg.accesslog is not None: + self._set_handler(self.access_log, cfg.accesslog, + fmt=logging.Formatter(self.access_fmt)) + else: + if os.path.exists(cfg.logconfig): + util.check_is_writeable(cfg.logconfig) + fileConfig(cfg.logconfig) + else: + raise RuntimeError("Error: log config '%s' not found" % path) def critical(self, msg, *args, **kwargs): @@ -124,10 +135,9 @@ class Logger(object): for format details """ - if not self.cfg.accesslog: + if not self.cfg.accesslog and not self.cfg.logconfig: return - status = resp.status.split(None, 1)[0] atoms = { 'h': environ['REMOTE_ADDR'], @@ -197,6 +207,7 @@ class Logger(object): if output == "-": h = logging.StreamHandler() else: + util.check_is_writeable(output) h = logging.FileHandler(output) h.setFormatter(fmt) diff --git a/gunicorn/util.py b/gunicorn/util.py index 054d76ac..5bf45dab 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -303,3 +303,11 @@ def seed(): random.seed(os.urandom(64)) except NotImplementedError: random.seed(random.random()) + + +def check_is_writeable(path): + try: + f = open(path, 'a') + except IOError, e: + raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e)) + f.close()