Skip to content

Commit

Permalink
Merge branch 'main' into reset
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek authored Aug 28, 2021
2 parents 892d90a + b39a720 commit ceaca1e
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 51 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ Changes:
^^^^^^^^

- ``structlog`` is now importable if ``sys.stdout`` is ``None`` (e.g. when running using ``pythonw``).
- If the `better-exceptions <https://github.com/qix-/better-exceptions>`_ package is present, ``structlog.dev.ConsoleRenderer`` will now pretty-print exceptions using it.
Pass ``pretty_exceptions=False`` to disable.
- Exception rendering in ``structlog.dev.ConsoleLogger`` is now configurable using the ``exception_formatter`` setting.
If either the `rich <https://github.com/willmcgugan/rich>`_ or the `better-exceptions <https://github.com/qix-/better-exceptions>`_ package is present, ``structlog`` will use them for pretty-printing tracebacks.
``rich`` takes precedence over ``better-exceptions`` if both are present.

This only works if ``format_exc_info`` is **absent** in the processor chain.
- ``structlog.threadlocal.get_threadlocal()`` and ``structlog.contextvars.get_threadlocal()`` can now be used to get a copy of the current thread-local/context-local context that has been bound using ``structlog.threadlocal.bind_threadlocal()`` and ``structlog.contextvars.bind_contextvars()``.
- ``structlog.threadlocal.get_merged_threadlocal(bl)`` and ``structlog.contextvars.get_merged_contextvars(bl)`` do the same, but also merge the context from a bound logger *bl*.
Expand Down
Binary file modified docs/_static/console_renderer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ API Reference
.. autoclass:: ConsoleRenderer
:members: get_default_level_styles

.. autofunction:: plain_traceback
.. autofunction:: rich_traceback
.. autofunction:: better_traceback

.. autofunction:: set_exc_info


Expand Down Expand Up @@ -312,6 +316,7 @@ Please see :doc:`thread-local` for details.
.. autodata:: Processor
.. autodata:: Context
.. autodata:: ExcInfo
.. autodata:: ExceptionFormatter


`structlog.twisted` Module
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def find_version(*file_paths):
("py:class", "PlainFileObserver"),
("py:class", "TLLogger"),
("py:class", "TextIO"),
("py:class", "traceback"),
("py:class", "structlog._base.BoundLoggerBase"),
("py:class", "structlog.dev._Styles"),
("py:class", "structlog.types.EventDict"),
Expand Down
4 changes: 2 additions & 2 deletions docs/contextvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Context Variables
Historically, ``structlog`` only supported thread-local context binding.
With the introduction of :mod:`contextvars` in Python 3.7, there is now a way of having a global context that is local to the current context and even works in concurrent code such as code using :mod:`asyncio`.

For that ``structlog`` provides a the `structlog.contextvars` module with a set of functions to bind variables to a context-local context.
For that ``structlog`` provides the `structlog.contextvars` module with a set of functions to bind variables to a context-local context.
This context is safe to be used in asynchronous code.

The general flow is:
Expand All @@ -24,7 +24,7 @@ The general flow is:
- Call `structlog.contextvars.clear_contextvars` at the beginning of your request handler (or whenever you want to reset the context-local context).
- Call `structlog.contextvars.bind_contextvars` and `structlog.contextvars.unbind_contextvars` instead of your bound logger's ``bind()`` and ``unbind()`` when you want to bind and unbind key-value pairs to the context-local context.
- Use ``structlog`` as normal.
Loggers act as the always do, but the `structlog.contextvars.merge_contextvars` processor ensures that any context-local binds get included in all of your log messages.
Loggers act as they always do, but the `structlog.contextvars.merge_contextvars` processor ensures that any context-local binds get included in all of your log messages.

.. doctest::

Expand Down
6 changes: 5 additions & 1 deletion docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ Development
To make development a more pleasurable experience, ``structlog`` comes with the `structlog.dev` module.

