summaryrefslogtreecommitdiff
path: root/paste/translogger.py
diff options
context:
space:
mode:
Diffstat (limited to 'paste/translogger.py')
-rw-r--r--paste/translogger.py122
1 files changed, 122 insertions, 0 deletions
diff --git a/paste/translogger.py b/paste/translogger.py
new file mode 100644
index 0000000..794efd8
--- /dev/null
+++ b/paste/translogger.py
@@ -0,0 +1,122 @@
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+"""
+Middleware for logging requests, using Apache combined log format
+"""
+
+import logging
+import six
+import time
+from six.moves.urllib.parse import quote
+
+class TransLogger(object):
+ """
+ This logging middleware will log all requests as they go through.
+ They are, by default, sent to a logger named ``'wsgi'`` at the
+ INFO level.
+
+ If ``setup_console_handler`` is true, then messages for the named
+ logger will be sent to the console.
+ """
+
+ format = ('%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s] '
+ '"%(REQUEST_METHOD)s %(REQUEST_URI)s %(HTTP_VERSION)s" '
+ '%(status)s %(bytes)s "%(HTTP_REFERER)s" "%(HTTP_USER_AGENT)s"')
+
+ def __init__(self, application,
+ logger=None,
+ format=None,
+ logging_level=logging.INFO,
+ logger_name='wsgi',
+ setup_console_handler=True,
+ set_logger_level=logging.DEBUG):
+ if format is not None:
+ self.format = format
+ self.application = application
+ self.logging_level = logging_level
+ self.logger_name = logger_name
+ if logger is None:
+ self.logger = logging.getLogger(self.logger_name)
+ if setup_console_handler:
+ console = logging.StreamHandler()
+ console.setLevel(logging.DEBUG)
+ # We need to control the exact format:
+ console.setFormatter(logging.Formatter('%(message)s'))
+ self.logger.addHandler(console)
+ self.logger.propagate = False
+ if set_logger_level is not None:
+ self.logger.setLevel(set_logger_level)
+ else:
+ self.logger = logger
+
+ def __call__(self, environ, start_response):
+ start = time.localtime()
+ req_uri = quote(environ.get('SCRIPT_NAME', '')
+ + environ.get('PATH_INFO', ''))
+ if environ.get('QUERY_STRING'):
+ req_uri += '?'+environ['QUERY_STRING']
+ method = environ['REQUEST_METHOD']
+ def replacement_start_response(status, headers, exc_info=None):
+ # @@: Ideally we would count the bytes going by if no
+ # content-length header was provided; but that does add
+ # some overhead, so at least for now we'll be lazy.
+ bytes = None
+ for name, value in headers:
+ if name.lower() == 'content-length':
+ bytes = value
+ self.write_log(environ, method, req_uri, start, status, bytes)
+ return start_response(status, headers)
+ return self.application(environ, replacement_start_response)
+
+ def write_log(self, environ, method, req_uri, start, status, bytes):
+ if bytes is None:
+ bytes = '-'
+ if time.daylight:
+ offset = time.altzone / 60 / 60 * -100
+ else:
+ offset = time.timezone / 60 / 60 * -100
+ if offset >= 0:
+ offset = "+%0.4d" % (offset)
+ elif offset < 0:
+ offset = "%0.4d" % (offset)
+ remote_addr = '-'
+ if environ.get('HTTP_X_FORWARDED_FOR'):
+ remote_addr = environ['HTTP_X_FORWARDED_FOR']
+ elif environ.get('REMOTE_ADDR'):
+ remote_addr = environ['REMOTE_ADDR']
+ d = {
+ 'REMOTE_ADDR': remote_addr,
+ 'REMOTE_USER': environ.get('REMOTE_USER') or '-',
+ 'REQUEST_METHOD': method,
+ 'REQUEST_URI': req_uri,
+ 'HTTP_VERSION': environ.get('SERVER_PROTOCOL'),
+ 'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,
+ 'status': status.split(None, 1)[0],
+ 'bytes': bytes,
+ 'HTTP_REFERER': environ.get('HTTP_REFERER', '-'),
+ 'HTTP_USER_AGENT': environ.get('HTTP_USER_AGENT', '-'),
+ }
+ message = self.format % d
+ self.logger.log(self.logging_level, message)
+
+def make_filter(
+ app, global_conf,
+ logger_name='wsgi',
+ format=None,
+ logging_level=logging.INFO,
+ setup_console_handler=True,
+ set_logger_level=logging.DEBUG):
+ from paste.util.converters import asbool
+ if isinstance(logging_level, (six.binary_type, six.text_type)):
+ logging_level = logging._levelNames[logging_level]
+ if isinstance(set_logger_level, (six.binary_type, six.text_type)):
+ set_logger_level = logging._levelNames[set_logger_level]
+ return TransLogger(
+ app,
+ format=format or None,
+ logging_level=logging_level,
+ logger_name=logger_name,
+ setup_console_handler=asbool(setup_console_handler),
+ set_logger_level=set_logger_level)
+
+make_filter.__doc__ = TransLogger.__doc__