Introductionv0.1.4
Koralog is an observability SDK for applications using LLMs (Anthropic, OpenAI, Gemini). It monitors latency, errors, estimated cost, and usage per feature/user — sending data to the Koralog backend without interfering with your application's flow.
The SDK is designed to be non-intrusive: if not initialized, track() passes through without instrumenting. Network failures are always silent and never propagate to your application.
How it works
Installation
Install Koralog from npm:
npm install koralogAI-powered Setup
Instead of applying track() manually, paste the Koralog setup prompt into any AI coding assistant (Claude, Cursor, Copilot) and it will handle everything autonomously:
How to use
Open a new conversation with your AI assistant, paste the full prompt, and let it run. No extra context needed — the prompt is self-contained.
View setup promptfetch() calls — different wrapping pattern
The prompt handles this automatically. If you're applying manually: when wrapping a raw fetch call, the arrow function must resolve to parsed JSON. Chain .then(res => res.json()) inside the arrow function, and remove any .json() call that previously existed after the await — it will throw since the response is already consumed.
// Before
const response = await fetch('https://api.openai.com/v1/chat/completions', options)
const data = await response.json()
const content = data.choices[0].message.content// After
import { track } from '@/lib/koralog'
const data = await track(
() => fetch('https://api.openai.com/v1/chat/completions', options).then(res => res.json()),
{
feature: 'chat-sendMessage',
provider: 'openai',
model: 'gpt-4o', // required for fetch — not auto-detected
}
)
const content = data.choices[0].message.contentQuick Start
Full working example — init, identify a user, track an LLM call, and flush before shutdown.
import { init, track, setUser, shutdown } from 'koralog'
init({
apiKey: 'kora_live_abc123',
serverless: false,
debug: false,
})
setUser({ id: 'usr_456', email: 'maria@company.com', name: 'Maria' })
const response = await track(
() => anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Explain JWT in 3 lines.' }],
}),
{
feature: 'explain-tech',
provider: 'anthropic',
model: 'claude-sonnet-4-6',
tags: { env: 'prod', version: '2' },
requestParams: { temperature: 0.5, max_tokens: 1024 },
}
)
// On server shutdown:
await shutdown()init()
Initializes the SDK. Must be called once at application boot, before any track() calls. Calling it multiple times has no effect after the first call.
import { init } from 'koralog'
init({
apiKey: 'kora_live_abc123',
debug: false,
serverless: true,
})track()
Wraps an async function (typically an LLM call) and records latency, status, and error type. Returns the same value the original function would return. If the SDK is not initialized, the function runs without any overhead.
import { track } from 'koralog'
const response = await track(
() => anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello' }],
}),
{
feature: 'chat',
provider: 'anthropic',
model: 'claude-sonnet-4-6',
tags: { env: 'prod' },
}
)setUser()
Defines the active user for the current session or request. User data (id, email, name) is merged into all subsequent events until clearUser() is called.
clearUser()
Removes the active user. Events after this call will not include user data unless setUser() is called again.
import { setUser, clearUser } from 'koralog'
setUser({ id: 'usr_123', email: 'user@example.com', name: 'Alice' })
// later, to remove the active user:
clearUser()flush()
Forces immediate dispatch of all queued events. Returns a Promise that resolves when the flush completes.
shutdown()
Alias for flush(). Semantically clearer at process termination — call it in your SIGTERM or process.on('exit') handler to ensure all events are sent before the process exits.
import { flush, shutdown } from 'koralog'
// Force immediate send:
await flush()
// Before process exit:
await shutdown()KoralogConfig
Options object passed to init(). Only apiKey is required.
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey | string | required | Your Koralog API key. Prefix determines environment: kora_test_* sends to localhost, kora_live_* sends to production. |
baseUrl | string | — | Custom ingestion endpoint URL. Overrides the endpoint auto-detected from the key prefix. |
debug | boolean | false | Logs all events to the console. Useful during development and local testing. |
serverless | boolean | true | Flush immediately after each event. Default true — correct for Vercel, AWS Lambda, and any ephemeral runtime. |
batchSize | number | 10 | Maximum events to accumulate before an automatic flush is triggered. Only active when serverless: false. |
flushInterval | number | 5000 | Timer interval in milliseconds for automatic flushing. Only active when serverless: false. |
capturePrompts | boolean | false | When true, includes the full LLM response content in the payload (content, choices, candidates fields). |
truncateAt | number | 500 | Maximum character count for text fields in the payload. Longer values are truncated. |
showSensitiveFields | boolean | false | When true, includes userIp and custom sensitive fields in the payload. |
TrackMetadata
Optional second argument to track(). All fields are optional and can be combined freely.
| Parameter | Type | Description |
|---|---|---|
feature | string | Feature name for grouping and filtering (e.g. "chat", "summary", "search"). |
userId | string | Overrides the userId set via setUser() for this specific event only. |
userEmail | string | Overrides the userEmail set via setUser() for this specific event only. |
model | string | LLM model identifier (e.g. "claude-sonnet-4-6", "gpt-4o", "gemini-2.0-flash"). |
provider | string | LLM provider: "anthropic" | "openai" | "gemini" | "other". |
tags | object | Free key-value pairs for filtering in the dashboard (e.g. { env: "prod", version: "2" }). |
requestParams | object | Parameters passed to the LLM request (temperature, max_tokens, top_p, etc.). |
userIp | string | User IP address. Only included in the payload when showSensitiveFields: true. |
sensitive | object | Arbitrary sensitive fields. Only included in the payload when showSensitiveFields: true. |
Payload Reference
Each event sent to the Koralog backend contains these fields:
| Parameter | Type | Description |
|---|---|---|
timestamp | string | ISO 8601 timestamp of when the LLM call was initiated. |
api_key | string | API key used for authentication. |
sdk_version | string | Version string of the SDK that generated the event. |
latency_ms | number | Total execution time of the wrapped function in milliseconds. |
status | string | "success" if the function resolved, "error" if it threw. |
error_type | string? | Classified error type. Present only when status is "error". |
error_message | string? | The error message string. Present only when status is "error". |
raw_response | object | Raw LLM response object. Sensitive content fields are stripped by default. Set capturePrompts: true to include them. |
metadata | object | All TrackMetadata fields merged with the active user data from setUser(). |
Error Classification
The SDK automatically classifies exceptions intercepted inside track(). After recording, the original exception is always rethrown.
| Error type | Triggered when |
|---|---|
timeout | AbortError thrown, or message contains "timeout". |
rate_limit | HTTP 429, or message contains "rate limit". |
context_exceeded | Message contains "context" + "limit", or "token" + "limit". |
invalid_request | HTTP 400 or 422. |
unknown | Any other exception type. |
Batching Strategy
Serverless mode (default)
Each event triggers an immediate flush(). Correct for Vercel, AWS Lambda, and any runtime where the process may be killed between requests.
Long-running mode
Set serverless: false to accumulate events in memory and flush in bulk. A flush is triggered when:
- –The queue reaches batchSize events (default: 10)
- –The flushInterval timer fires (default: 5 000 ms)
- –flush() or shutdown() is called explicitly
- –The Node.js process emits beforeExit
Privacy & Sensitive Fields
Koralog is privacy-first by default. The SDK strips response content fields before sending payloads:
| Provider | Stripped fields |
|---|---|
| Anthropic | content |
| OpenAI | choices output |
| Gemini | candidates |
To capture full response content, set capturePrompts: true in init().
To include userIp and custom sensitive fields, set showSensitiveFields: true.
API Key Modes
The API key prefix determines where events are sent. A custom baseUrl overrides both prefix-based endpoints.
| Key prefix | Endpoint | Description |
|---|---|---|
kora_test_* | localhost:8080 | Sends events to http://localhost:8080. Ideal for local development — run the Koralog dev server to inspect payloads. |
kora_live_* | api.koralog.com | Sends events to https://api.koralog.com. Use this key in production. |
Get your API key
Create a project in the Koralog dashboard to generate your API key.