Skip to content
Browse files

Abandoning library. Checking in last changes and a patch for flask to…

… make it somewhat work
  • Loading branch information...
1 parent 326ff0a commit 2005a121d558c00f044b6ded04c095682e5b0d54 @mitsuhiko committed Jul 2, 2010
Showing with 174 additions and 27 deletions.
  1. +102 −0 flask-add-log-wrappers.patch
  2. +72 −27 flaskext/extended_logging.py
View
102 flask-add-log-wrappers.patch
@@ -0,0 +1,102 @@
+diff --git a/flask.py b/flask.py
+index 90867fc..f01c94e 100644
+--- a/flask.py
++++ b/flask.py
+@@ -944,10 +944,20 @@ class Flask(_PackageBoundObject):
+ #: to load a config from files.
+ self.config = Config(self.root_path, self.default_config)
+
+- #: Prepare the deferred setup of the logger.
++ # Prepare the deferred setup of the logger.
+ self._logger = None
++
++ #: The name of the logger
++ #:
++ #: .. versionadded:: 0.4
+ self.logger_name = self.import_name
+
++ #: A list of wrapper classes to be applied to the logger. By
++ #: default this list is empty but can be filled by extensions
++ #: or application code with callables that are passed the
++ #: current decorated logger and return a new one.
++ self.logger_wrappers = []
++
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+@@ -1058,9 +1068,26 @@ class Flask(_PackageBoundObject):
+ logger = getLogger(self.logger_name)
+ logger.__class__ = DebugLogger
+ logger.addHandler(handler)
++ for wrapper in self.logger_wrappers:
++ logger = wrapper(logger)
+ self._logger = logger
+ return logger
+
++ def logwrapper(self, f):
++ """Here an example log wrapper that injects an `app_version`
++ variable to the logging system. This can be used by
++ applications and extensions to add a proxy for a logger.
++
++ Keep in mind that all attributes have to be forwarded to
++ the proxied class, a standard :class:`logging.LoggerAdapter`
++ is not sufficient.
++
++ .. versionadded:: 0.5
++ """
++ self.logger_wrappers.append(f)
++ self._logger = None
++ return f
++
+ def create_jinja_loader(self):
+ """Creates the Jinja loader. By default just a package loader for
+ the configured package is returned that looks up templates in the
+diff --git a/tests/flask_tests.py b/tests/flask_tests.py
+index 1c5dc72..64fbb5c 100644
+--- a/tests/flask_tests.py
++++ b/tests/flask_tests.py
+@@ -16,7 +16,7 @@ import sys
+ import flask
+ import unittest
+ import tempfile
+-from logging import StreamHandler
++import logging
+ from contextlib import contextmanager
+ from datetime import datetime
+ from werkzeug import parse_date, parse_options_header
+@@ -750,7 +750,7 @@ class LoggingTestCase(unittest.TestCase):
+ out = StringIO()
+ app = flask.Flask(__name__)
+ app.logger_name = 'flask_tests/test_exception_logging'
+- app.logger.addHandler(StreamHandler(out))
++ app.logger.addHandler(logging.StreamHandler(out))
+
+ @app.route('/')
+ def index():
+@@ -788,6 +788,26 @@ class LoggingTestCase(unittest.TestCase):
+ assert rv.status_code == 500
+ assert rv.data == 'Hello Server Error'
+
++ def test_logger_wrappers(self):
++ out = StringIO()
++ class LoggerWrapper(logging.LoggerAdapter):
++ def __getattr__(self, name):
++ return getattr(self.logger, name)
++
++ app = flask.Flask(__name__)
++ app.logger_name = __name__ + '/test_logger_wrappers'
++ @app.logwrapper
++ def app_version_log_wrapper(logger):
++ return LoggerWrapper(logger, extra={
++ 'app_version': '1.0'
++ })
++ handler = logging.StreamHandler(out)
++ handler.setFormatter(logging.Formatter('%(app_version)s:%(message)s'))
++ app.logger.addHandler(handler)
++
++ app.logger.error('Testing')
++ assert out.getvalue().strip() == '1.0:Testing'
++
+
+ class ConfigTestCase(unittest.TestCase):
+
View
99 flaskext/extended_logging.py
@@ -3,18 +3,29 @@
flaskext.extended_logging
~~~~~~~~~~~~~~~~~~~~~~~~~
- Description of the module goes here...
+ Abandoned:
+
+ problems with logging:
+
+ - loggers cannot be wrapped to inject additional information.
+ The locations are totally broken then because logging uses
+ non-configurable magic to look up locations.
+
+ - loggers cannot be deleted which is painful for unittesting.
+ There just should not be a different loggers in a registry.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+import sys
+import time
import traceback
from datetime import datetime
from flask import _request_ctx_stack
-from logging import LoggerAdapter, Formatter
+from logging import Formatter
-class LoggerWrapper(LoggerAdapter, object):
+class LoggerWrapper(object):
"""Wrapps a logger to inject additional variables into the
format string automatically.
@@ -38,9 +49,63 @@ class LoggerWrapper(LoggerAdapter, object):
"""
def __init__(self, logger):
- LoggerAdapter.__init__(self, logger, {})
+ self.logger = logger
self.extra_handlers = []
+ def process(self, msg, kwargs):
+ path = method = remote_addr = user_agent = url = u''
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ path = ctx.request.path
+ url = ctx.request.url
+ method = ctx.request.method
+ remote_addr = ctx.request.remote_addr
+ user_agent = ctx.request.headers.get('user-agent', u'')
+ kwargs['extra'] = dict(
+ http_path=path,
+ http_url=url,
+ http_method=method,
+ http_remote_addr=remote_addr,
+ http_user_agent=user_agent
+ )
+ for handler in self.extra_handlers:
+ handler(kwargs['extra'], ctx)
+ return msg, kwargs
+
+ def debug(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.debug(msg, *args, **kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.info(msg, *args, **kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.warning(msg, *args, **kwargs)
+
+ def error(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.error(msg, *args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ kwargs['exc_info'] = sys.exc_info()
+ # exception won't work, because it does not
+ # accept an extra argument (for whatever reason)
+ self.logger.error(msg, *args, **kwargs)
+
+ def critical(self, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.critical(msg, *args, **kwargs)
+
+ def log(self, level, msg, *args, **kwargs):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.log(level, msg, *args, **kwargs)
+
+ def __getattr__(self, name):
+ return getattr(self.logger, name)
+
@property
def inject(self, f):
"""Registers a function that is passed a dictionary with values
@@ -63,27 +128,6 @@ def log_user(d, ctx):
self.extra_handlers.append(f)
return f
- def process(self, msg, kwargs):
- msg, kwargs = LoggerAdapter.process(self, msg, kwargs)
- path = method = remote_addr = user_agent = url = u''
- ctx = _request_ctx_stack.top
- if ctx is not None:
- path = ctx.request.path
- url = ctx.request.url
- method = ctx.request.method
- remote_addr = ctx.request.remote_addr
- user_agent = ctx.request.headers.get('user-agent', u'')
- kwargs['extra'].update(
- http_path=path,
- http_url=url,
- http_method=method,
- http_remote_addr=remote_addr,
- http_user_agent=user_agent
- )
- for handler in self.extra_handlers:
- handler(kwargs['extra'], ctx)
- return msg, kwargs
-
class _ExceptionInfo(object):
@@ -181,7 +225,7 @@ def format(self, record):
continue
# we pass datetime objects to jinja, they are easier to handle.
if key == 'created':
- value = datetime(*value[:7])
+ value = datetime(*time.gmtime(value)[:7])
# make sure strings are unicode
if isinstance(value, str):
value = value.decode('utf-8', 'replace')
@@ -190,9 +234,10 @@ def format(self, record):
# the exception information is an object with a few methods to
# test and format exceptions
context['exc_info'] = _ExceptionInfo(context.get('exc_info'))
+ context['message'] = record.getMessage()
return self.template.render(context)
def init_extended_logging(app):
"""Activates extended logging for the given application."""
- app.logger = LoggerWrapper(app.logger)
+ app.logwrapper(LoggerWrapper)

0 comments on commit 2005a12

Please sign in to comment.
Something went wrong with that request. Please try again.