fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Document logging with OpenTelemetry

Open strawgate opened this issue 2 months ago • 3 comments

Enhancement

We should probably document emitting logs with OpenTelemetry, we may also be able to emit spans via middleware

strawgate avatar Oct 03 '25 23:10 strawgate

Claude finished @strawgate's task —— View job


Recommendation

Create documentation for integrating OpenTelemetry with FastMCP's existing logging infrastructure. FastMCP already has comprehensive logging capabilities (both server-side via logging.py utilities and MCP client logging via Context), structured logging middleware, and timing middleware. These provide the foundation for OpenTelemetry integration without requiring new code.

The documentation should focus on:

  1. Logging Integration: How to use OpenTelemetry's LoggingHandler with FastMCP's existing logger (fastmcp.utilities.logging.get_logger())
  2. Middleware-based Spans: How to create a custom middleware that emits OpenTelemetry spans for MCP operations
  3. Integration with StructuredLoggingMiddleware: How structured logs can be consumed by OpenTelemetry collectors
Findings

Current State

No OpenTelemetry Integration Exists

  • Code search found zero references to "opentelemetry", "otel", or related terms in the codebase
  • FastMCP has no dependencies on OpenTelemetry packages
  • This is purely a documentation gap, not a missing feature

Robust Logging Infrastructure Already Present FastMCP has comprehensive logging capabilities that can integrate with OpenTelemetry:

  1. Server-Side Logging (src/fastmcp/utilities/logging.py)

    • get_logger(): Creates loggers under the fastmcp.* namespace
    • configure_logging(): Configures Rich handlers with traceback support
    • Uses Python's standard logging module (compatible with OpenTelemetry's LoggingHandler)
  2. MCP Client Logging (docs/servers/logging.mdx)

    • Context-based logging methods (ctx.debug(), ctx.info(), etc.)
    • Supports structured logging with extra parameter
    • Sends logs back to MCP clients
  3. Middleware for Observability (src/fastmcp/server/middleware/)

    • LoggingMiddleware: Logs all MCP messages with configurable detail
    • StructuredLoggingMiddleware: Outputs JSON-structured logs
    • TimingMiddleware: Tracks request duration
    • All middleware supports payload inspection, token estimation, etc.
  4. Middleware Hook System (docs/servers/middleware.mdx)

    • Comprehensive hook hierarchy: on_message, on_request, on_call_tool, etc.
    • Perfect foundation for creating span-emitting middleware
    • Already documented with examples

Related Work

Issue #1501 discusses OpenTelemetry instrumentation for visualizing MCP calls in Grafana. The issue notes this might belong in the MCP SDK itself, but FastMCP could provide documentation on how users can add OpenTelemetry to their servers.

Recent Logging Improvements:

  • PR #1805: Consolidated logging and structured logging middleware
  • PR #1974: Added request duration logging
  • PR #1840: Added log level CLI configuration
  • Multiple PRs improving structured logging with extra parameter

Technical Feasibility

OpenTelemetry integration requires zero new code in FastMCP:

  1. Logging: Users can configure OpenTelemetry's LoggingHandler to capture FastMCP logs
  2. Spans: Users can create custom middleware using the documented middleware system
  3. Metrics: StructuredLoggingMiddleware already emits structured data (request counts, durations, token estimates)

The documentation would show users how to:

  • Install opentelemetry-api and opentelemetry-sdk packages
  • Configure OpenTelemetry providers (console, OTLP, etc.)
  • Attach OpenTelemetry's LoggingHandler to FastMCP's logger
  • Create span-emitting middleware using hooks like on_call_tool
  • Export to backends (Jaeger, Zipkin, Grafana, etc.)
Detailed Action Plan

1. Create New Documentation File

File: docs/integrations/opentelemetry.mdx Register in: docs/docs.json under integrations section

Content Structure:

---
title: OpenTelemetry Integration
description: Instrument your FastMCP server with OpenTelemetry for distributed tracing and observability
icon: chart-line
---

## Overview
Brief intro explaining OpenTelemetry and why it's useful for MCP servers

## Prerequisites
- Installation: `pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp`
- Link to OpenTelemetry Python docs

## Logging Integration

### Basic Setup
Show how to configure OpenTelemetry LoggingHandler:

