Skip to content

Commit

Permalink
Correct recipe for custom level due to 'opt()' not chainable (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
Delgan committed Dec 27, 2019
1 parent da9e51e commit d4851c4
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 8 deletions.
26 changes: 22 additions & 4 deletions docs/resources/recipes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,33 @@ After adding a new level, it's habitually used with the |log| function::

For convenience, one can assign a new logging function which automatically uses the custom added level::

def foobar(self, message, *args, **kwargs):
self.opt(depth=1).log("foobar", message, *args, **kwargs)
from functools import partialmethod

logger.__class__.foobar = foobar
logger.__class__.foobar = partialmethod(logger.__class__.log, "foobar")

logger.foobar("A message")


The new method need to be added only once and will be usable across all your files importing the ``logger``. Note that the call to ``opt(depth=1)`` is necessary to make sure that the logged message contains contextual information of the parent stack frame (where ``logger.foobar()`` is called). Also, assigning the method to ``logger.__class__`` rather than ``logger`` directly ensures that it stays available even after calling ``logger.bind()``, ``logger.patch()`` and ``logger.opt()`` (because these functions return a new ``logger`` instance).
The new method need to be added only once and will be usable across all your files importing the ``logger``. Assigning the method to ``logger.__class__`` rather than ``logger`` directly ensures that it stays available even after calling ``logger.bind()``, ``logger.patch()`` and ``logger.opt()`` (because these functions return a new ``logger`` instance).


Preserving an ``opt()`` parameter for the whole module
------------------------------------------------------

Supposing you wish to color each of your log messages without having to call ``logger.opt(ansi=True)`` every time, you can add this at the very beginning of your module::

logger = logger.opt(ansi=True)

logger.info("It <green>works</>!")

However, it should be noted that it's not possible to chain |opt| calls, using this method again will reset the ``ansi`` option to its default value (which is ``False``). For this reason, it is also necessary to patch the |opt| method so that all subsequent calls continue to use the desired value::

from functools import partial

logger = logger.opt(ansi=True)
logger.opt = partial(logger.opt, ansi=True)

logger.opt(raw=True).info("It <green>still</> works!\n")


Dynamically formatting messages to properly align values with padding
Expand Down
3 changes: 3 additions & 0 deletions loguru/_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,9 @@ def catch_wrapper(*args, **kwargs):
def opt(self, *, exception=None, record=False, lazy=False, ansi=False, raw=False, depth=0):
r"""Parametrize a logging call to slightly change generated log message.
Note that it's not possible to chain |opt| calls, the last one takes precedence over the
others as it will "reset" the options to their default values.
Parameters
----------
exception : |bool|, |tuple| or |Exception|, optional
Expand Down
6 changes: 2 additions & 4 deletions tests/test_levels.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import functools
from loguru import logger
from loguru._ansimarkup import AnsiMarkup

Expand Down Expand Up @@ -168,10 +169,7 @@ def test_updating_min_level(writer):
def test_assign_custom_level_method(writer):
logger.level("foobar", no=33, icon="🤖", color="<blue>")

def foobar(self, message, *args, **kwargs):
self.opt(depth=1).log("foobar", message, *args, **kwargs)

logger.__class__.foobar = foobar
logger.__class__.foobar = functools.partialmethod(logger.__class__.log, "foobar")
logger.foobar("Message not logged")
logger.add(
writer,
Expand Down

0 comments on commit d4851c4

Please sign in to comment.