Skip to content
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/

### Added

- Added `structlog.dev.get_active_console_renderer()` that returns the currently active `structlog.dev.ConsoleRenderer()`.
[#749](https://github.com/hynek/structlog/pull/749)

- `structlog.dev.ConsoleRenderer()` now supports setting the `exception_formatter` attribute.

You can now disable the pretty-printing of exceptions by setting it to `structlog.dev.plain_traceback`:

```python
cr = structlog.dev.get_active_console_renderer()
cr.exception_formatter = structlog.dev.plain_traceback
```
[#749](https://github.com/hynek/structlog/pull/749)

- Added `structlog.dev.ConsoleRenderer.get_default_column_styles` for reuse the default column styles.
[#741](https://github.com/hynek/structlog/pull/741)

Expand Down
6 changes: 5 additions & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ API Reference
.. automodule:: structlog.dev

.. autoclass:: ConsoleRenderer
:members: get_default_level_styles, get_default_column_styles
:members: get_default_level_styles, get_default_column_styles, exception_formatter

.. autofunction:: get_active_console_renderer
.. autoexception:: NoConsoleRendererConfiguredError
.. autoexception:: MultipleConsoleRenderersConfiguredError

.. autoclass:: Column
.. autoclass:: ColumnFormatter(typing.Protocol)
Expand Down
9 changes: 8 additions & 1 deletion docs/console-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,11 @@ It's possible to override this behavior by setting two standard environment vari

## Disabling exception pretty-printing

If you prefer the default terse Exception rendering, but still want Rich installed, you can disable the pretty-printing by instantiating {class}`structlog.dev.ConsoleRenderer()` yourself and passing `exception_formatter=structlog.dev.plain_traceback`.
If you prefer the default terse Exception rendering, but still want Rich installed, you can disable the auto-enabled pretty-printing by configuring your {class}`~structlog.dev.ConsoleRenderer` to use {class}`structlog.dev.plain_traceback`.

You can either instantiate {class}`structlog.dev.ConsoleRenderer()` yourself and pass `exception_formatter=structlog.dev.plain_traceback`, or set the `exception_formatter` attribute of the active console renderer to it:

```python
cr = structlog.dev.get_active_console_renderer()
cr.exception_formatter = structlog.dev.plain_traceback
```
59 changes: 59 additions & 0 deletions src/structlog/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
)

from ._frames import _format_exception
from .exceptions import (
MultipleConsoleRenderersConfiguredError,
NoConsoleRendererConfiguredError,
)
from .processors import _figure_out_exc_info
from .typing import EventDict, ExceptionRenderer, ExcInfo, WrappedLogger

Expand Down Expand Up @@ -463,6 +467,13 @@ class ConsoleRenderer:
*after* the log line. If Rich_ or better-exceptions_ are present, in colors
and with extra context.

Tip:
Since `ConsoleRenderer` is mainly a development helper, it is less
strict about immutability than the rest of *structlog* for better
ergonomics. Notably, the currently active instance can be obtained by
calling `get_active_console_renderer()` and it offers properties to
configure its behavior after instantiation.

Args:
columns:
A list of `Column` objects defining both the order and format of
Expand Down Expand Up @@ -829,6 +840,22 @@ def get_default_level_styles(colors: bool = True) -> dict[str, str]:
"notset": styles.level_notset,
}

@property
def exception_formatter(self) -> ExceptionRenderer:
"""
The exception formatter used by this console renderer.

.. versionadded:: 25.5.0
"""
return self._exception_formatter

@exception_formatter.setter
def exception_formatter(self, value: ExceptionRenderer) -> None:
"""
.. versionadded:: 25.5.0
"""
self._exception_formatter = value


_SENTINEL = object()

Expand All @@ -852,3 +879,35 @@ def set_exc_info(
event_dict["exc_info"] = True

return event_dict


def get_active_console_renderer() -> ConsoleRenderer:
"""
If *structlog* is configured to use `ConsoleRenderer`, it's returned.

It does not have to be the last processor.

Raises:
NoConsoleRendererConfiguredError:
If no ConsoleRenderer is found in the current configuration.

MultipleConsoleRenderersConfiguredError:
If more than one is found in the current configuration. This is
almost certainly a bug.

.. versionadded:: 25.5.0
"""
from ._config import get_config

cr = None
for p in get_config()["processors"]:
if isinstance(p, ConsoleRenderer):
if cr is not None:
raise MultipleConsoleRenderersConfiguredError

cr = p

if cr is None:
raise NoConsoleRendererConfiguredError

return cr
17 changes: 17 additions & 0 deletions src/structlog/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ class DropEvent(BaseException):

Derives from BaseException because it's technically not an error.
"""


class NoConsoleRendererConfiguredError(Exception):
"""
A user asked for the current `structlog.dev.ConsoleRenderer` but none is
configured.

.. versionadded:: 25.5.0
"""


class MultipleConsoleRenderersConfiguredError(Exception):
"""
A user asked for the current `structlog.dev.ConsoleRenderer` and more than one is configured.

.. versionadded:: 25.5.0
"""
51 changes: 51 additions & 0 deletions tests/test_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import pytest

import structlog

from structlog import dev


Expand Down Expand Up @@ -682,6 +684,18 @@ def test_does_not_modify_styles(self):

assert copy == styles

def test_exception_formatter_property(self, cr):
"""
The exception formatter can be set and retrieved without
re-instantiating ConsoleRenderer.
"""
sentinel = object()

cr.exception_formatter = sentinel

assert sentinel is cr.exception_formatter
assert sentinel is cr._exception_formatter


class TestSetExcInfo:
def test_wrong_name(self):
Expand Down Expand Up @@ -820,3 +834,40 @@ def test_no_style(self):
assert "[critical]" == dev.LogLevelColumnFormatter(None, "foo")(
"", "critical"
)


class TestGetActiveConsoleRenderer:
def test_ok(self):
"""
If there's an active ConsoleRenderer, it's returned.
"""
assert (
structlog.get_config()["processors"][-1]
is dev.get_active_console_renderer()
)

def test_no_console_renderer(self):
"""
If no ConsoleRenderer is configured, raise
NoConsoleRendererConfiguredError.
"""
structlog.configure(processors=[])

with pytest.raises(
structlog.exceptions.NoConsoleRendererConfiguredError
):
dev.get_active_console_renderer()

def test_multiple_console_renderers(self):
"""
If multiple ConsoleRenderers are configured, raise
MultipleConsoleRenderersConfiguredError because it's most likely a bug.
"""
structlog.configure(
processors=[dev.ConsoleRenderer(), dev.ConsoleRenderer()]
)

with pytest.raises(
structlog.exceptions.MultipleConsoleRenderersConfiguredError
):
dev.get_active_console_renderer()
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ commands = cog -rP README.md docs/index.md

[testenv:pre-commit]
skip_install = true
deps = pre-commit
commands = pre-commit run --all-files
deps = prek
commands = prek run --all-files


[testenv:mypy-pkg]
Expand Down