Documentation / JavaScript

Integrate a JavaScript or TypeScript app with Logister.

The best path for JavaScript and TypeScript projects is the published npm package logister-js. It is aimed at the kinds of JavaScript teams that need visibility across HTTP handlers, background jobs, scripts, and console output, with optional Express middleware and console capture when you need them.

Before you start

Create the project with the JavaScript / TypeScript integration type.

What you needWhy it matters
Project API tokenAuthenticates your app when sending events
Logister instance base URLUsed to build the ingest, check-in, and deployment endpoints
Node.js 22 or newerRequired by the published logister-js package for Node-based apps and build tooling
Optional GitHub App installationLets Logister use repository, commit_sha, branch, and deployment records for source excerpts and deploy links

What you get

Use the JavaScript integration to connect server-side errors, logs, request timing, and deploy context.

NeedWhat appears in Logister
Catch Express request failuresIssues with structured JavaScript stack frames, request context, route or URL metadata, runtime details, and chained causes when available.
Keep console output visibleconsole activity in the project activity feed, with console.error(Error) reported as error events.
Measure server and browser workTransaction and span events for request handlers, scripts, jobs, browser page loads, resources, and custom operations that feed the performance view.
Correlate logs and errorsRequest, trace, session, or user identifiers shared across log and error events so related logs appear in the detail pane.
Resolve source and deploy historyrepository, commit_sha, branch, release, and deployment records that let Logister show source excerpts, GitHub links, and "started after deploy" context when a GitHub repository is connected to the project.
Monitor recurring jobsCheck-ins from workers and scripts that show up as monitor status.

Setup flow

Recommended installation path

  1. Create the project. Logister opens its setup path at the API key step; create the key there.
  2. Install logister-js in the JavaScript or TypeScript app.
  3. Configure the client with the project API key, base URL, environment, release, and source context.
  4. Add the Express middleware and error handler if you run Express, or start with direct shared-client calls in your framework.
  5. Attach instrumentConsole() if you want standard console activity to flow into Logister too.
  6. Use the shared client for custom metrics, logs, transactions, spans, and check-ins.
  7. Set onError to send delivery failures to your own logger, and await client.flush() before a short-lived script or handler exits. See Background delivery.
  8. From CI/CD, call recordDeployment() after a successful deploy so releases map to exact commits.

Tip

JavaScript teams usually get the fastest value by starting with uncaught errors plus request or job timing, then adding request spans, console capture, browser timing, source context, deployment records, and custom events once the noisy operational paths are under control.

Install

Install logister-js from npm.

Use the published npm package when you want a Node or TypeScript service to send errors, logs, metrics, transactions, spans, and check-ins into Logister with the package manager your team already uses. The npm listing is the canonical package page for install commands, version history, and package metadata.

shell
npm install logister-js
yarn add logister-js
pnpm add logister-js
bun add logister-js
typescript
import { LogisterClient } from "logister-js";

const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example",
  environment: process.env.LOGISTER_ENVIRONMENT,
  release: process.env.LOGISTER_RELEASE,
  repository: process.env.LOGISTER_REPOSITORY,
  commitSha: process.env.LOGISTER_COMMIT_SHA ?? process.env.GITHUB_SHA,
  branch: process.env.LOGISTER_BRANCH ?? process.env.GITHUB_REF_NAME
});

Source and deployments

Send repository, commit, branch, and deployment records when you have them.

If the project is connected to the GitHub App, Logister can use event source context to resolve stack frames to repository files. Deployment records are stronger than event-only release strings because they tell Logister which commit was deployed for a release and environment.

typescript
await client.recordDeployment({
  release: process.env.LOGISTER_RELEASE ?? "checkout@2026.06.18",
  environment: process.env.LOGISTER_ENVIRONMENT ?? "production",
  repository: process.env.LOGISTER_REPOSITORY ?? "acme/checkout",
  commitSha: process.env.LOGISTER_COMMIT_SHA ?? process.env.GITHUB_SHA ?? "",
  branch: process.env.LOGISTER_BRANCH ?? process.env.GITHUB_REF_NAME,
  deployedAt: new Date(),
  releaseTag: process.env.GITHUB_REF_NAME,
  workflowRunUrl: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
    ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
    : undefined
});
FieldSet it fromUsed for
repositoryLOGISTER_REPOSITORY or GITHUB_REPOSITORYFinds the GitHub repository connected to the project.
commitShaLOGISTER_COMMIT_SHA or GITHUB_SHAResolves stack frames and deployment context to the exact commit.
branchLOGISTER_BRANCH or GITHUB_REF_NAMEFalls back when an event has no commit or deployment record yet.
releaseYour app version, build ID, tag, or package versionConnects event filters, release health, and deployments.

Verify it

