What is gunsole?
Gunsole is a desktop log viewer. You install it, it runs a local HTTP server on port 17655, and your apps send structured logs to it via a lightweight SDK.
That’s the whole pitch.
console.log doesn’t scale. It’s fine when you have 5 logs. It’s not fine when you have 5,000, across 3 services, and you need to find the one request that broke.
Cloud logging services are powerful but they cost money, require accounts, send your data to someone else’s servers, and come with a pile of configuration you don’t need during development.
Gunsole sits in between. It’s a real log viewer — with filtering, bucketing, tagging, search — but it runs entirely on your machine. No account. No monthly bill. No data leaving localhost.
The parts
Section titled “The parts”Desktop app — a native application. Receives logs, stores them in SQLite, displays them in a virtualized table with real-time streaming. Runs on macOS, Windows, Linux.
local.gunsole.com (coming soon) — the same log viewer, but in your browser. No installation required. Any environment can send logs to it and they’re stored locally in your browser’s database.
Gunsole Cloud (coming soon) — hosted SaaS for production metrics, error tracking, and team access at app.gunsole.com.
SDK — JavaScript/TypeScript, shipped as two packages:
@gunsole/web— for browser apps (React, Next.js client, Angular, Solid, Vue)@gunsole/core— for Node.js, server-side, and CLI
Both collect logs, batch them, gzip-compress, and send them to your chosen destination. Handle retries and never crash your app.
HTTP API — the desktop app exposes a REST API on localhost:17655 for querying logs programmatically. It also runs an MCP server so AI agents can read your logs.
Type-safe by default
Section titled “Type-safe by default”The SDK is built for TypeScript. Two killer features:
Type-safe buckets — define your buckets once, get callable accessors with full autocomplete. gunsole.payments("Checkout done") instead of gunsole.info({ bucket: "payments", ... }). Typos are caught at compile time. Reserved names are enforced at both the type level and runtime.
Type-safe tags — pass a tag schema as a generic and every tag key is validated at compile time. Invalid keys fail in your editor, not in production.
type Tags = { action: string; status: string };
const gunsole = createGunsoleClient<Tags>({ projectId: "my-app", mode: "local", buckets: ["auth", "payments"] as const,});
gunsole.auth("User logged in", { tags: { action: "login" } }); // ✅gunsole.payments.error("Charge failed", { tags: { typo: "x" } }); // ❌ compile errorSee Typed Buckets and Tags for the full deep dive.
The 5 log levels
Section titled “The 5 log levels”Each level answers a different question about your system:
| Level | Question | Example | When to use |
|---|---|---|---|
debug | What’s happening internally? | State snapshots, flow tracing, variable dumps | Development-time visibility. Trace execution paths, inspect intermediate state. |
info | What just happened? | User logged in, payment processed, email sent | Normal operations you want a record of. The “everything is fine” level. |
warn | Is something off? | Empty response, deprecated usage, slow query (>2s) | Something unexpected but not broken. The app continues, but you should investigate. |
error | Is something broken? | Failed write, invalid state, backend 500, network timeout | Something failed and a feature isn’t working. Needs attention. |
fatal | Is something dead? | Uncaught exception, unhandled rejection, error boundary catch | The app (or a part of it) has crashed. Immediate action required. |
The error vs fatal distinction
Section titled “The error vs fatal distinction”This is the most important mental model:
error= something is broken, but the app keeps running. A payment failed, an API returned garbage, a write didn’t persist. The user might see an error message, but they can still use the app.fatal= something is dead. An uncaught exception, an unhandled promise rejection, a React error boundary catch. This part of the app has stopped working entirely.
Think of it this way:
error→ “This feature is broken”fatal→ “This page/process is dead”
Automatic fatal logging
Section titled “Automatic fatal logging”The SDK’s global error handlers automatically log fatal for:
window.onerror(browser uncaught exceptions)window.onunhandledrejection(browser unhandled promise rejections)process.uncaughtException(Node.js)process.unhandledRejection(Node.js)
You can also call gunsole.fatal() explicitly — for example, in a React error boundary:
class ErrorBoundary extends React.Component { componentDidCatch(error, info) { gunsole.fatal({ bucket: "error_boundary", message: error.message, context: { stack: error.stack, componentStack: info.componentStack }, }); }}What it is not
Section titled “What it is not”- Not a production monitoring tool
- Not an APM (no metrics, no traces visualization yet)
- Not a cloud service (nothing leaves your machine)
- Not trying to replace your production logging pipeline
It’s a development tool. It makes the “what the hell is happening in my app” question answerable without grepping through terminal output.