The highlight is `structlog.dev.ConsoleRenderer` that offers nicely aligned and colorful (requires the `colorama package <https://pypi.org/project/colorama/>`_ if on Windows) console output.
If the `better-exceptions <https://github.com/Qix-/better-exceptions>`_ package is installed, it will also pretty-print exceptions with helpful contextual data.

If one of the `rich <https://rich.readthedocs.io/>`_ or `better-exceptions <https://github.com/Qix-/better-exceptions>`_ packages is installed, it will also pretty-print exceptions with helpful contextual data.
``rich`` takes precedence over ``better-exceptions``, but you can configure it by passing `structlog.dev.plain_traceback` or `structlog.dev.better_traceback` for the ``exception_formatter`` parameter of `ConsoleRenderer`.

The following output is rendered using ``rich``:

.. figure:: _static/console_renderer.png
:alt: Colorful console output by ConsoleRenderer.
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Installation

$ pip install structlog

If you'd like colorful output and pretty exceptions in development (you know you do!), install using::
If you want pretty exceptions in development (you know you do!), additionally install either `rich <https://github.com/willmcgugan/rich>`_ or `better-exceptions <https://github.com/qix-/better-exceptions>`_. Try both to find out which one you like better -- the screenshot in the README and docs homepage is rendered by ``rich``.

$ pip install structlog colorama better-exceptions
On Windows, you also have to install `colorama`_ if you want colorful output beside exceptions.


Your First Log Entry
Expand Down
114 changes: 91 additions & 23 deletions src/structlog/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@
import warnings

from io import StringIO
from typing import Any, Optional, Type, Union
from typing import Any, Optional, TextIO, Type, Union

from ._frames import _format_exception
from .types import EventDict, Protocol, WrappedLogger
from .types import (
EventDict,
ExceptionFormatter,
ExcInfo,
Protocol,
WrappedLogger,
)


try:
Expand All @@ -28,8 +34,21 @@
except ImportError:
better_exceptions = None

try:
import rich

from rich.console import Console
from rich.traceback import Traceback
except ImportError:
rich = None # type: ignore


__all__ = ["ConsoleRenderer"]
__all__ = [
"ConsoleRenderer",
"plain_traceback",
"rich_traceback",
"better_traceback",
]

_IS_WINDOWS = sys.platform == "win32"

Expand Down Expand Up @@ -137,13 +156,64 @@ class _PlainStyles:
kv_value = ""


def plain_traceback(sio: TextIO, exc_info: ExcInfo) -> None:
"""
"Pretty"-print *exc_info* to *sio* using our own plain formatter.
To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument.
Used by default if neither ``rich`` not ``better-exceptions`` are present.
.. versionadded:: 21.2
"""
sio.write("\n" + _format_exception(exc_info))


def rich_traceback(sio: TextIO, exc_info: ExcInfo) -> None:
"""
Pretty-print *exc_info* to *sio* using the ``rich`` package.
To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument.
Used by default if ``rich`` is installed.
.. versionadded:: 21.2
"""
sio.write("\n")
Console(file=sio, color_system="truecolor").print(
Traceback.from_exception(*exc_info, show_locals=True)
)


def better_traceback(sio: TextIO, exc_info: ExcInfo) -> None:
"""
Pretty-print *exc_info* to *sio* using the ``better-exceptions`` package.
To be passed into `ConsoleRenderer`'s ``exception_formatter`` argument.
Used by default if ``better-exceptions`` is installed and ``rich`` is
absent.
.. versionadded:: 21.2
"""
sio.write("\n" + "".join(better_exceptions.format_exception(*exc_info)))


if rich is not None:
default_exception_formatter = rich_traceback
elif better_exceptions is not None: # type: ignore
default_exception_formatter = better_traceback
else:
default_exception_formatter = plain_traceback


