Privacy-first session recording for web applications
The LogSpace JavaScript SDK is a developer-friendly, privacy-first session recording library that you can embed directly in your web application. Unlike the Chrome Extension, the SDK runs inside your app and provides programmatic control over recording.
Built-in PII masking and configurable privacy controls
Capture context before errors with rolling buffer
Works with React, Vue, Angular, or vanilla JS
# Using npmnpm install @logspace/sdk# Using yarnyarn add @logspace/sdk# Using bunbun add @logspace/sdk
import LogSpace from '@logspace/sdk';LogSpace.init({serverUrl: 'https://your-logspace-server.com',apiKey: 'your-api-key',});
Only record sessions where errors occur, capturing context before the error:
LogSpace.init({serverUrl: 'https://your-logspace-server.com',apiKey: 'your-api-key',sampling: {enabled: true,bufferBefore: 10, // Keep 10 seconds before triggerrecordAfter: 10, // Continue 10 seconds aftertriggers: {onError: true, // Uncaught errorsonConsoleError: true, // console.erroronNetworkStatus: [500, 502, 503], // Server errors},},});
LogSpace.init({serverUrl: 'https://your-logspace-server.com',apiKey: 'your-api-key',privacy: {maskSensitiveData: true, // Auto-mask PIImaskSelectors: ['.credit-card', '[data-sensitive]'],excludeUrls: [/api\/auth/, /stripe\.com/],blockNetworkBodies: ['/api/payments'],redactHeaders: ['authorization', 'x-api-key'],},});
Control recording fidelity vs file size with a simple preset:
LogSpace.init({serverUrl: 'https://your-logspace-server.com',apiKey: 'your-api-key',recordingQuality: 'low', // 'low' | 'medium' | 'high' | 'extrahigh'});
lowSmallest files, basic replay. Good for very long sessions.
mediumSmaller files, less frequent snapshots. Good for long sessions.
highBalanced quality and size. Recommended default.
extrahighFull fidelity, larger files. Best for detailed debugging.
All configuration options are optional. Here are the defaults:
1LogSpace.init({2 // Server Configuration3 serverUrl: undefined, // No server - sessions stored locally only4 apiKey: undefined, // No authentication5 headers: {}, // Additional headers for API requests67 // Recording Quality Preset - simple way to control size vs fidelity8 recordingQuality: 'medium', // 'low' | 'medium' | 'high' | 'extrahigh'910 // Capture Configuration - what to record11 capture: {12 rrweb: true, // ✓ DOM recording for visual replay13 console: true, // ✓ Console logs14 network: true, // ✓ Network requests (XHR/Fetch)15 errors: true, // ✓ JavaScript errors16 interactions: true, // ✓ User clicks/inputs17 performance: true, // ✓ Performance metrics18 websocket: true, // ✓ WebSocket connections19 sse: true, // ✓ Server-Sent Events20 storage: true, // ✓ localStorage/sessionStorage changes21 },2223 // RRWeb (DOM Recording) Configuration24 rrweb: {25 maskAllInputs: false, // Mask input values for privacy26 maskTextSelector: undefined, // CSS selector for masking text27 blockSelector: undefined, // CSS selector for blocking elements28 ignoreSelector: undefined, // CSS selector for ignoring elements29 recordCanvas: false, // Don't record canvas (expensive)30 checkoutEveryNth: 500, // Full snapshot every 500 events (medium quality)31 },3233 // Privacy Configuration34 privacy: {35 maskSensitiveData: true, // Auto-mask emails, phones, SSN, cards36 maskSelectors: [], // Additional CSS selectors to mask37 excludeUrls: [], // URL patterns to exclude (regex)38 blockNetworkBodies: [], // URLs to block request/response bodies39 redactHeaders: [], // Header names to redact40 logLevels: ['log', 'info', 'warn', 'error', 'debug'], // All levels41 },4243 // Session Limits - protection against memory issues44 limits: {45 maxLogs: 10000, // Stop after 10,000 logs46 maxSize: 50 * 1024 * 1024, // Stop at 50MB uncompressed (gzipped before upload)47 maxDuration: 1800, // Stop after 30 minutes48 idleTimeout: 120, // Auto-end after 2 minutes of no activity49 rateLimit: 100, // Max 100 logs per second50 deduplicate: true, // Collapse duplicate consecutive logs51 maxNetworkBodySize: 10 * 1024, // 10KB max for request/response bodies52 },5354 // Auto-End Configuration55 autoEnd: {56 continueOnRefresh: true, // Continue same session across page refreshes57 onIdle: true, // Auto-end on idle timeout58 onLimitReached: true, // Auto-end when limits hit59 },6061 // Lifecycle Hooks - all optional62 hooks: {63 onSessionStart: undefined, // (session) => void64 onSessionEnd: undefined, // (session, logs) => void65 onLimitReached: undefined, // (reason) => void66 onAction: undefined, // (log, session) => void67 beforeSend: undefined, // (logs) => logs | null68 onError: undefined, // (error) => void69 },7071 // Development Options72 debug: false, // Disable debug logging73 dryRun: false, // Actually send data (not dry run)74});
You can initialize with an empty config:
LogSpace.init({}); // Works! Sessions stored locally
Recommended minimum for production:
LogSpace.init({serverUrl: 'https://your-server.com', // To send sessions to serverapiKey: 'sdk_live_xxxxx', // From Admin Dashboard → Settings → API Keys});
serverUrlstring | undefinedURL of your LogSpace API server. If not provided, sessions are stored locally only.
apiKeystring | undefinedSDK API key (starts with sdk_live_) for authentication. Generate your API key in the Admin Dashboard → Settings → API Keys. The key is shown only once at creation time, so save it securely.
headersRecord<string, string>Additional headers to include in API requests (e.g., custom auth).
Simple way to control the tradeoff between recording fidelity and file size. The preset adjusts rrweb settings like checkoutEveryNth.
recordingQualitydefault: 'medium'Choose a quality preset: 'low', 'medium', 'high', or 'extrahigh'
lowcheckoutEveryNth: 1000
Smallest files, basic replay
mediumcheckoutEveryNth: 500
Smaller files, good for long sessions
highcheckoutEveryNth: 300
Balanced (recommended)
extrahighcheckoutEveryNth: 150
Full fidelity, larger files
Note: Your explicit rrweb config always overrides preset values.
Configure what data to record (all enabled by default):
capture.rrwebdefault: trueDOM recording for visual replay using rrweb library
capture.consoledefault: trueConsole logs (log, info, warn, error, debug)
capture.networkdefault: trueXHR and Fetch requests with timing and payloads
capture.errorsdefault: trueJavaScript errors with stack traces
capture.interactionsdefault: trueUser clicks, inputs, scrolls, and navigation
capture.performancedefault: truePerformance metrics (LCP, FID, CLS, TTFB)
capture.websocketdefault: trueWebSocket connections and messages
capture.ssedefault: trueServer-Sent Events streams
capture.storagedefault: truelocalStorage, sessionStorage, cookie, and IndexedDB operations
Fine-tune DOM recording behavior:
rrweb.maskAllInputsdefault: falseMask all input field values with asterisks for privacy.
rrweb.maskTextSelectordefault: undefinedCSS selector for elements whose text content should be masked.
Example: ".user-name, .email-address"
rrweb.blockSelectordefault: undefinedCSS selector for elements to completely hide (replaced with placeholder).
Example: ".credit-card-form, [data-private]"
rrweb.ignoreSelectordefault: undefinedCSS selector for elements to ignore completely (not recorded at all).
rrweb.recordCanvasdefault: falseRecord canvas elements. ⚠️ Can be expensive for performance.
rrweb.checkoutEveryNthdefault: 150Take a full DOM snapshot every N events. Lower = larger recordings but faster seeking.
Built-in privacy controls for sensitive data:
privacy.maskSensitiveDatadefault: trueAuto-detect and mask emails, phone numbers, SSNs, and credit card numbers.
privacy.maskSelectorsdefault: []Additional CSS selectors for elements to mask.
Example: [".credit-card", "[data-sensitive]"]
privacy.excludeUrlsdefault: []URL patterns (regex) to exclude from network logging.
Example: [/api\/auth/, /stripe\.com/]
privacy.blockNetworkBodiesdefault: []URLs where request/response bodies should not be captured.
Example: ["/api/payments", "/api/auth/login"]
privacy.redactHeadersdefault: []HTTP header names to redact from network logs.
Example: ["authorization", "x-api-key", "cookie"]
privacy.logLevelsdefault: all levelsWhich console log levels to capture.
Options: ["log", "info", "warn", "error", "debug"]
Smart sampling keeps a rolling buffer and only saves sessions when triggers fire:
sampling: {enabled: true, // Enable sampling modebufferBefore: 10, // Keep 10 seconds of logs before triggerrecordAfter: 10, // Continue recording 10 seconds aftertriggers: {onError: true, // Trigger on uncaught JavaScript errorsonConsoleError: true, // Trigger on console.error() callsonNetworkStatus: [500, 502, 503, 504], // Trigger on HTTP errors},}
sampling.enableddefault: falseEnable sampling mode. When enabled, sessions only save on triggers.
sampling.bufferBeforedefault: 30Seconds of logs to keep before the trigger event.
sampling.recordAfterdefault: 30Seconds to continue recording after the trigger event.
sampling.triggers.onErrordefault: trueTrigger on uncaught JavaScript errors (window.onerror).
sampling.triggers.onConsoleErrordefault: trueTrigger when console.error() is called.
sampling.triggers.onNetworkStatusdefault: [500, 502, 503]Array of HTTP status codes that trigger recording.
Protect against memory issues and runaway sessions:
limits.maxLogsdefault: 10,000Maximum number of log entries before session auto-ends.
limits.maxSizedefault: 50MBMaximum uncompressed session size in bytes. Data is gzipped before upload.
limits.maxDurationdefault: 1800Maximum session duration in seconds (30 minutes).
limits.idleTimeoutdefault: 120Seconds of inactivity before auto-ending session (2 minutes).
limits.rateLimitdefault: 100Maximum logs per second. Excess logs are dropped.
limits.deduplicatedefault: trueCollapse duplicate consecutive log entries.
limits.maxNetworkBodySizedefault: 10KBMaximum size in bytes for network request/response bodies. Larger bodies are truncated.
Control when sessions automatically end:
autoEnd.continueOnRefreshdefault: trueContinue the same session across page refreshes (uses sessionStorage).
autoEnd.onIdledefault: trueAutomatically end session when idle timeout is reached.
autoEnd.onLimitReacheddefault: trueAutomatically end session when any limit is hit.
Customize SDK behavior with optional hooks. All hooks are optional:
hooks: {// Called on every captured actiononAction: (log, session) => {console.log('Captured:', log.type, log.data);},// Transform/filter logs before sending - return null to drop allbeforeSend: (logs) => {return logs.filter(log => log.type !== 'performance');},// Called when session startsonSessionStart: (session) => {analytics.track('logspace_session_started', { id: session.id });},// Called when session endsonSessionEnd: (session, logs) => {console.log(`Session ended with ${logs.length} logs`);},// Called when a limit is reachedonLimitReached: (reason) => {// reason: 'maxLogs' | 'maxSize' | 'maxDuration' | 'idle'console.warn('Session ended:', reason);},// Called on transport/network errorsonError: (error) => {Sentry.captureException(error);},}
hooks.onAction(log, session) => voidCalled on every captured log entry.
hooks.beforeSend(logs) => logs | nullTransform or filter logs before sending. Return null to drop.
hooks.onSessionStart(session) => voidCalled when a new session begins.
hooks.onSessionEnd(session, logs) => voidCalled when session ends with all captured logs.
hooks.onLimitReached(reason) => voidCalled when session auto-ends due to limits (maxLogs, maxSize, maxDuration, idle).
hooks.onError(error) => voidCalled on transport/network errors when sending data.
hooks.onSamplingTrigger(trigger, log?) => voidCalled when sampling is triggered (error, consoleError, networkError, manual).
hooks.onSessionDiscarded(session, reason) => voidCalled when a sampling session is discarded (no trigger occurred). Reason: noTrigger, idle, maxDuration, manual.
debugdefault: falseEnable verbose console logging for debugging SDK behavior.
dryRundefault: falseWhen true, captures data but doesn't send to server. Useful for testing.
Identify the current user for session attribution:
LogSpace.identify('user-123', {plan: 'premium',company: 'Acme Inc',});
Track custom events:
LogSpace.track('button_clicked', {buttonId: 'checkout',page: '/cart',});
Add navigation breadcrumbs:
LogSpace.breadcrumb('User navigated to checkout', 'navigation');
Manually trigger recording (when using sampling mode):
LogSpace.trigger('user_reported_issue');
startSession(metadata?)Start a new recording session
stopSession()Stop the current session
pauseRecording()Pause recording temporarily
resumeRecording()Resume paused recording
getSession()Get current session info
getSessionLogs()Get all logs for current session
getConfig()Get current configuration (read-only)
setConfig(updates)Update config at runtime (rateLimit, maxLogs, maxSize, idleTimeout, debug)
import { useEffect } from 'react';import LogSpace from '@logspace/sdk';function App() {useEffect(() => {LogSpace.init({serverUrl: 'https://your-server.com',apiKey: 'your-api-key',});return () => LogSpace.destroy();}, []);return <YourApp />;}
'use client';import { useEffect } from 'react';import LogSpace from '@logspace/sdk';export function LogSpaceProvider({ children }) {useEffect(() => {LogSpace.init({serverUrl: 'https://your-server.com',apiKey: 'your-api-key',});return () => LogSpace.destroy();}, []);return <>{children}</>;}
import { createApp } from 'vue';import LogSpace from '@logspace/sdk';import App from './App.vue';LogSpace.init({serverUrl: import.meta.env.VITE_LOGSPACE_URL,apiKey: import.meta.env.VITE_LOGSPACE_API_KEY,});createApp(App).mount('#app');
The SDK automatically queues logs when offline and syncs when the connection is restored:
Logs are stored in IndexedDB with configurable limits
Queued logs are automatically uploaded in batches
The SDK uses multiple layers of persistence to ensure session data is never lost:
Every 15 seconds, session data is saved to IndexedDB. If the browser crashes, this checkpoint is recovered on next page load.
On page unload, data is saved to localStorage (synchronous, reliable). This ensures data survives even if IndexedDB operations don't complete.
If sending fails (network error, server down), sessions are queued for automatic retry with exponential backoff.
Each session includes an endReason field indicating how it ended:
| End Reason | Description |
|---|---|
manual | Session stopped via stopSession() or destroy() |
idle | Auto-ended due to idle timeout (no user activity) |
maxLogs | Hit limits.maxLogs threshold |
maxSize | Hit limits.maxSize threshold |
maxDuration | Hit limits.maxDuration threshold |
unload | Page is being unloaded (navigation, refresh, close) |
navigateAway | Tab was hidden for longer than limits.navigateAwayTimeout |
crash | Recovered from browser crash (no cleanup code ran) |
samplingTriggered | Sampling mode session ended after trigger + recordAfter |
| Format | Size | Gzipped |
|---|---|---|
| ESM | 80.5 KB | 16.5 KB |
| UMD | 39.1 KB | 11.5 KB |
| IIFE | 39.0 KB | 11.4 KB |
serverUrl and apiKey are correctdebug: true to see console logsMake sure you're importing correctly:
// ✓ Correctimport LogSpace from '@logspace/sdk';// ✗ Wrong - won't have typesimport LogSpace from '@logspace/sdk/logspace.esm.js';
If the SDK uses too much memory:
limits.maxLogs (default: 10,000)limits.maxSize (default: 50MB uncompressed)capture.rrweb (DOM recording is expensive)privacy.excludeUrls to skip high-traffic endpoints