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

[RFC] Allow to configure exception formatter #86

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions loguru/_handler.py
Expand Up @@ -93,12 +93,12 @@ def emit(self, record, level_color, ansi_message, raw):

formatter_record = record.copy()

if not record["exception"]:
error = ""
else:
if record["exception"] and self._exception_formatter:
type_, value, tb = record["exception"]
lines = self._exception_formatter.format_exception(type_, value, tb)
error = "".join(lines)
else:
error = ""

formatter_record["exception"] = error

Expand Down
40 changes: 24 additions & 16 deletions loguru/_logger.py
Expand Up @@ -32,6 +32,8 @@

start_time = now()

UNSET = object()


class Logger:
"""An object to dispatch logging messages to configured handlers.
Expand Down Expand Up @@ -163,6 +165,7 @@ def add(
diagnose=_defaults.LOGURU_DIAGNOSE,
enqueue=_defaults.LOGURU_ENQUEUE,
catch=_defaults.LOGURU_CATCH,
exception_formatter=UNSET,
**kwargs
):
r"""Add a handler sending log messages to a sink adequately configured.
Expand Down Expand Up @@ -201,6 +204,8 @@ def add(
Whether or not errors occurring while sink handles logs messages should be caught or
not. If ``True``, an exception message is displayed on |sys.stderr| but the exception is
not propagated to the caller, preventing your app to crash.
exception_formatter : |ExceptionFormatter|, optional
By default ``loguru._better_exceptions.ExceptionFormatter`` is used.
**kwargs
Additional parameters that will be passed to the sink while creating it or while
logging messages (the exact behavior depends on the sink type).
Expand Down Expand Up @@ -738,18 +743,6 @@ def filter_func(r):
"Invalid level value, it should be a positive integer, not: %d" % levelno
)

if isinstance(format, str):
formatter = format + "\n{exception}"
is_formatter_dynamic = False
elif callable(format):
formatter = format
is_formatter_dynamic = True
else:
raise ValueError(
"Invalid format, it should be a string or a function, not: '%s'"
% type(format).__name__
)

try:
encoding = sink.encoding
except AttributeError:
Expand All @@ -758,10 +751,7 @@ def filter_func(r):
if not encoding:
encoding = "ascii"

with self._lock:
handler_id = next(self._handlers_count)
colors = [lvl.color for lvl in self._levels.values()] + [""]

if exception_formatter is UNSET:
exception_formatter = ExceptionFormatter(
colorize=colorize,
encoding=encoding,
Expand All @@ -770,6 +760,24 @@ def filter_func(r):
hidden_frames_filename=self.catch.__code__.co_filename,
)

if isinstance(format, str):
formatter = format
if exception_formatter:
formatter += "\n{exception}"
is_formatter_dynamic = False
elif callable(format):
formatter = format
is_formatter_dynamic = True
else:
raise ValueError(
"Invalid format, it should be a string or a function, not: '%s'"
% type(format).__name__
)

with self._lock:
handler_id = next(self._handlers_count)
colors = [lvl.color for lvl in self._levels.values()] + [""]

handler = Handler(
writer=writer,
stopper=stopper,
Expand Down
17 changes: 17 additions & 0 deletions tests/test_formatting.py
Expand Up @@ -121,3 +121,20 @@ def test_extra_formatting(writer):
def test_invalid_color_markup(writer):
with pytest.raises(AnsiMarkupError):
logger.add(writer, format="<red>Not closed tag", colorize=True)


def test_handler_with_no_exception_formatter():
class Handler:
def write(self, message):
assert message == "got_exception"
self.passed_write = True

handler = Handler()
logger.add(handler, format="{message}", catch=False, exception_formatter=None)
assert [x._exception_formatter for x in logger._handlers.values()] == [None]
try:
raise ValueError
except Exception:
logger.exception("got_exception")

assert handler.passed_write is True