After a deploy, send one test error with the same release. Open the event detail and check for source lookup status, deployment context, and GitHub links. If source is missing, confirm the GitHub App is installed, linked to the project, and the repository is connected; then check that the event has repository plus either commit_sha or a matching deployment record.

Express

Capture requests and uncaught route errors.

typescript
import express from "express";
import { LogisterClient } from "logister-js";
import {
  createLogisterErrorHandler,
  createLogisterMiddleware
} from "logister-js/express";

const app = express();
const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example"
});

app.use(createLogisterMiddleware({ client, captureRequestSpans: true }));

app.get("/orders/:id", async (_req, res) => {
  res.json({ ok: true });
});

app.use(createLogisterErrorHandler({ client }));

Important

Register the Logister request middleware before your routes and the Logister error handler before your final Express error response middleware. Set captureRequestSpans: true when you want completed requests to appear in request load waterfall charts.

Express events are queued and sent in batches in the background. A Logister outage, a bad API key, or a slow network never fails your requests; delivery failures go to the client's onError. By default the middleware records the URL, query, and the user-agent and x-request-id headers, with originalUrl, req.query, and allowlisted headers redacted. The client IP is not recorded unless you opt in.

OptionDefaultWhat it does
captureRemoteIpfalseRecords req.ip as remote_ip. An IP address is personal data, so it is off unless you opt in. Configure Express trust proxy if you want the forwarded client address.
transactionSampleRate1The fraction of requests, from 0 to 1, recorded as transactions and request spans. Errors are always captured.
headerAllowList["user-agent", "x-request-id"]The request headers to record. Sensitive headers such as authorization and cookie are redacted even when you allow them. x-forwarded-for is not in the default list.
captureTransactions, captureRequestSpans, captureErrors, ignoreRoutes, transactionNamer, shouldCaptureErrorControl which requests and errors are recorded and how transactions are named.
typescript
const logisterOptions = {
  client,
  captureRequestSpans: true,
  transactionSampleRate: 0.25, // record a quarter of requests; errors are always captured
  captureRemoteIp: true,       // opt in to remote_ip
  headerAllowList: ["user-agent", "x-request-id"]
};

app.use(createLogisterMiddleware(logisterOptions));
// ...routes...
app.use(createLogisterErrorHandler(logisterOptions));

Each event is also bounded by the client's maxEventBytes (default 256 KiB), so an oversized request context is trimmed instead of being rejected with its batch.

Verify it

Request one route, then open its transaction in Logister. The context should show only the allowlisted headers, sensitive values as [REDACTED], and no remote_ip unless captureRemoteIp is true. In a test script, await client.flush() before checking so queued events are sent.

Browser timing

Capture page-load and resource timing from browser code.

typescript
import { LogisterClient } from "logister-js";
import { capturePageLoad } from "logister-js/browser";

const client = new LogisterClient({
  apiKey: window.LOGISTER_API_KEY,
  baseUrl: "https://your-logister-host.example",
  environment: "production",
  release: window.LOGISTER_RELEASE
});

await capturePageLoad(client, {
  route: window.location.pathname,
  includeResources: true,
  maxResources: 20
});

The browser entrypoint sends a root browser span for page load timing and optional child resource spans for images, scripts, stylesheets, and API calls exposed through the browser performance API.

capturePageLoad() resolves even when delivery fails and reports failures to the client's onError, so awaiting it never rejects in page code.

Console logging

Forward console output without rewriting every call site.

typescript
import { LogisterClient } from "logister-js";
import { instrumentConsole } from "logister-js/console";

const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example"
});

const restoreConsole = instrumentConsole(client, {
  context: { service: "worker" }
});

console.warn("Queue backlog rising", { queue: "emails" });

restoreConsole();

This is the low-friction path when a JavaScript service already leans on console.warn() and console.error() during jobs, scripts, and server-side debugging. The console integration sends console.debug/info/log/warn/error calls as Logister log events and records console.error() calls that include an Error object as error events. Console events are queued and sent in batches in the background.

Capture runs after the original console method and never throws at your call site:

  • Circular references are replaced with "[Circular]".
  • Deep, wide, or large values are truncated.
  • Throwing getters and proxies become "[Unserializable]".
  • Arguments and the rendered message are redacted.
  • Console output written while capturing, including the SDK's own failure warnings, is not captured again, so process.on("unhandledRejection", (e) => console.error(e)) cannot loop.

Background delivery

Know which calls queue events and which calls post immediately.

The Express, console, traced-fetch, and browser integrations hand events to a bounded in-memory queue on the client instead of posting each one. The client sends queued events to the gzip/NDJSON batch endpoint in the background. These integrations never reject or throw when delivery fails.

