JavaScript / TypeScript SDK
Packages
Section titled “Packages”The SDK ships as two packages:
| Package | Use when | Install |
|---|---|---|
@gunsole/web | Code runs in the browser | pnpm add @gunsole/web |
@gunsole/core | Code runs on the server | pnpm add @gunsole/core |
@gunsole/web — for any browser code: React, Angular, Solid, Vue, Next.js client components.
@gunsole/core — for any server code: Node.js backends, Next.js server components, API routes, CLI tools.
SSR frameworks (Next.js, Nuxt, etc.) — use both. @gunsole/core for server-side, @gunsole/web for client-side. See the Next.js guide for a full example.
Zero runtime dependencies.
Initialize
Section titled “Initialize”import { createGunsoleClient } from "@gunsole/web";
const gunsole = createGunsoleClient({ projectId: "my-app", mode: "local",});Config options
Section titled “Config options”| Option | Type | Default | Description |
|---|---|---|---|
projectId | string | required | Identifies your project in the desktop app |
mode | "local" | "desktop" | "cloud" | required | Determines the endpoint URL |
apiKey | string | — | API key (required for cloud mode) |
endpoint | string | — | Override the endpoint URL entirely |
env | string | — | Environment label (e.g. "production", "staging") |
appName | string | — | Application name, attached to every log |
appVersion | string | — | Application version, attached to every log |
defaultTags | Record<string, string> | — | Tags merged into every log entry |
batchSize | number | 10 | Number of logs to collect before auto-flushing |
flushInterval | number | 5000 | Auto-flush interval in milliseconds |
maxQueueSize | number | 1000 | Drop oldest entries when exceeded |
maxLogRate | number | 10 | Logs per second (token bucket, 0 = unlimited) |
maxBurst | number | 100 | Burst allowance for rate limiting |
buckets | string[] | — | Typed bucket accessors with autocomplete |
beforeSend | (entry) => entry | null | — | Transform or drop logs before batching |
hooks | object | — | Lifecycle hooks: onLog, onFlush, onError |
isDisabled | boolean | false | Kill switch — disables all logging |
isDebug | boolean | false | Disables gzip compression for debugging |
sessionId | string | auto-generated | Override auto-generated session ID |
fetch | FetchFunction | globalThis.fetch | Custom fetch implementation |
Modes and endpoints
Section titled “Modes and endpoints”| Mode | Endpoint | Use Case |
|---|---|---|
desktop | http://localhost:17655 | Gunsole desktop app running on your machine |
local | https://local.gunsole.com | Browser-based log viewer — logs stored in your browser’s local database |
cloud | https://api.gunsole.com | Gunsole Cloud (coming soon, requires apiKey) |
desktop— sends logs directly to the Gunsole desktop app on localhost.local— sends logs to local.gunsole.com (coming soon), a browser-based log viewer. Works like the desktop app but runs entirely in your browser — any environment can send logs to it and they’re stored locally in the browser’s database. No installation required.cloud— sends logs to Gunsole Cloud (coming soon) for production metrics, error tracking, and team access.
The endpoint option overrides the mode-based URL if you need a custom address.
Logging
Section titled “Logging”Five methods, one for each level:
gunsole.info({ bucket: "api", message: "Request handled" });gunsole.debug({ bucket: "api", message: "Cache miss", context: { key: "user:123" } });gunsole.warn({ bucket: "api", message: "Slow query", context: { duration: 2500 } });gunsole.error({ bucket: "api", message: "Connection refused", tags: { db: "postgres" } });gunsole.fatal({ bucket: "app", message: "Unhandled crash", context: { stack: err.stack } });gunsole.log() accepts an optional level as the first argument:
gunsole.log({ bucket: "api", message: "Request handled" }); // defaults to "info"gunsole.log("error", { bucket: "api", message: "Something broke" }); // explicit levelLogOptions
Section titled “LogOptions”| Field | Type | Required | Description |
|---|---|---|---|
message | string | yes | The log message |
bucket | string | yes | Category/namespace for this log |
context | Record<string, unknown> | no | Arbitrary structured data |
tags | Record<string, string> | no | Key-value pairs for filtering (string values only) |
traceId | string | no | Links related logs across operations |
What gets sent
Section titled “What gets sent”Each log entry is internally enriched before sending:
{ // from your call bucket: "api", message: "Request handled", level: "info", context: { ... }, tags: { ...defaultTags, ...yourTags }, // merged
// auto-populated timestamp: 1708214400000, // Date.now() userId: "u_123", // from setUser() sessionId: "sess_abc", // from setSessionId() env: "production", // from config appName: "my-app", // from config appVersion: "1.0.0", // from config}User and session tracking
Section titled “User and session tracking”gunsole.setUser({ id: "u_123", name: "Ada Lovelace", traits: { plan: "pro", team: "backend", },});
gunsole.setSessionId("sess_abc123");Once set, userId and sessionId are attached to every subsequent log. Useful for filtering logs by user in the desktop app.
Session persistence (@gunsole/web)
Section titled “Session persistence (@gunsole/web)”In browser apps, use persistSession() to keep the same session ID across page reloads:
import { createGunsoleClient, persistSession } from "@gunsole/web";
const gunsole = createGunsoleClient({ projectId: "my-app", mode: "local" });persistSession(gunsole);Tags are flat key-value pairs (string → string) that become dynamic filters in the desktop app.
gunsole.info({ bucket: "api", message: "POST /users", tags: { route: "/users", method: "POST", status: "201", },});Default tags from config are merged with per-log tags. Per-log tags win on conflict.
const gunsole = createGunsoleClient({ projectId: "my-app", mode: "local", defaultTags: { service: "api-server", region: "us-east-1" },});
// Sent tags: { service: "api-server", region: "us-east-1", route: "/users" }gunsole.info({ bucket: "api", message: "Request", tags: { route: "/users" } });For compile-time tag safety with TypeScript, see Typed Tags.
Batching and flushing
Section titled “Batching and flushing”Logs are buffered and sent in batches. A batch is flushed when:
- The batch reaches
batchSize(default: 10) - The
flushIntervaltimer fires (default: 5000ms) - You call
gunsole.flush()manually
await gunsole.flush();The SDK compresses payloads with gzip by default. Set isDebug: true to send plain JSON (useful for inspecting network requests).
Rate limiting
Section titled “Rate limiting”The SDK uses a token bucket algorithm to prevent flooding. Default: 10 logs/sec with a burst allowance of 100. Configure via maxLogRate and maxBurst, or set maxLogRate: 0 to disable.
Queue management
Section titled “Queue management”If logs accumulate faster than they can be sent, maxQueueSize (default: 1000) limits the queue. When exceeded, the oldest entries are dropped.
beforeSend hook
Section titled “beforeSend hook”Transform or drop logs before they’re added to the batch:
const gunsole = createGunsoleClient({ projectId: "my-app", mode: "local", beforeSend: (entry) => { if (entry.message.includes("sensitive")) return null; // drop log return entry; },});Lifecycle hooks
Section titled “Lifecycle hooks”const gunsole = createGunsoleClient({ projectId: "my-app", mode: "local", hooks: { onLog: (entry) => console.log("Logged:", entry.message), onFlush: (logs, success) => console.log(`Flushed ${logs.length}, ok=${success}`), onError: (error) => console.error("Flush failed:", error), },});Error handling
Section titled “Error handling”The SDK is built to never crash your app. All internal errors are silently caught (with optional onError hook). Failed HTTP requests are retried up to 3 times with exponential backoff (1s, 2s, 4s) with jitter (0.5-1.5x). HTTP 413 splits the batch and retries. 4xx (except 429) are not retried. Logs are dropped after 10 consecutive flush failures.
Global error handlers
Section titled “Global error handlers”Automatically catch unhandled errors and promise rejections, logging them as fatal:
// @gunsole/core — manual attachmentgunsole.attachGlobalErrorHandlers();
// @gunsole/web — automatically attached on createGunsoleClient()In the browser, this listens to window.onerror and window.onunhandledrejection.
In Node.js, this listens to process.uncaughtException and process.unhandledRejection.
Captured errors are logged as fatal level to these buckets: global_error, unhandled_rejection, uncaught_exception.
// Detach when done (e.g., in tests)gunsole.detachGlobalErrorHandlers();Browser lifecycle (@gunsole/web)
Section titled “Browser lifecycle (@gunsole/web)”@gunsole/web automatically handles browser edge cases so you don’t lose logs:
- Tab closing — logs are flushed before the page unloads
- Offline/online — queued logs are flushed when the browser comes back online
- Tab switch/minimize — logs are flushed when the page goes to background
- Debug mode — add
?gunsole_debug=1to any URL to enable verbose logging
Cleanup
Section titled “Cleanup”When your app is shutting down (or a component unmounts):
gunsole.destroy();This flushes remaining logs, clears timers, and detaches global error handlers.
// ReactuseEffect(() => { const gunsole = createGunsoleClient({ ... }); return () => gunsole.destroy();}, []);Next steps
Section titled “Next steps”- Typed Buckets — first-class bucket accessors with autocomplete
- Tags — dynamic filters and compile-time tag safety
- Configuration Reference — every option in one place