# SPDX-License-Identifier: MIT OR Apache-2.0# This file is dual licensed under the terms of the Apache License, Version# 2.0, and the MIT License. See the LICENSE file in the root of this# repository for complete details."""Helpers to test your application's logging behavior... versionadded:: 20.1.0See :doc:`testing`."""from__future__importannotationsfromcontextlibimportcontextmanagerfromtypingimportAny,Generator,NamedTuple,NoReturnfrom._configimportconfigure,get_configfrom._log_levelsimportmap_method_namefrom.exceptionsimportDropEventfrom.typingimportEventDict,WrappedLogger__all__=["CapturedCall","CapturingLogger","CapturingLoggerFactory","LogCapture","ReturnLogger","ReturnLoggerFactory","capture_logs",]
[docs]classLogCapture:""" Class for capturing log messages in its entries list. Generally you should use `structlog.testing.capture_logs`, but you can use this class if you want to capture logs with other patterns. :ivar List[structlog.typing.EventDict] entries: The captured log entries. .. versionadded:: 20.1.0 .. versionchanged:: 24.3.0 Added mapping from "exception" to "error" Added mapping from "warn" to "warning" """entries:list[EventDict]def__init__(self)->None:self.entries=[]def__call__(self,_:WrappedLogger,method_name:str,event_dict:EventDict)->NoReturn:event_dict["log_level"]=map_method_name(method_name)self.entries.append(event_dict)raiseDropEvent
[docs]@contextmanagerdefcapture_logs()->Generator[list[EventDict],None,None]:""" Context manager that appends all logging statements to its yielded list while it is active. Disables all configured processors for the duration of the context manager. Attention: this is **not** thread-safe! .. versionadded:: 20.1.0 """cap=LogCapture()# Modify `_Configuration.default_processors` set via `configure` but always# keep the list instance intact to not break references held by bound# loggers.processors=get_config()["processors"]old_processors=processors.copy()try:# clear processors list and use LogCapture for testingprocessors.clear()processors.append(cap)configure(processors=processors)yieldcap.entriesfinally:# remove LogCapture and restore original processorsprocessors.clear()processors.extend(old_processors)configure(processors=processors)
[docs]classReturnLogger:""" Return the arguments that it's called with. >>> from structlog import ReturnLogger >>> ReturnLogger().info("hello") 'hello' >>> ReturnLogger().info("hello", when="again") (('hello',), {'when': 'again'}) .. versionchanged:: 0.3.0 Allow for arbitrary arguments and keyword arguments to be passed in. """
[docs]defmsg(self,*args:Any,**kw:Any)->Any:""" Return tuple of ``args, kw`` or just ``args[0]`` if only one arg passed """# Slightly convoluted for backwards compatibility.iflen(args)==1andnotkw:returnargs[0]returnargs,kw
[docs]classReturnLoggerFactory:r""" Produce and cache `ReturnLogger`\ s. To be used with `structlog.configure`\ 's *logger_factory*. Positional arguments are silently ignored. .. versionadded:: 0.4.0 """def__init__(self)->None:self._logger=ReturnLogger()def__call__(self,*args:Any)->ReturnLogger:returnself._logger
[docs]classCapturedCall(NamedTuple):""" A call as captured by `CapturingLogger`. Can also be unpacked like a tuple. Args: method_name: The method name that got called. args: A tuple of the positional arguments. kwargs: A dict of the keyword arguments. .. versionadded:: 20.2.0 """method_name:strargs:tuple[Any,...]kwargs:dict[str,Any]
[docs]classCapturingLogger:""" Store the method calls that it's been called with. This is nicer than `ReturnLogger` for unit tests because the bound logger doesn't have to cooperate. **Any** method name is supported. .. versionadded:: 20.2.0 """calls:list[CapturedCall]def__init__(self)->None:self.calls=[]def__repr__(self)->str:returnf"<CapturingLogger with {len(self.calls)} call(s)>"def__getattr__(self,name:str)->Any:""" Capture call to `calls` """deflog(*args:Any,**kw:Any)->None:self.calls.append(CapturedCall(name,args,kw))returnlog
[docs]classCapturingLoggerFactory:r""" Produce and cache `CapturingLogger`\ s. Each factory produces and reuses only **one** logger. You can access it via the ``logger`` attribute. To be used with `structlog.configure`\ 's *logger_factory*. Positional arguments are silently ignored. .. versionadded:: 20.2.0 """logger:CapturingLoggerdef__init__(self)->None:self.logger=CapturingLogger()def__call__(self,*args:Any)->CapturingLogger:returnself.logger