mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 20:44:17 +02:00
Add verbose logging to Context for debugging Lambda ContextVar issue
Adds detailed logging to Context.__aenter__ and __aexit__ to track: - When Context is entered/exited - Values of server._docket and server._worker - ContextVar values before and after setting - Token values for debugging reset issues This will help diagnose why ContextVars might not propagate in Lambda. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b32f7f5f99
commit
6e8c60b65e
1 changed files with 60 additions and 0 deletions
|
|
@ -175,14 +175,25 @@ class Context:
|
|||
|
||||
async def __aenter__(self) -> Context:
|
||||
"""Enter the context manager and set this context as the current context."""
|
||||
import socket
|
||||
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
instance_id = f"{socket.gethostname()}#{id(self.fastmcp)}"
|
||||
|
||||
logger.info(f"[{instance_id}] Context.__aenter__ ENTERING")
|
||||
|
||||
parent_context = _current_context.get(None)
|
||||
if parent_context is not None:
|
||||
# Inherit state from parent context
|
||||
self._state = copy.deepcopy(parent_context._state)
|
||||
logger.info(f"[{instance_id}] Context: inherited state from parent context")
|
||||
|
||||
# Always set this context and save the token
|
||||
token = _current_context.set(self)
|
||||
self._tokens.append(token)
|
||||
logger.info(f"[{instance_id}] Context: _current_context SET (token={token})")
|
||||
|
||||
# Set current server for dependency injection (use weakref to avoid reference cycles)
|
||||
from fastmcp.server.dependencies import (
|
||||
|
|
@ -192,20 +203,55 @@ class Context:
|
|||
)
|
||||
|
||||
self._server_token = _current_server.set(weakref.ref(self.fastmcp))
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: _current_server SET (token={self._server_token})"
|
||||
)
|
||||
|
||||
# Set docket/worker from server instance for this request's context.
|
||||
# This ensures ContextVars work even in environments (like Lambda) where
|
||||
# lifespan ContextVars don't propagate to request handlers.
|
||||
server = self.fastmcp
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: server._docket={server._docket}, "
|
||||
f"server._worker={server._worker}, "
|
||||
f"_current_docket.get() BEFORE={_current_docket.get()}"
|
||||
)
|
||||
if server._docket is not None:
|
||||
self._docket_token = _current_docket.set(server._docket)
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: _current_docket SET from server._docket "
|
||||
f"(token={self._docket_token}), _current_docket.get() AFTER={_current_docket.get()}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[{instance_id}] Context: server._docket is None, skipping _current_docket.set()"
|
||||
)
|
||||
|
||||
if server._worker is not None:
|
||||
self._worker_token = _current_worker.set(server._worker)
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: _current_worker SET from server._worker "
|
||||
f"(token={self._worker_token})"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[{instance_id}] Context: server._worker is None, skipping _current_worker.set()"
|
||||
)
|
||||
|
||||
logger.info(f"[{instance_id}] Context.__aenter__ COMPLETE")
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
"""Exit the context manager and reset the most recent token."""
|
||||
import socket
|
||||
|
||||
from fastmcp.utilities.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
instance_id = f"{socket.gethostname()}#{id(self.fastmcp)}"
|
||||
|
||||
logger.info(f"[{instance_id}] Context.__aexit__ ENTERING (exc_type={exc_type})")
|
||||
|
||||
# Flush any remaining notifications before exiting
|
||||
await self._flush_notifications()
|
||||
|
||||
|
|
@ -217,20 +263,34 @@ class Context:
|
|||
)
|
||||
|
||||
if hasattr(self, "_worker_token"):
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: resetting _current_worker (token={self._worker_token})"
|
||||
)
|
||||
_current_worker.reset(self._worker_token)
|
||||
delattr(self, "_worker_token")
|
||||
if hasattr(self, "_docket_token"):
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: resetting _current_docket (token={self._docket_token})"
|
||||
)
|
||||
_current_docket.reset(self._docket_token)
|
||||
delattr(self, "_docket_token")
|
||||
if hasattr(self, "_server_token"):
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: resetting _current_server (token={self._server_token})"
|
||||
)
|
||||
_current_server.reset(self._server_token)
|
||||
delattr(self, "_server_token")
|
||||
|
||||
# Reset context token
|
||||
if self._tokens:
|
||||
token = self._tokens.pop()
|
||||
logger.info(
|
||||
f"[{instance_id}] Context: resetting _current_context (token={token})"
|
||||
)
|
||||
_current_context.reset(token)
|
||||
|
||||
logger.info(f"[{instance_id}] Context.__aexit__ COMPLETE")
|
||||
|
||||
@property
|
||||
def request_context(self) -> RequestContext[ServerSession, Any, Request] | None:
|
||||
"""Access to the underlying request context.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue