ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
Concepts
API Management
AI Gateway
MCP Gateway
    Overview
    Getting Started
      How it works
      Connect MCP clients
      Authentication
      Cross App Access
      Configuration
      Policies
      Observability
        AnalyticsLogging
      ReferenceServer RegistryTroubleshooting
    MCP Server
    Developer Portal
    Development
    Deploying & Source Control
    Analytics
    Observability
    Networking & Infrastructure
    Account Management
    Programming API
    Build with AI
    Zuplo CLI
    Migration Guides
    Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
    powered by Zuplo
    Observability

    Logging and OpenTelemetry

    The Zuplo MCP Gateway emits structured logs alongside its analytics events. Each log entry is keyed by an event string, carries auto-attached fields identifying the route and authenticated user, and is shaped to cross-reference the analytics dashboard one-to-one. This page explains the log model — what's emitted, what's deliberately excluded, and how the identifiers line up across analytics and logs.

    For the list of supported destinations and how to enable a log plugin, see Logging in the platform docs.

    Structured-first by design

    The gateway writes every log entry in the structured form, with a stable event key plus contextual fields:

    Code
    context.log.info( { event: "mcp_auth_downstream_token_issued", subjectId, operationId }, "Gateway issued an OAuth access token to an MCP client", );

    The event field is the searchable key. Names use snake-case throughout with mcp_ as the family prefix — mcp_auth_downstream_token_issued, mcp_auth_upstream_connection_established, mcp_capability_invoked, and so on. The human-readable message is for log readers; the event field and the structured properties are what dashboards, alerts, and queries run against.

    The three audiences

    Log entries serve three distinct audiences, and the gateway uses severity to distinguish them:

    • Audit entries (info) record OAuth lifecycle moments — token issuance, consent approval, upstream connection established, token revocation. They exist so a security or compliance team can answer "what auth events happened, when, and to whom?" without scanning request logs.
    • Visibility entries (debug and info) record request acceptance, response shaping, and other engineering-facing operational detail. They exist for the platform team running the gateway.
    • Error entries (error) include flattened error-chain fields — errName, errMessage, causeName, causeMessage, up to four cause levels — so the original failure context survives rethrows. The chain shape means a single log entry usually carries enough context to root-cause a failure without correlating multiple lines.

    Fields attached to every request log

    Three identifying fields are attached to every log entry emitted during an MCP request:

    • operationId — the route identity, the same value used as the virtualServerName in analytics.
    • upstreamServerId — the upstream id from the token exchange policy, populated once the upstream is resolved.
    • subjectId — the authenticated user's stable subject id, populated once the bearer token is validated.

    These fields make it trivial to filter a log provider by route, upstream, or user without scanning message bodies. The same three identifiers also appear on the corresponding analytics events, which is what lets an operator pivot from a Portal analytics view to the underlying log entries with the same filter values.

    Additional custom log properties can be attached from a policy or handler using context.log.setLogProperties(). See Logging → Custom log properties for the platform-level reference.

    What the gateway never logs

    The gateway is deliberately strict about credentials and secrets. The following are never written to any log entry:

    • Bearer tokens (downstream or upstream)
    • OAuth authorization codes
    • PKCE code verifiers
    • Client secrets
    • Raw signed JWTs (state, session, browser-ticket)
    • Full redirect URIs (only the host is logged, via a safeHost() helper)
    • Customer request bodies

    If a credential or body needs inspection for debugging, it's done through purpose-built tooling — the gateway's log surface is intentionally narrow, and widening it would defeat the audit guarantees.

    Cross-referencing with analytics

    The gateway uses the same identifiers in logs and analytics, so an operator can pivot between the two with the same filter values:

    • An analytics event's reasonCode matches the reasonCode field on the corresponding log entry (e.g., missing_token, invalid_audience, connect_required).
    • The analytics virtualServerName is the log entry's operationId.
    • The analytics subjectId is the log entry's subjectId.
    • The analytics upstreamServerName is the log entry's upstreamServerId.

    When the Analytics dashboard surfaces an interesting slice — say, a spike of connect_required reason codes for a specific upstream — the same dimensions filter the log provider directly. The two data surfaces are designed to reinforce each other, not duplicate each other.

    Routing the logs to your provider

    The MCP Gateway logs flow through the standard Zuplo logging pipeline. Enabling a log plugin in modules/zuplo.runtime.ts is independent of the MCP Gateway — once the plugin is wired up, gateway logs appear alongside the rest of the project's logs.

    Supported destinations include AWS CloudWatch, Datadog, Dynatrace, Google Cloud Logging, Loki, New Relic, Splunk, Sumo Logic, and VMware Log Insight. For destinations not in that list, the custom logging plugin pattern applies the same way.

    OpenTelemetry

    The gateway integrates with Zuplo's OpenTelemetry plugin to export traces and logs in OTLP format. Traces include spans for the request, every inbound policy (mcp-*-inbound), the handler, and the upstream fetch; logs export the same structured entries described above with their event field preserved.

    Registering both plugins is a small addition to modules/zuplo.runtime.ts:

    Code
    import { OpenTelemetryPlugin } from "@zuplo/otel"; import { environment, RuntimeExtensions } from "@zuplo/runtime"; import { McpGatewayPlugin } from "@zuplo/runtime/mcp-gateway"; export function runtimeInit(runtime: RuntimeExtensions) { runtime.addPlugin(new McpGatewayPlugin()); runtime.addPlugin( new OpenTelemetryPlugin({ traceUrl: environment.OTLP_TRACES_ENDPOINT, logUrl: environment.OTLP_LOGS_ENDPOINT, headers: { Authorization: environment.OTLP_AUTHORIZATION, }, service: { name: "mcp-gateway", }, }), ); }

    Logs are only exported when logUrl is set alongside traceUrl — configuring only exporter.url exports traces alone. Grafana Cloud is one common destination — define the endpoints and credentials as environment variables:

    TerminalCode
    OTLP_TRACES_ENDPOINT=https://otlp-gateway-prod-<region>.grafana.net/otlp/v1/traces OTLP_LOGS_ENDPOINT=https://otlp-gateway-prod-<region>.grafana.net/otlp/v1/logs OTLP_AUTHORIZATION=Basic <base64 of instance-id:api-token>

    The plugin sends OTLP data to the configured URLs exactly as given, so include the full /v1/traces and /v1/logs paths. Other OTLP-compatible destinations (Honeycomb, Dynatrace, New Relic, Tempo, self-hosted Jaeger) work the same way; substitute the endpoint and credential headers.

    Metrics export isn't supported by the current OpenTelemetry plugin. The plugin exports traces and logs only. For metrics, use a dedicated metrics plugin.

    Related

    • Analytics — the dashboard view of the same underlying events.
    • Logging — Zuplo's general logging guide, including custom log properties and the full plugin list.
    • OpenTelemetry — trace and log export configuration, including the available options on the plugin.
    • Metrics plugins — metrics destinations (separate from the OTel plugin).
    Edit this page
    Last modified on June 10, 2026
    AnalyticsReference
    On this page
    • Structured-first by design
    • The three audiences
    • Fields attached to every request log
    • What the gateway never logs
    • Cross-referencing with analytics
    • Routing the logs to your provider
    • OpenTelemetry
    • Related
    TypeScript
    TypeScript