MainSDK
Search⌘K
Integrations

OpenTelemetry

Use any OpenTelemetry backend for MainSDK logs and traces - OTLP endpoint, auth header as a secret, sampling, local testing and swapping.

2 interfaces2 driversWritten guide
Updated 2026-10-28

OpenTelemetry is the open standard for logs, traces and metrics, and the otel driver is how log sends your app's telemetry to a backend you already run. Point it at any endpoint that accepts OTLP: your own collector, or a hosted backend such as Datadog, Grafana, Honeycomb or Sentry. Pick it when your team already has an observability tool, or when telemetry must stay in infrastructure you control. Logging works with no configuration; this driver only decides where records go after they leave the terminal and the local event store.

Interfaces it serves

Pick OpenTelemetry in main.yaml for any of these. Your calls stay the same, and you can swap later.

Before you start

You need an endpoint that receives OTLP over HTTP/protobuf, and usually a token to authenticate to it.

  1. Choose where telemetry lands. Either run an OpenTelemetry Collector yourself, or use a backend that accepts OTLP directly. A collector is useful when you want to fan out to several backends or filter before data leaves your network.
  2. Get the OTLP HTTP endpoint. Collectors listen for OTLP over HTTP on port 4318 by default. Hosted backends publish their own OTLP endpoint, often per region; use the one in the region where you want the data stored.
  3. Create an ingest token. Most hosted backends issue an API or ingest key that goes in a request header. Use a key that can only write telemetry, not read or administer your account.
  4. Store the token in your secrets manager, never in code or a committed env file.

Configure

Two blocks: logging for what is recorded and redacted, telemetry for where it goes. Add otel to telemetry.to and give it the endpoint and headers.

main.yaml
logging: { audit: true, redact: [personal, sensitive, child, phi] }
telemetry:
  to: [terminal, otel]                          # any mix; default [terminal]
  otel:
    endpoint: https://otel.pinecart.dev:4318    # OTLP over HTTP/protobuf
    headers: { authorization: secret:OTEL_TOKEN }  # suggested name; your backend's ingest token
  sample: 0.2                                   # traces only

The header name your backend expects may differ from authorization; headers is a map, so use the name it documents and keep the value a secret: reference. A literal token in main.yaml fails mainsdk check with MAIN-CONFIG-0004.

Keep local logs in development and export only in production with an overlay:

main.env.prod.yaml
telemetry:
  to: [otel]
  sample: 0.2

Use it

Your code calls log as usual; nothing in it names OpenTelemetry.

app/api/orders/[id]/ship/route.ts
import { auth, log } from "mainsdk";

export async function POST(req: Request, { params }: { params: { id: string } }) {
  const session = await auth.require(req);
  try {
    log.info("order shipped", { orderId: params.id, by: session.user.id });
    return new Response(null, { status: 204 });
  } catch (err) {
    log.error(err, { orderId: params.id });
    throw err;
  }
}

Records follow the OpenTelemetry logs data model, carry W3C Trace Context ids, and arrive with the resource attributes service.name and mainsdk.project, so your backend can group them by app.

Local development and testing

  • No backend: the default terminal destination prints records and keeps recent events in .mainsdk/events/ (git-ignored) for 7 days.
  • A local collector: run an OpenTelemetry Collector on your machine and point telemetry.otel.endpoint at http://localhost:4318 to see exactly what your backend will receive.
  • Tests: createTestApp() from @mainsdk/testing runs log without a network, and chaos.on("log", { outage: true }) simulates the endpoint being down. Assertions on log and audit records in the testing kit are not defined yet.
app/api/orders/ship.test.ts
import { it } from "node:test";
import { createTestApp, chaos } from "@mainsdk/testing";

it("keeps serving when the telemetry endpoint is down", async () => {
  createTestApp({ main: "./main.yaml" });
  chaos.on("log", { outage: true });
  // ... call your route and assert it still returns 204
});

What MainSDK adds on top

  • Personal data and secrets redacted in process before any export: labelled fields, anything resolved from a secret: reference, and known key formats. User ids are pseudonymised with a keyed hash and IP addresses trimmed.
  • Only fields listed in the telemetry@1 contract are exported; anything else is dropped with MAIN-LOG-0005.
  • Export never blocks a request. It is asynchronous, buffered and bounded; when the endpoint is unreachable, telemetry is dropped with a telemetry.dropped counter.
  • Errors, policy decisions and audit records are never sampled; sample applies to traces only.

See Logs, audit and telemetry for the full contract.

Limits and notes

  • The otel driver comes with the log-telemetry spec (phase 1).
  • Export uses OTLP over HTTP/protobuf. Whether OTLP over gRPC is supported is not stated.
  • Data residency is wherever your endpoint is. MainSDK regions are US, EU and India; choose a backend region to match. Telemetry sent to MainSDK Cloud is tagged with the project's region.
  • authorization, cookie and set-cookie headers, cookies and request and response bodies are not collected by default.
  • Where audit records are exported (logging.export) and the audit log's production home are not decided yet.

Swap to or from it

Change the destination list. Your log.* calls and audit records stay the same.

main.yaml
telemetry:
  to: [otel, mainsdk]   # was [otel]

Moving from one OpenTelemetry backend to another is a change of endpoint and header. Past telemetry stays in the old backend; nothing needs to migrate.

FAQ

Which backends work? Any that accepts OTLP over HTTP, including your own collector. Datadog, Grafana, Honeycomb and Sentry all take the same export.

Will a slow or broken endpoint slow my app? No. Export is buffered and bounded, and drops with a counter rather than block a request.

Can I send to my backend and MainSDK Cloud at once? Yes. List both in telemetry.to. Nothing reaches MainSDK Cloud until you run mainsdk login and opt in.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet