Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stdlib.BoundLogger.log #42

Merged
merged 2 commits into from
Jan 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ structlog Package
.. automodule:: structlog.stdlib

.. autoclass:: BoundLogger
:members: bind, unbind, new, debug, info, warning, warn, error, critical, exception
:members: bind, unbind, new, debug, info, warning, warn, error, critical, exception, log

.. autoclass:: LoggerFactory
:members: __call__
Expand Down
3 changes: 2 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
Changelog
=========

- :feature:`42` Add :func:`structlog.stdlib.BoundLogger.log`.
- :feature:`19` Pass positional arguments to stdlib wrapped loggers that use string formatting.
- :feature:`28` structlog is now dually licensed under the `Apache License, Version 2 <http://choosealicense.com/licenses/apache-2.0/>`_ and the `MIT <http://choosealicense.com/licenses/mit/>`_ license.
Therefore it is now legal to use structlog with `GPLv2 <http://choosealicense.com/licenses/gpl-2.0/>`_-licensed projects.
- :feature:`22` :class:`structlog.stdlib.BoundLogger` now has an exception function.
- :feature:`22` Add :func:`structlog.stdlib.BoundLogger.exception`.
- :release:`0.4.2 <2014-07-26>`
- :bug:`8` Fixed a memory leak in greenlet code that emulates thread locals.
It shouldn't matter in practice unless you use multiple wrapped dicts within one program that is rather unlikely.
Expand Down
18 changes: 15 additions & 3 deletions structlog/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class BoundLogger(BoundLoggerBase):

Use it like::

configure(
structlog.configure(
wrapper_class=structlog.stdlib.BoundLogger,
)

Expand Down Expand Up @@ -94,6 +94,13 @@ def exception(self, event=None, *args, **kw):
kw['exc_info'] = True
return self.error(event, *args, **kw)

def log(self, level, event, *args, **kw):
"""
Process event and call the appropriate logging method depending on
`level`.
"""
return self._proxy_to_logger(_LEVEL_TO_NAME[level], event, *args, **kw)

fatal = critical

def _proxy_to_logger(self, method_name, event, *event_args,
Expand Down Expand Up @@ -281,7 +288,7 @@ def __call__(self, _, __, event_dict):
DEBUG = 10
NOTSET = 0

_nameToLevel = {
_NAME_TO_LEVEL = {
'critical': CRITICAL,
'error': ERROR,
'warn': WARNING,
Expand All @@ -291,6 +298,11 @@ def __call__(self, _, __, event_dict):
'notset': NOTSET,
}

_LEVEL_TO_NAME = dict(
(v, k) for k, v in _NAME_TO_LEVEL.items()
if k not in ("warn", "notset")
)


def filter_by_level(logger, name, event_dict):
"""
Expand All @@ -311,7 +323,7 @@ def filter_by_level(logger, name, event_dict):
...
DropEvent
"""
if logger.isEnabledFor(_nameToLevel[name]):
if logger.isEnabledFor(_NAME_TO_LEVEL[name]):
return event_dict
else:
raise DropEvent
21 changes: 15 additions & 6 deletions tests/test_stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ def build_bl(logger=None, processors=None, context=None):
)


def return_method_name(_, method_name, __):
"""
A final renderer that returns the name of the logging method.
"""
return method_name


class TestLoggerFactory(object):
def setup_method(self, method):
"""
Expand Down Expand Up @@ -133,20 +140,24 @@ def test_proxies_to_correct_method(self, method_name):
"""
The basic proxied methods are proxied to the correct counterparts.
"""
def return_method_name(_, method_name, __):
return method_name
bl = BoundLogger(ReturnLogger(), [return_method_name], {})
assert method_name == getattr(bl, method_name)('event')

def test_proxies_exception(self):
"""
BoundLogger.exception is proxied to Logger.error.
"""
def return_method_name(_, method_name, __):
return method_name
bl = BoundLogger(ReturnLogger(), [return_method_name], {})
assert "error" == bl.exception("event")

def test_proxies_log(self):
"""
BoundLogger.exception.log() is proxied to the apropriate method.
"""
bl = BoundLogger(ReturnLogger(), [return_method_name], {})
assert "critical" == bl.log(50, "event")
assert "debug" == bl.log(10, "event")

def test_positional_args_proxied(self):
"""
Positional arguments supplied are proxied as kwarg.
Expand Down Expand Up @@ -201,8 +212,6 @@ def test_exception_exc_info(self):
{"exc_info": True, "event": "event"}) == bl.exception('event')

def test_exception_maps_to_error(self):
def return_method_name(_, method_name, __):
return method_name
bl = BoundLogger(ReturnLogger(), [return_method_name], {})
assert "error" == bl.exception("event")

Expand Down