diff --git a/sanic/app.py b/sanic/app.py index 4c0fa15e..1d4497d0 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -14,7 +14,7 @@ from sanic.config import Config from sanic.constants import HTTP_METHODS from sanic.exceptions import ServerError, URLBuildError, SanicException from sanic.handlers import ErrorHandler -from sanic.log import logger, error_logger +from sanic.log import logger, error_logger, LOGGING_CONFIG_DEFAULTS from sanic.response import HTTPResponse, StreamingHTTPResponse from sanic.router import Router from sanic.server import serve, serve_multiple, HttpProtocol, Signal @@ -28,13 +28,16 @@ class Sanic: def __init__(self, name=None, router=None, error_handler=None, load_env=True, request_class=None, - strict_slashes=False): + strict_slashes=False, log_config=None): # Get name from previous stack frame if name is None: frame_records = stack()[1] name = getmodulename(frame_records[1]) + # logging + logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) + self.name = name self.router = router or Router() self.request_class = request_class @@ -567,7 +570,7 @@ class Sanic: def run(self, host=None, port=None, debug=False, ssl=None, sock=None, workers=1, protocol=None, backlog=100, stop_event=None, register_sys_signals=True, - access_log=True): + access_log=True, log_config=None): """Run the HTTP Server and listen until keyboard interrupt or term signal. On termination, drain connections before closing. @@ -585,6 +588,8 @@ class Sanic: :param protocol: Subclass of asyncio protocol class :return: Nothing """ + logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) + if sock is None: host, port = host or "127.0.0.1", port or 8000 @@ -627,12 +632,14 @@ class Sanic: async def create_server(self, host=None, port=None, debug=False, ssl=None, sock=None, protocol=None, backlog=100, stop_event=None, - access_log=True): + access_log=True, log_config=None): """Asynchronous version of `run`. NOTE: This does not support multiprocessing and is not the preferred way to run a Sanic application. """ + logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) + if sock is None: host, port = host or "127.0.0.1", port or 8000 diff --git a/sanic/config.py b/sanic/config.py index 1f0bbd3e..b5430445 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -4,14 +4,8 @@ import syslog import platform import types -SANIC_PREFIX = 'SANIC_' -_address_dict = { - 'Windows': ('localhost', 514), - 'Darwin': '/var/run/syslog', - 'Linux': '/dev/log', - 'FreeBSD': '/var/run/log' -} +SANIC_PREFIX = 'SANIC_' class Config(dict): diff --git a/sanic/log.py b/sanic/log.py index 5abce0e2..72636cd0 100644 --- a/sanic/log.py +++ b/sanic/log.py @@ -19,7 +19,7 @@ LOGGING_CONFIG_DEFAULTS = dict( "sanic.access": { "level": "INFO", - "handlers": ["console"], + "handlers": ["access_console"], "propagate": True, "qualname": "sanic.access" } @@ -35,13 +35,24 @@ LOGGING_CONFIG_DEFAULTS = dict( "formatter": "generic", "stream": "sys.stderr" }, + "access_console": { + "class": "logging.StreamHandler", + "formatter": "access", + "stream": "sys.stdout" + }, }, formatters={ "generic": { "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s", "datefmt": "[%Y-%m-%d %H:%M:%S %z]", "class": "logging.Formatter" - } + }, + "access": { + "format": "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " + + "%(request)s %(message)s %(status)d %(byte)d", + "datefmt": "[%Y-%m-%d %H:%M:%S %z]", + "class": "logging.Formatter" + }, } ) diff --git a/sanic/request.py b/sanic/request.py index b54d463f..c842cb4b 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -17,7 +17,7 @@ except ImportError: json_loads = json.loads from sanic.exceptions import InvalidUsage -from sanic.log import logger +from sanic.log import error_logger DEFAULT_HTTP_CONTENT_TYPE = "application/octet-stream" @@ -114,7 +114,7 @@ class Request(dict): self.parsed_form, self.parsed_files = ( parse_multipart_form(self.body, boundary)) except Exception: - logger.exception("Failed when parsing form") + error_logger.exception("Failed when parsing form") return self.parsed_form diff --git a/tests/test_logging.py b/tests/test_logging.py index 3d75dbe0..112c94a0 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -2,6 +2,7 @@ import uuid from importlib import reload from sanic.response import text +from sanic.log import LOGGING_CONFIG_DEFAULTS from sanic import Sanic from io import StringIO import logging @@ -37,3 +38,36 @@ def test_log(): request, response = app.test_client.get('/') log_text = log_stream.getvalue() assert rand_string in log_text + + +def test_logging_defaults(): + reset_logging() + app = Sanic("test_logging") + + for fmt in [h.formatter for h in logging.getLogger('root').handlers]: + assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['generic']['format'] + + for fmt in [h.formatter for h in logging.getLogger('sanic.error').handlers]: + assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['generic']['format'] + + for fmt in [h.formatter for h in logging.getLogger('sanic.access').handlers]: + assert fmt._fmt == LOGGING_CONFIG_DEFAULTS['formatters']['access']['format'] + + +def test_logging_pass_customer_logconfig(): + reset_logging() + + modified_config = LOGGING_CONFIG_DEFAULTS + modified_config['formatters']['generic']['format'] = '%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s' + modified_config['formatters']['access']['format'] = '%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s' + + app = Sanic("test_logging", log_config=modified_config) + + for fmt in [h.formatter for h in logging.getLogger('root').handlers]: + assert fmt._fmt == modified_config['formatters']['generic']['format'] + + for fmt in [h.formatter for h in logging.getLogger('sanic.error').handlers]: + assert fmt._fmt == modified_config['formatters']['generic']['format'] + + for fmt in [h.formatter for h in logging.getLogger('sanic.access').handlers]: + assert fmt._fmt == modified_config['formatters']['access']['format']