class ConsoleRenderer:
"""
Render ``event_dict`` nicely aligned, possibly in colors, and ordered.
If ``event_dict`` contains a true-ish ``exc_info`` key, it will be
rendered *after* the log line. If better-exceptions_ is present, in
colors and with extra context.
rendered *after* the log line. If rich_ or better-exceptions_ are present,
in colors and with extra context.
:param pad_event: Pad the event to this many characters.
:param colors: Use colors for a nicer output. `True` by default if
Expand All @@ -160,14 +230,17 @@ class ConsoleRenderer:
must be a dict from level names (strings) to colorama styles. The
default can be obtained by calling
`ConsoleRenderer.get_default_level_styles`
:param pretty_exceptions: Render exceptions with colors and extra
information. `True` by default if better-exceptions_ is installed.
:param exception_formatter: A callable to render ``exc_infos``. If rich_
or better-exceptions_ are installed, they are used for pretty-printing
by default (rich_ taking precendence). You can also manually set it to
`plain_traceback`, `better_traceback`, `rich_traceback`, or implement
your own.
Requires the colorama_ package if *colors* is `True`, and the
better-exceptions_ package if *pretty_exceptions* is `True`.
Requires the colorama_ package if *colors* is `True` **on Windows**.
.. _colorama: https://pypi.org/project/colorama/
.. _better-exceptions: https://pypi.org/project/better-exceptions/
.. _rich: https://pypi.org/project/rich/
.. versionadded:: 16.0
.. versionadded:: 16.1 *colors*
Expand All @@ -182,13 +255,14 @@ class ConsoleRenderer:
anymore because it breaks rendering.
.. versionchanged:: 21.1 It is additionally possible to set the logger name
using the ``logger_name`` key in the ``event_dict``.
.. versionadded:: 21.2 *pretty_exceptions*
.. versionadded:: 21.2 *exception_formatter*
.. versionchanged:: 21.2 `ConsoleRenderer` now handles the ``exc_info``
event dict key itself. Do **not** use the
`structlog.processors.format_exc_info` processor together with
`ConsoleRenderer` anymore! It will keep working, but you can't have
pretty exceptions and a warning will be raised if you ask for them.
.. versionchanged:: 21.3 The colors keyword now defaults to True on
customize exception formatting and a warning will be raised if you ask
for it.
.. versionchanged:: 21.2 The colors keyword now defaults to True on
non-Windows systems, and either True or False in Windows depending on
whether colorama is installed.
"""
Expand All @@ -200,7 +274,7 @@ def __init__(
force_colors: bool = False,
repr_native_str: bool = False,
level_styles: Optional[Styles] = None,
pretty_exceptions: bool = (better_exceptions is not None),
exception_formatter: ExceptionFormatter = default_exception_formatter,
):
styles: Styles
if colors:
Expand Down Expand Up @@ -241,7 +315,7 @@ def __init__(
)

self._repr_native_str = repr_native_str
self._pretty_exceptions = pretty_exceptions
self._exception_formatter = exception_formatter

def _repr(self, val: Any) -> str:
"""
Expand Down Expand Up @@ -331,17 +405,11 @@ def __call__(
if not isinstance(exc_info, tuple):
exc_info = sys.exc_info()

if self._pretty_exceptions:
sio.write(
"\n"
+ "".join(better_exceptions.format_exception(*exc_info))
)
else:
sio.write("\n" + _format_exception(exc_info))
self._exception_formatter(sio, exc_info)
elif exc is not None:
if self._pretty_exceptions:
if self._exception_formatter is not plain_traceback:
warnings.warn(
"Remove `render_exc_info` from your processor chain "
"Remove `format_exc_info` from your processor chain "
"if you want pretty exceptions."
)
sio.write("\n" + exc)
Expand Down
11 changes: 11 additions & 0 deletions src/structlog/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Mapping,
MutableMapping,
Optional,
TextIO,
Tuple,
Type,
Union,
Expand Down Expand Up @@ -81,6 +82,16 @@
"""


ExceptionFormatter = Callable[[TextIO, ExcInfo], None]
"""
A callable that pretty-prints an `ExcInfo` into a file-like object.
Used by `structlog.dev.ConsoleRenderer`.
.. versionadded:: 21.2
"""


@runtime_checkable
class BindableLogger(Protocol):
"""
Expand Down
Loading

0 comments on commit ceaca1e

Please sign in to comment.