```python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry._logs import set_logger_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from fastmcp import FastMCP
from fastmcp.utilities.logging import get_logger
import logging

# Configure OpenTelemetry
resource = Resource(attributes={"service.name": "my-fastmcp-server"})
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)

# Add OTLP exporter
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4317")
logger_provider.add_log_record_processor(BatchLogRecordProcessor(otlp_exporter))

# Attach to FastMCP logger
handler = LoggingHandler(logger_provider=logger_provider)
logger = get_logger("my_server")
logger.addHandler(handler)

mcp = FastMCP("My Server")
# Your tools here

Using with StructuredLoggingMiddleware

Show how structured logs integrate with OpenTelemetry collectors

Spans via Middleware

Creating a Tracing Middleware

Full example of middleware that creates spans for tool calls:

from fastmcp.server.middleware import Middleware, MiddlewareContext
from opentelemetry import trace

class OpenTelemetryMiddleware(Middleware):
    def __init__(self, tracer_name: str = "fastmcp"):
        self.tracer = trace.get_tracer(tracer_name)
    
    async def on_call_tool(self, context: MiddlewareContext, call_next):
        with self.tracer.start_as_current_span(
            f"tool.call.{context.message.name}",
            attributes={
                "mcp.method": context.method,
                "mcp.tool.name": context.message.name,
                "mcp.source": context.source,
            }
        ) as span:
            try:
                result = await call_next(context)
                span.set_attribute("mcp.success", True)
                return result
            except Exception as e:
                span.set_attribute("mcp.success", False)
                span.record_exception(e)
                raise

mcp.add_middleware(OpenTelemetryMiddleware())

Comprehensive Middleware Example

Show middleware that handles all operation types (tools, resources, prompts)

Exporting to Observability Backends

Console Exporter (Development)

Quick setup for local testing

OTLP Exporter (Production)

Configuration for Grafana, Jaeger, Zipkin, etc.

Custom Exporters

Brief mention of other export options

Best Practices

  • When to use logging vs spans
  • Performance considerations
  • Sampling strategies for high-volume servers
  • Security: avoiding logging sensitive data

Examples

Link to a full example in examples/ directory (optional)


### 2. Add OpenTelemetry Entry to docs.json

Add to the integrations section around line 150-170:

```json
{
  "group": "Integrations",
  "pages": [
    ...
    "integrations/opentelemetry",
    ...
  ]
}

3. Create Example (Optional but Recommended)

File: examples/opentelemetry_example.py

A working example demonstrating:

  • FastMCP server with tools
  • OpenTelemetry configuration (console exporter for simplicity)
  • Custom middleware emitting spans
  • LoggingHandler integration
  • Comments explaining each section

4. Update Related Documentation

Update docs/servers/middleware.mdx: Add a brief callout in the middleware examples section mentioning OpenTelemetry as a use case:

### Observability and Tracing

FastMCP's middleware system integrates seamlessly with observability platforms. See [OpenTelemetry Integration](/integrations/opentelemetry) for details on creating span-emitting middleware.

Update docs/servers/logging.mdx: Add a tip box mentioning OpenTelemetry:

<Tip>
For production observability and distributed tracing, consider integrating [OpenTelemetry](/integrations/opentelemetry) with FastMCP's logging infrastructure.
</Tip>

5. Testing

  • Verify all code examples run without errors
  • Test with at least one OpenTelemetry exporter (console exporter recommended)
  • Ensure documentation renders correctly in Mintlify
  • Check all internal links work

6. Consider Future Enhancements

Document but don't implement:

  • Potential for built-in OpenTelemetry middleware in fastmcp.server.middleware.observability
  • Automatic trace context propagation for composed/mounted servers
  • Integration with FastMCP Cloud (if applicable)
