# 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."""Primitives to deal with a concurrency supporting context, as introduced inPython 3.7 as :mod:`contextvars`... versionadded:: 20.1.0.. versionchanged:: 21.1.0 Reimplemented without using a single dict as context carrier for improved isolation. Every key-value pair is a separate `contextvars.ContextVar` now... versionchanged:: 23.3.0 Callsite parameters are now also collected under asyncio.See :doc:`contextvars`."""from__future__importannotationsimportcontextlibimportcontextvarsfromtypesimportFrameTypefromtypingimportAny,Generator,Mappingimportstructlogfrom.typingimportBindableLogger,EventDict,WrappedLoggerSTRUCTLOG_KEY_PREFIX="structlog_"STRUCTLOG_KEY_PREFIX_LEN=len(STRUCTLOG_KEY_PREFIX)_ASYNC_CALLING_STACK:contextvars.ContextVar[FrameType]=(contextvars.ContextVar("_ASYNC_CALLING_STACK"))# For proper isolation, we have to use a dict of ContextVars instead of a# single ContextVar with a dict.# See https://github.com/hynek/structlog/pull/302 for details._CONTEXT_VARS:dict[str,contextvars.ContextVar[Any]]={}
[docs]defget_contextvars()->dict[str,Any]:""" Return a copy of the *structlog*-specific context-local context. .. versionadded:: 21.2.0 """rv={}ctx=contextvars.copy_context()forkinctx:ifk.name.startswith(STRUCTLOG_KEY_PREFIX)andctx[k]isnotEllipsis:rv[k.name[STRUCTLOG_KEY_PREFIX_LEN:]]=ctx[k]returnrv
[docs]defget_merged_contextvars(bound_logger:BindableLogger)->dict[str,Any]:""" Return a copy of the current context-local context merged with the context from *bound_logger*. .. versionadded:: 21.2.0 """ctx=get_contextvars()ctx.update(structlog.get_context(bound_logger))returnctx
[docs]defmerge_contextvars(logger:WrappedLogger,method_name:str,event_dict:EventDict)->EventDict:""" A processor that merges in a global (context-local) context. Use this as your first processor in :func:`structlog.configure` to ensure context-local context is included in all log calls. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """ctx=contextvars.copy_context()forkinctx:ifk.name.startswith(STRUCTLOG_KEY_PREFIX)andctx[k]isnotEllipsis:event_dict.setdefault(k.name[STRUCTLOG_KEY_PREFIX_LEN:],ctx[k])returnevent_dict
[docs]defclear_contextvars()->None:""" Clear the context-local context. The typical use-case for this function is to invoke it early in request- handling code. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """ctx=contextvars.copy_context()forkinctx:ifk.name.startswith(STRUCTLOG_KEY_PREFIX):k.set(Ellipsis)
[docs]defbind_contextvars(**kw:Any)->Mapping[str,contextvars.Token[Any]]:r""" Put keys and values into the context-local context. Use this instead of :func:`~structlog.BoundLogger.bind` when you want some context to be global (context-local). Return the mapping of `contextvars.Token`\s resulting from setting the backing :class:`~contextvars.ContextVar`\s. Suitable for passing to :func:`reset_contextvars`. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 Return the `contextvars.Token` mapping rather than None. See also the toplevel note. """rv={}fork,vinkw.items():structlog_k=f"{STRUCTLOG_KEY_PREFIX}{k}"try:var=_CONTEXT_VARS[structlog_k]exceptKeyError:var=contextvars.ContextVar(structlog_k,default=Ellipsis)_CONTEXT_VARS[structlog_k]=varrv[k]=var.set(v)returnrv
[docs]defreset_contextvars(**kw:contextvars.Token[Any])->None:r""" Reset contextvars corresponding to the given Tokens. .. versionadded:: 21.1.0 """fork,vinkw.items():structlog_k=f"{STRUCTLOG_KEY_PREFIX}{k}"var=_CONTEXT_VARS[structlog_k]var.reset(v)
[docs]defunbind_contextvars(*keys:str)->None:""" Remove *keys* from the context-local context if they are present. Use this instead of :func:`~structlog.BoundLogger.unbind` when you want to remove keys from a global (context-local) context. .. versionadded:: 20.1.0 .. versionchanged:: 21.1.0 See toplevel note. """forkinkeys:structlog_k=f"{STRUCTLOG_KEY_PREFIX}{k}"ifstructlog_kin_CONTEXT_VARS:_CONTEXT_VARS[structlog_k].set(Ellipsis)
[docs]@contextlib.contextmanagerdefbound_contextvars(**kw:Any)->Generator[None,None,None]:""" Bind *kw* to the current context-local context. Unbind or restore *kw* afterwards. Do **not** affect other keys. Can be used as a context manager or decorator. .. versionadded:: 21.4.0 """context=get_contextvars()saved={k:context[k]forkincontext.keys()&kw.keys()}bind_contextvars(**kw)try:yieldfinally:unbind_contextvars(*kw.keys())bind_contextvars(**saved)