Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/structlog/testing.py: 55%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: MIT OR Apache-2.0
2# This file is dual licensed under the terms of the Apache License, Version
3# 2.0, and the MIT License. See the LICENSE file in the root of this
4# repository for complete details.
6"""
7Helpers to test your application's logging behavior.
9.. versionadded:: 20.1.0
11See :doc:`testing`.
12"""
14from __future__ import annotations
16from collections.abc import Generator, Iterable
17from contextlib import contextmanager
18from typing import Any, NamedTuple, NoReturn
20from ._config import configure, get_config
21from ._log_levels import map_method_name
22from .exceptions import DropEvent
23from .typing import EventDict, Processor, WrappedLogger
26__all__ = [
27 "CapturedCall",
28 "CapturingLogger",
29 "CapturingLoggerFactory",
30 "LogCapture",
31 "ReturnLogger",
32 "ReturnLoggerFactory",
33 "capture_logs",
34]
37class LogCapture:
38 """
39 Class for capturing log messages in its entries list.
40 Generally you should use `structlog.testing.capture_logs`,
41 but you can use this class if you want to capture logs with other patterns.
43 :ivar List[structlog.typing.EventDict] entries: The captured log entries.
45 .. versionadded:: 20.1.0
47 .. versionchanged:: 24.3.0
48 Added mapping from "exception" to "error"
49 Added mapping from "warn" to "warning"
50 """
52 entries: list[EventDict]
54 def __init__(self) -> None:
55 self.entries = []
57 def __call__(
58 self, _: WrappedLogger, method_name: str, event_dict: EventDict
59 ) -> NoReturn:
60 event_dict["log_level"] = map_method_name(method_name)
61 self.entries.append(event_dict)
63 raise DropEvent
66@contextmanager
67def capture_logs(
68 processors: Iterable[Processor] = (),
69) -> Generator[list[EventDict], None, None]:
70 """
71 Context manager that appends all logging statements to its yielded list
72 while it is active. Disables all configured processors for the duration
73 of the context manager.
75 Attention: this is **not** thread-safe!
77 Args:
78 processors: Processors to apply before the logs are captured.
80 .. versionadded:: 20.1.0
81 .. versionadded:: 25.5.0 *processors* parameter
82 """
83 cap = LogCapture()
84 # Modify `_Configuration.default_processors` set via `configure` but always
85 # keep the list instance intact to not break references held by bound
86 # loggers.
87 configured_processors = get_config()["processors"]
88 old_processors = configured_processors.copy()
89 try:
90 # clear processors list and use LogCapture for testing
91 configured_processors.clear()
92 configured_processors.extend(processors)
93 configured_processors.append(cap)
94 configure(processors=configured_processors)
95 yield cap.entries
96 finally:
97 # remove LogCapture and restore original processors
98 configured_processors.clear()
99 configured_processors.extend(old_processors)
100 configure(processors=configured_processors)
103class ReturnLogger:
104 """
105 Return the arguments that it's called with.
107 >>> from structlog import ReturnLogger
108 >>> ReturnLogger().info("hello")
109 'hello'
110 >>> ReturnLogger().info("hello", when="again")
111 (('hello',), {'when': 'again'})
113 .. versionchanged:: 0.3.0
114 Allow for arbitrary arguments and keyword arguments to be passed in.
115 """
117 def msg(self, *args: Any, **kw: Any) -> Any:
118 """
119 Return tuple of ``args, kw`` or just ``args[0]`` if only one arg passed
120 """
121 # Slightly convoluted for backwards compatibility.
122 if len(args) == 1 and not kw:
123 return args[0]
125 return args, kw
127 log = debug = info = warn = warning = msg
128 fatal = failure = err = error = critical = exception = msg
131class ReturnLoggerFactory:
132 r"""
133 Produce and cache `ReturnLogger`\ s.
135 To be used with `structlog.configure`\ 's *logger_factory*.
137 Positional arguments are silently ignored.
139 .. versionadded:: 0.4.0
140 """
142 def __init__(self) -> None:
143 self._logger = ReturnLogger()
145 def __call__(self, *args: Any) -> ReturnLogger:
146 return self._logger
149class CapturedCall(NamedTuple):
150 """
151 A call as captured by `CapturingLogger`.
153 Can also be unpacked like a tuple.
155 Args:
156 method_name: The method name that got called.
158 args: A tuple of the positional arguments.
160 kwargs: A dict of the keyword arguments.
162 .. versionadded:: 20.2.0
163 """
165 method_name: str
166 args: tuple[Any, ...]
167 kwargs: dict[str, Any]
170class CapturingLogger:
171 """
172 Store the method calls that it's been called with.
174 This is nicer than `ReturnLogger` for unit tests because the bound logger
175 doesn't have to cooperate.
177 **Any** method name is supported.
179 .. versionadded:: 20.2.0
180 """
182 calls: list[CapturedCall]
184 def __init__(self) -> None:
185 self.calls = []
187 def __repr__(self) -> str:
188 return f"<CapturingLogger with {len(self.calls)} call(s)>"
190 def __getattr__(self, name: str) -> Any:
191 """
192 Capture call to `calls`
193 """
195 def log(*args: Any, **kw: Any) -> None:
196 self.calls.append(CapturedCall(name, args, kw))
198 return log
201class CapturingLoggerFactory:
202 r"""
203 Produce and cache `CapturingLogger`\ s.
205 Each factory produces and reuses only **one** logger.
207 You can access it via the ``logger`` attribute.
209 To be used with `structlog.configure`\ 's *logger_factory*.
211 Positional arguments are silently ignored.
213 .. versionadded:: 20.2.0
214 """
216 logger: CapturingLogger
218 def __init__(self) -> None:
219 self.logger = CapturingLogger()
221 def __call__(self, *args: Any) -> CapturingLogger:
222 return self.logger