PathCallsWhen delivery fails
BackgroundExpress middleware and error handler, instrumentConsole(), createTracedFetch(), capturePageLoad(), and enqueueException, enqueueMessage, enqueueMetric, enqueueTransaction, enqueueSpan, enqueueEventNever throws or rejects. The enqueue* methods return true when the event was queued. Failures go to onError.
DirectAwaited capture*, sendEvent, and sendEventsPosts immediately and rejects when Logister returns a non-2xx response. Decide at the call site whether to retry, log locally, or continue without telemetry.
typescript
const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example",
  maxQueueSize: 1000,     // events beyond this are dropped and counted
  flushIntervalMs: 1000,  // the longest a queued event waits for a batch
  maxEventBytes: 262144,  // oversized events have their context trimmed
  onError: (error) => myLogger.warn({ error }, "Logister delivery failed")
});

client.enqueueMessage("job finished", { context: { queue: "emails" } });

// Before a short-lived script or serverless handler exits:
await client.flush();
// or, to also stop accepting new background events:
await client.close();
OptionDefaultWhat it does
maxQueueSize1000The most events the queue holds. When it is full, new events are dropped and counted in client.droppedEventCount.
flushIntervalMs1000The longest a queued event waits before its batch is sent.
batchSize50Events per batch. A batch is sent at once when batchSize events are waiting.
maxEventBytes262144 (256 KiB)The size cap for one event. Its context is compacted first. If it is still too large, only the correlation IDs are kept, with logister_truncated: true. Messages are capped at 16,384 characters.
onErrorNoneReceives delivery and integration failures. Errors thrown by onError itself are ignored. Without it, the client writes one console.warn and stays silent afterwards.
  • Queued events are sent every flushIntervalMs, immediately once batchSize events are waiting, on flush() or close(), and on Node's beforeExit.
  • Background flush, retry, and per-attempt timeout timers are unref()'d, so they never keep a Node process alive.
  • client.queuedEventCount reports the backlog and client.droppedEventCount reports events dropped because the queue was full.
  • LogisterRequestError (with status) and LogisterTimeoutError are exported for inspecting failures in onError.
  • Telemetry requests use redirect: "error" and do not follow redirects, so set baseUrl to the final address of your Logister instance.
  • The client warns once when baseUrl uses plain http:// on a host other than localhost, 127.0.0.1, or ::1, because the API key and telemetry would travel unencrypted.

Flush short-lived processes

Scripts, CLI tools, cron jobs, and serverless handlers can exit before the next batch is sent. Call await client.flush(), or await client.close(), before exit if the process cannot rely on beforeExit. A process that calls process.exit() should always flush first.

Verify it

Queue one event with client.enqueueMessage(), await client.flush(), and confirm the message appears in the project. client.droppedEventCount should be 0. To check failure reporting in development, point the client at a wrong API key: onError should receive a LogisterRequestError whose status is 401.

Redaction

Sensitive values become [REDACTED] before events leave the process.

The client redacts every event's message and context, and check-in context, before delivery. That covers captured URLs, query strings, referers, headers, request and user context, tags, breadcrumbs, log fields, span attributes, and exception data. These rules are shared by all Logister SDKs.

RuleWhat happens
Sensitive key namesA key is lowercased and every non-alphanumeric character is removed. It is sensitive if it exactly matches one of password, passwd, pwd, secret, token, apikey, authorization, proxyauthorization, cookie, setcookie, sessiontoken, sessionkey, credential, credentials, privatekey, clientsecret, accesstoken, refreshtoken, idtoken, authtoken, xapikey, xauthtoken, xaccesstoken, xcsrftoken, xxsrftoken, signature, ssn, creditcard, cardnumber, or cvv, or if it contains token, secret, password, passwd, apikey, authorization, credential, or cookie. So X-Api-Key, api_key, githubToken, and DB_PASSWORD are all redacted. The value becomes "[REDACTED]".
Credential-like valuesStrings are scrubbed of Bearer <credential> and Basic <credential> (the scheme word is kept), JWT-shaped values, and lgst_ keys.
URLs and query stringsThe scheme, host, and path are kept. A sensitive query parameter keeps its name and its value becomes [REDACTED], for example /callback?code=1&token=[REDACTED]. Userinfo is redacted and the fragment is dropped.
Exempt fieldsThe SDK's own envelope fields are never changed: uuid, event_type, and the top-level trace_id, span_id, parent_span_id, request_id, and session_id in context.

Add your own keys with redactKeys. They are normalized the same way and must match exactly. Use client.redact(value) to apply the client's redaction and serialization bounds to any value, for example to check what a context object will look like before you send it.

typescript
const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example",
  redactKeys: ["account_number", "otp"]
});

client.redact({ otp: "123456", note: "Bearer abc123def456" });
// => { otp: "[REDACTED]", note: "Bearer [REDACTED]" }

