OpenTelemetry
Use any OpenTelemetry backend for MainSDK logs and traces - OTLP endpoint, auth header as a secret, sampling, local testing and swapping.
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.
logBuilding for 0.2Driver otel Any OpenTelemetry backend
telemetry:
to: otelSwap to another provider
analyticsBuilding for 0.3Driver otel Any OpenTelemetry backend
analytics:
driver: otelSwap to another provider
Before you start
You need an endpoint that receives OTLP over HTTP/protobuf, and usually a token to authenticate to it.
- 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.
- 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.
- 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.
- 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.
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 onlyThe 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:
telemetry:
to: [otel]
sample: 0.2Use it
Your code calls log as usual; nothing in it names OpenTelemetry.
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
terminaldestination 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.endpointathttp://localhost:4318to see exactly what your backend will receive. - Tests:
createTestApp()from@mainsdk/testingrunslogwithout a network, andchaos.on("log", { outage: true })simulates the endpoint being down. Assertions on log and audit records in the testing kit are not defined yet.
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@1contract are exported; anything else is dropped withMAIN-LOG-0005. - Export never blocks a request. It is asynchronous, buffered and bounded; when the endpoint is unreachable, telemetry is dropped with a
telemetry.droppedcounter. - Errors, policy decisions and audit records are never sampled;
sampleapplies to traces only.
See Logs, audit and telemetry for the full contract.
Limits and notes
- The
oteldriver comes with thelog-telemetryspec (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,cookieandset-cookieheaders, 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.
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?