Related Issues and Pull Requests
Repository Issue or PR Relevance
jlowin/fastmcp #1501: OpenTelemetry instrumentation Directly requests OpenTelemetry support for visualizing MCP calls in Grafana. Mentions this might belong in the SDK but FastMCP could document it.
jlowin/fastmcp #1973: Add request duration to logging middleware Enhanced logging middleware to include timing data, which complements OpenTelemetry span duration tracking.
jlowin/fastmcp #1805: Refactor Logging and Structured Logging Middleware Consolidated logging middleware and added payload size/token estimation - structured data useful for OpenTelemetry.
jlowin/fastmcp #1840: Add log level support for stdio and HTTP transports Added CLI configuration for log levels, making it easier to control OpenTelemetry logging verbosity.
jlowin/fastmcp #1326: Structured client-side logging Added extra parameter support for structured logging, which aligns with OpenTelemetry's attribute model.
Related Files
Repository File Relevance Sections
jlowin/fastmcp src/fastmcp/utilities/logging.py Core logging utilities that can be extended with OpenTelemetry handlers. The get_logger() function creates loggers under the fastmcp namespace that can receive OpenTelemetry handlers. 13-22, 25-69
jlowin/fastmcp src/fastmcp/server/middleware/logging.py Logging middleware that already captures structured data (timestamps, payloads, tokens). This data can be exposed to OpenTelemetry via custom middleware. 143-196, 198-246
jlowin/fastmcp docs/servers/middleware.mdx Comprehensive middleware documentation showing the hook system. This is the foundation for creating OpenTelemetry span-emitting middleware. 67-103, 445-486
jlowin/fastmcp docs/servers/logging.mdx Server logging documentation covering Context-based logging. Should reference OpenTelemetry integration. 1-26, 56-73
jlowin/fastmcp src/fastmcp/server/middleware/timing.py Timing middleware that tracks request duration. Similar pattern can be used for OpenTelemetry span duration tracking. N/A (not read, but relevant based on middleware.mdx)

Summary

FastMCP has all the infrastructure needed for OpenTelemetry integration - it just needs documentation. The request is straightforward: document how users can integrate OpenTelemetry with FastMCP's existing logging and middleware systems. No code changes are required.

Key points:

  • Logging: FastMCP uses Python's standard logging module, which is directly compatible with OpenTelemetry's LoggingHandler
  • Spans: FastMCP's middleware hook system (on_call_tool, on_read_resource, etc.) is perfect for creating span-emitting middleware
  • Structured Data: StructuredLoggingMiddleware already emits JSON logs with request metadata that OpenTelemetry collectors can consume
  • Related Issue: #1501 requested this functionality for Grafana visualization

The documentation should be added to docs/integrations/opentelemetry.mdx with practical examples showing both logging integration and span creation via custom middleware.

--- • Branch: claude/issue-1998-20251004-0141

/marvin please thoroughly review OpenTelemetry Python documentation before getting started but give this a shot please

strawgate avatar Oct 04 '25 02:10 strawgate

Claude finished @strawgate's task —— View job • claude/issue-1998-20251004-0217 • Create PR ➔


Completed OpenTelemetry Documentation ✓

What I Did

Created comprehensive OpenTelemetry integration documentation that shows users how to instrument their FastMCP servers with distributed tracing and logging. The documentation is production-ready and includes working examples.

Changes Made

  1. New Documentation (docs/integrations/opentelemetry.mdx):

    • Complete guide to OpenTelemetry integration with FastMCP
    • Logging integration using OpenTelemetry's LoggingHandler
    • Custom middleware examples for creating spans
    • Production OTLP export configuration
    • Best practices for performance, security, and sampling
    • Console and OTLP exporter examples
  2. Updated Documentation:

    • docs/servers/middleware.mdx: Added tip referencing OpenTelemetry for production observability
    • docs/servers/logging.mdx: Added reference to OpenTelemetry integration guide
    • docs/docs.json: Registered new doc under "Observability" section in Integrations
  3. Working Example (examples/opentelemetry_example.py):

    • Complete weather server with OpenTelemetry instrumentation
    • Demonstrates tracing middleware with spans
    • Shows logging integration with LoggingHandler
    • Ready to run with console exporters (easily switch to OTLP)

Key Features of the Documentation

  • No FastMCP code changes required: Uses existing logging and middleware infrastructure
  • Production-ready: Shows both development (console) and production (OTLP) configurations
  • Comprehensive middleware examples: From basic request tracing to tool-specific spans
  • Best practices: Performance considerations, security, sampling strategies
  • Multiple backends: Works with Grafana, Jaeger, Zipkin, Datadog, etc.

Validation

  • ✅ uv sync completed successfully
  • ✅ uv run pre-commit run --all-files passed all checks
  • ✅ uv run pytest passed all 2765 tests

View job • Branch: claude/issue-1998-20251004-0217