> ## Documentation Index
> Fetch the complete documentation index at: https://fentaris.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Observability

> Structured logging and monitoring patterns.

> Capture consistent logs, metrics, and trace metadata for Fentaris proxy operations.

Start with `autoLog`, add stable tags in middleware, then forward structured entries to your telemetry pipeline when stdout is not enough.

## Quick Start

Enable automatic proxy logging on the high-level proxy declaration.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { fentaris, mcp, stdio } from "@fentaris/core";

const proxy = fentaris({
  servers: [
    mcp("filesystem", {
      transport: stdio({
        command: "npx",
        args: ["-y", "@modelcontextprotocol/server-filesystem", "/srv/shared"],
      }),
    }),
  ],
  autoLog: true,
});
```

This records request timing, subject metadata, server name, tool name, policy outcome, credential source metadata, and lifecycle events where available.

For JSON logs on stdout, pass `jsonConsoleLogger()` as the proxy logger.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { fentaris, jsonConsoleLogger } from "@fentaris/core";

const proxy = fentaris({
  logger: jsonConsoleLogger(),
  autoLog: true,
});
```

## Add Contextual Metadata

Use middleware to attach correlation tags that should appear on every log entry for a request.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.use(async (ctx, next) => {
  ctx.log.setTag("traceId", ctx.transport.requestId ?? "no-trace");
  ctx.log.setTag("tenantId", String(ctx.subject?.tenant?.id ?? "unknown"));
  ctx.log.setTag("subject", ctx.subject?.id ?? "anonymous");

  return next();
});
```

Prefer stable tags such as `traceId`, `tenantId`, `subject`, and `server`. Avoid raw prompts, raw tool arguments, API keys, bearer tokens, and decrypted credentials.

## Trace Tool Calls

Use server-scoped routes when you need custom logs for one upstream MCP server.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.mcp("github").tool("create_issue", async (ctx, next) => {
  const started = Date.now();
  const result = await next();

  ctx.log.info("github.create_issue.completed", {
    durationMs: Date.now() - started,
    policyAllowed: ctx.policy.allowed,
  });

  return result;
});
```

Keep event names stable. They become query keys in log stores and dashboards.

## Trace Resources And Prompts

Resource, prompt, and completion operations emit lifecycle events as well as tool calls.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.mcp("docs").operation("resource:read", async (ctx, next) => {
  ctx.log.info("docs.resource.read", {
    uri: ctx.resource?.uri,
    allowed: ctx.policy.allowed,
  });

  return next();
});

proxy.on("prompt:success", async ({ ctx, durationMs }) => {
  ctx.log.info("prompt.success", {
    prompt: ctx.prompt?.name,
    durationMs,
  });
});
```

Audit logs should include operation, subject, server, target, policy outcome, and credential source metadata.

Local capabilities declared with `app.local(name)` emit the same event families. The local namespace is reported as `ctx.server?.name`.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.on("tool:success", ({ ctx, durationMs }) => {
  if (ctx.server?.name === "workspace") {
    ctx.log.info("workspace.tool.success", {
      tool: ctx.tool?.name,
      durationMs,
    });
  }
});

proxy.on("resource:error", ({ ctx, error }) => {
  ctx.log.warn("resource.error", {
    namespace: ctx.server?.name,
    uri: ctx.resource?.uri,
    error: error?.message,
  });
});
```

## Use A Custom Driver

Pass a logger to `fentaris(...)` when stdout is not the right destination.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { Logger, fentaris, mcp, stdio } from "@fentaris/core";

const logger = new Logger({
  driver: {
    write(entry) {
      sendToTelemetry({
        level: entry.level,
        message: entry.message,
        tags: entry.tags,
        metadata: entry.metadata,
        timestamp: entry.timestamp,
      });
    },
  },
});

const proxy = fentaris({
  logger,
  autoLog: true,
  servers: [
    mcp("github", {
      transport: stdio({ command: "github-mcp-server" }),
    }),
  ],
});
```

If you use Datadog, Honeycomb, OpenTelemetry, or another structured telemetry system, forward the full entry and map tags to indexed fields.

## Recommended Signals

Track these signals for production operations:

* Tool call counts by server, tool, subject, and policy outcome.
* Denied calls and denial reasons.
* Latency by server, tool, operation, and upstream transport.
* Session start, session close, and transport errors.
* Resource, prompt, and completion operations by target.

## Metrics Aggregation

If you prefer metrics over logs, wrap the logger driver to emit counters and histograms while still writing structured logs.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
const logger = new Logger({
  driver: {
    write(entry) {
      metrics.increment(`fentaris.${entry.message}`, entry.tags);
      logs.write(entry);
    },
  },
});
```

<Note>
  Metrics should use bounded labels. Do not use raw resource URIs, prompt text, or user input as high-cardinality metric labels.
</Note>

## Related Documentation

* [Logger](/guides/logger)
* [Security](/guides/security)
* [Governance auth](/guides/governance-auth)