Context is also bounded while it is serialized:

  • Cycles become "[Circular]".
  • Depth is capped at 12 and arrays at 200 items.
  • Each object keeps at most 200 keys, and the whole event at most 400.
  • Strings are capped at 16,384 characters.

Verify it

Run client.redact() on a representative context object from your app and check that every field you consider sensitive comes back as [REDACTED]; add any that do not to redactKeys. Redaction is a safety net, not a data policy. Avoid sending personal data you do not need.

Upgrading to 0.6.0

Review these defaults when you upgrade to 0.6.0.

  • Background batching. Express, console, and traced-fetch events are now batched in the background instead of posted one request at a time. Call await client.flush() or close() before a short-lived process exits if it cannot rely on beforeExit. See Background delivery.
  • Failure reporting. Background delivery failures go to the new onError option. Without it, the client writes a single console.warn. capturePageLoad() now resolves even when delivery fails.
  • Client IP is opt-in. Express no longer records req.ip by default. Set captureRemoteIp: true if you rely on remote_ip.
  • Header allowlist. The default headerAllowList is ["user-agent", "x-request-id"]; x-forwarded-for is no longer included.
  • Default redaction. Every event's message and context, and check-in context, are redacted with the shared rule. Express originalUrl, req.query, and allowlisted headers are redacted too. Add application-specific fields with redactKeys. See Redaction.
  • Event size. maxEventBytes (default 256 KiB) trims an oversized event's context, and messages are capped at 16,384 characters.
  • Sampling. Express transactionSampleRate defaults to 1, so every request is still recorded until you lower it.
  • Redirects. Telemetry requests use redirect: "error". Make sure baseUrl is the final address of your instance.
  • Unchanged. Directly awaited capture*, sendEvent, and sendEvents calls still post immediately and reject on failure.

Other frameworks

Use the shared client even if your app is not on Express.

logister-js is first-class for Node, Express, and TypeScript apps today, but other JavaScript developers are not blocked. If your framework can run JavaScript and make HTTP requests, you can still send events into Logister.

ScenarioRecommended path
Custom Node server, Fastify, scripts, workers, cron jobsUse the shared LogisterClient directly and call captureException, captureMessage, captureMetric, captureTransaction, captureSpan, or checkIn.
Framework without a first-class Logister middleware yetWrap the framework's request lifecycle or error hooks with the shared client, then add custom events where you need more context.
Runtime where you do not want the SDK yetSend JSON directly to the HTTP API using the project API key.
typescript
import { LogisterClient } from "logister-js";

const client = new LogisterClient({
  apiKey: process.env.LOGISTER_API_KEY ?? "",
  baseUrl: process.env.LOGISTER_BASE_URL ?? "https://your-logister-host.example"
});

try {
  await runJob();
} catch (error) {
  await client.captureException(error, {
    context: { job: "nightly-import" }
  });
}

await client.captureTransaction("nightly-import", 1824, {
  context: { environment: process.env.NODE_ENV ?? "development" }
});

Need another framework?

If you are on Next.js, NestJS, Fastify, or another JavaScript framework, start with the shared client or the HTTP API. That gives you working ingestion today while the package grows more framework-specific integrations over time.

Custom events

Report metrics, logs, spans, and check-ins from the same client.

typescript
await client.captureMetric("cache.hit_rate", 0.98, {
  unit: "ratio",
  traceId: "trace-123",
  requestId: "req-123",
  context: { service: "checkout-api" }
});

await client.captureSpan("render checkout", 82.1, {
  kind: "render",
  status: "ok",
  traceId: "trace-123",
  parentSpanId: "span-root",
  requestId: "req-123",
  context: { route: "POST /checkout" }
});

await client.captureMessage("Queue backlog rising", {
  level: "warn",
  context: { queue: "emails" }
});

await client.checkIn("nightly-import", "ok", {
  release: "worker@2026.05.21",
  durationMs: 842.7,
  expectedIntervalSeconds: 3600,
  traceId: "trace-123",
  requestId: "req-123",
  context: { environment: "production" }
});

Captured JavaScript exceptions include structured stack frames and chained causes when the original error uses cause. Capture options support per-event environment, release, trace ID, request ID, session ID, and user ID fields; span options add span ID, parent span ID, kind, status, and start/end timestamps; metric events include structured metric context; check-ins send release and monitor metadata to the check-in endpoint.

These awaited capture* calls post immediately and reject when Logister returns a non-2xx response. Use the enqueue* methods from Background delivery when you want the same events queued and batched without handling failures at each call site.

Verification
Events page checklist
transactions visible
request spans visible
uncaught route errors visible
console logging events visible
browser page-load spans visible if enabled
custom metrics, spans, and check-ins present

Follow requests between projects

Logister 3.7 adds explicit project connections and request-identifier evidence. See request correlation setup for the required SDK versions, origin allowlists, permissions, and verification.