Lesson 23 / 25
Logging and Observability
Log every tool call safely so failures can be traced and costs understood.
A trace you can read
Record for every call: time, tool name, redacted arguments, duration, success or error, and result size. With these you can answer "why did the agent do that?" and "which tool is slow or failing?". Log to standard error or a file, and redact secrets and personal data before writing.
A logging wrapper
Wrap tool functions so every call is timed and logged. redact removes sensitive values. The log goes to a logger on standard error, never to stdout on a stdio server.
import functools, logging, time
log = logging.getLogger("mcp.tools")
def logged(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return fn(*args, **kwargs)
finally:
ms = (time.perf_counter() - start) * 1000
log.info("tool=%s args=%s ms=%.0f", fn.__name__, redact(kwargs), ms)
return wrapperQuick check: What must you do to arguments before logging them?
- Nothing, log everything
- Redact secrets and personal data
- Translate them to Hindi
- Encrypt the server
Answer
Redact secrets and personal data — Logs are widely readable and long-lived, so sensitive values must not be written to them.