Overview
Loom’s session analytics system tracks user engagement periods to enable release health metrics like crash-free rate and adoption tracking. Uses hybrid storage: individual sessions for recent data (30 days) and hourly aggregates for historical dashboards.
Key Features
Session tracking for web, Node.js, and Rust applications
Release health metrics : crash-free rate, adoption percentage
Hybrid storage : individual sessions (30 days) + hourly aggregates (forever)
Sampling support for high-volume applications
Real-time updates via SSE for release health changes
Core Concepts
Session
A single user engagement period:
pub struct Session {
pub id : SessionId ,
pub person_id : Option < PersonId >,
pub distinct_id : String ,
pub status : SessionStatus ,
pub release : Option < String >,
pub environment : String ,
pub error_count : u32 , // Handled errors
pub crash_count : u32 , // Unhandled errors
pub crashed : bool , // crash_count > 0
pub started_at : DateTime < Utc >,
pub ended_at : Option < DateTime < Utc >>,
pub duration_ms : Option < u64 >,
pub platform : Platform ,
pub sampled : bool ,
pub sample_rate : f64 ,
}
Session statuses:
Status Description ActiveSession ongoing ExitedEnded normally (user closed tab, app backgrounded) CrashedHad at least one unhandled error AbnormalEnded unexpectedly (no end signal) ErroredHad handled errors but completed normally
pub enum Platform {
JavaScript , // Browser
Node , // Node.js
Rust ,
Python ,
}
Release Health
Computed metrics for a release:
pub struct ReleaseHealth {
pub release : String ,
pub environment : String ,
pub total_sessions : u64 ,
pub crashed_sessions : u64 ,
pub total_users : u64 ,
pub crashed_users : u64 ,
pub crash_free_session_rate : f64 , // Percentage
pub crash_free_user_rate : f64 , // Percentage
pub adoption_rate : f64 , // Percentage
pub adoption_stage : AdoptionStage ,
}
Adoption stages:
Stage Adoption Rate New< 5% Growing5-50% Adopted50-95% Replaced< 5% (was higher before)
Automatic Tracking
Sessions are automatically tracked by the crash SDK:
Rust
TypeScript (Browser)
Node.js
use loom_crash :: CrashClient ;
// Sessions enabled by default
let crash = CrashClient :: builder ()
. api_key ( "loom_crash_xxx" )
. release ( env! ( "CARGO_PKG_VERSION" ))
. with_session_tracking ( true ) // Default: true
. session_sample_rate ( 1.0 ) // Default: 1.0 (100%)
. build () ? ;
// Session starts automatically on build()
// Errors update session
crash . capture_error ( & error ); // Increments error_count
// Unhandled errors mark as crashed
// Session ends on shutdown
crash . shutdown () . await ? ;
import { CrashClient } from '@loom/crash' ;
const crash = new CrashClient ({
apiKey: 'loom_crash_xxx' ,
release: '1.2.3' ,
// Session options
autoSessionTracking: true , // Default: true
sessionSampleRate: 1.0 , // Default: 1.0
});
// Session starts automatically
// Session ends on page unload or manual call
crash . endSession ();
import { CrashClient } from '@loom/crash' ;
const crash = new CrashClient ({
apiKey: 'loom_crash_xxx' ,
release: '1.2.3' ,
autoSessionTracking: true ,
});
// Session starts on init
// Session ends on process exit or manual call
process . on ( 'SIGTERM' , async () => {
await crash . endSession ();
process . exit ( 0 );
});
Session Lifecycle
Web (Browser)
Page Load
Session starts when SDK initializes
User Activity
Session remains active while user interacts
Tab/Window Close
Session ends via beforeunload or pagehide event
Visibility Timeout
If page hidden for 30+ minutes, session ends and new one starts when visible again
Node.js / Rust
Process Start
Session starts when crash client initializes
Process Exit
Session ends normally on clean shutdown
Uncaught Exception
Session marked as Crashed on unhandled error
Sampling
For high-traffic applications, reduce storage with sampling:
const crash = new CrashClient ({
apiKey: 'loom_crash_xxx' ,
release: '1.2.3' ,
// Sample 10% of normal sessions
sessionSampleRate: 0.1 ,
// Always store crashed sessions (default: true)
alwaysStoreCrashed: true ,
});
Crashed sessions are always stored regardless of sample rate. Only successful sessions are sampled.
Sampling Rules
Base sample rate (e.g., 0.1 = 10%)
Always store crashed (recommended: true)
Metrics are automatically adjusted for sample rate
Example calculation:
Sampled sessions: 1,000
Crashed sessions: 50 (always stored)
Sample rate: 0.1 (10%)
Adjusted total = 50 + ((1000 - 50) / 0.1) = 9,550
Crash-free rate = (9550 - 50) / 9550 = 99.48%
Release Health Metrics
Crash-Free Session Rate
Percentage of sessions without unhandled errors:
crash_free_session_rate =
(total_sessions - crashed_sessions) / total_sessions × 100
Example:
Total sessions: 10,000
Crashed sessions: 50
Crash-free rate: 99.5%
Crash-Free User Rate
Percentage of users who didn’t experience crashes:
crash_free_user_rate =
(total_users - crashed_users) / total_users × 100
Example:
Total users: 1,000
Users with crashes: 25
Crash-free user rate: 97.5%
User rate is typically lower than session rate because one user can have multiple sessions.
Adoption Rate
Percentage of traffic on this release:
adoption_rate =
release_sessions / all_sessions × 100
Example:
Release 1.2.3 sessions: 7,500
All sessions: 10,000
Adoption rate: 75% (Adopted stage)
API Endpoints
Session Ingestion (SDK)
Start a session Request: {
"session_id" : "ses_xxx" ,
"distinct_id" : "user_123" ,
"release" : "1.2.3" ,
"environment" : "production" ,
"platform" : "javascript" ,
"started_at" : "2026-01-18T12:00:00Z" ,
"sample_rate" : 1.0
}
End a session Request: {
"session_id" : "ses_xxx" ,
"status" : "exited" ,
"error_count" : 0 ,
"crash_count" : 0 ,
"ended_at" : "2026-01-18T12:30:00Z" ,
"duration_ms" : 1800000
}
Release Health (User Auth)
GET /api/projects/:id/releases
List releases with health metrics Query Parameters:
environment - Filter by environment
start_date - Start of time range
end_date - End of time range
Response: {
"releases" : [
{
"release" : "1.2.3" ,
"environment" : "production" ,
"crash_free_session_rate" : 99.5 ,
"crash_free_user_rate" : 97.8 ,
"adoption_rate" : 75.2 ,
"adoption_stage" : "adopted" ,
"total_sessions" : 10000 ,
"crashed_sessions" : 50 ,
"total_users" : 1500
}
]
}
GET /api/projects/:id/releases/:version
Get detailed health for specific release Response: {
"release" : "1.2.3" ,
"environment" : "production" ,
"crash_free_session_rate" : 99.5 ,
"crash_free_user_rate" : 97.8 ,
"adoption_rate" : 75.2 ,
"adoption_stage" : "adopted" ,
"total_sessions" : 10000 ,
"crashed_sessions" : 50 ,
"total_users" : 1500 ,
"crashed_users" : 33 ,
"first_seen" : "2026-01-15T00:00:00Z" ,
"last_seen" : "2026-01-18T12:00:00Z" ,
"crash_free_rate_trend" : 0.2
}
GET /api/projects/:id/health/trend
Get health trend over time Query Parameters:
interval - hour, day, week
start_date - Start timestamp
end_date - End timestamp
Response: {
"trend" : [
{
"timestamp" : "2026-01-15T00:00:00Z" ,
"crash_free_session_rate" : 99.2 ,
"crash_free_user_rate" : 97.5 ,
"total_sessions" : 2000
},
{
"timestamp" : "2026-01-16T00:00:00Z" ,
"crash_free_session_rate" : 99.5 ,
"crash_free_user_rate" : 97.8 ,
"total_sessions" : 2100
}
]
}
Session Queries
GET /api/projects/:id/sessions
List recent sessions Query Parameters:
release - Filter by release
status - Filter by status (active, exited, crashed)
limit - Max results (default: 100)
GET /api/projects/:id/sessions/:sessionId
Get session detail Response: {
"session" : {
"id" : "ses_xxx" ,
"person_id" : "per_yyy" ,
"status" : "exited" ,
"release" : "1.2.3" ,
"environment" : "production" ,
"error_count" : 2 ,
"crash_count" : 0 ,
"duration_ms" : 1800000 ,
"platform" : "javascript" ,
"started_at" : "2026-01-18T12:00:00Z" ,
"ended_at" : "2026-01-18T12:30:00Z"
}
}
Real-time Updates (SSE)
Subscribe to release health changes:
const eventSource = new EventSource (
'https://loom.example.com/api/projects/proj-123/sessions/stream' ,
{ headers: { Authorization: 'Bearer <token>' } }
);
eventSource . addEventListener ( 'release.health_changed' , ( event ) => {
const data = JSON . parse ( event . data );
console . log ( `Release ${ data . release } : ${ data . crash_free_session_rate } % crash-free` );
updateDashboard ( data );
});
Events:
session.started - New session started
session.ended - Session ended
session.crashed - Session marked as crashed
release.health_changed - Release health metrics updated
Aggregation & Retention
Storage Strategy
Individual Sessions Retention: 30 daysPurpose: Debugging, detailed analysisFull session data with all context
Hourly Aggregates Retention: ForeverPurpose: Long-term dashboards, trendsRolled-up counts and statistics
Hourly Aggregation
Every hour, individual sessions are aggregated:
SELECT
release,
environment,
COUNT ( * ) as total_sessions,
SUM ( CASE WHEN status = 'crashed' THEN 1 ELSE 0 END ) as crashed_sessions,
COUNT ( DISTINCT person_id) as unique_users,
COUNT ( DISTINCT CASE WHEN crashed = 1 THEN person_id END ) as crashed_users,
AVG (duration_ms) as avg_duration_ms
FROM sessions
WHERE started_at >= ? AND started_at < ?
GROUP BY release, environment
After 30 days, individual sessions are deleted but aggregates remain.
Metric Definitions
Metric Formula Description Crash-free session rate (total - crashed) / total × 100% sessions without unhandled errors Crash-free user rate (users - crashed_users) / users × 100% users without crashes Adoption rate release_sessions / all_sessions × 100% traffic on this release Error rate errored / total × 100% sessions with handled errors
Best Practices
Set Release Version Always set release to track health per version Use semantic versioning or commit SHA
Use Sampling For high-traffic apps, sample normal sessions Crashed sessions are always stored
Monitor Trends Track crash-free rate trends over time Alert on significant drops
Environment Separation Separate production/staging environments Monitor both independently
See Also
Crash Tracking Investigate crashed sessions
Analytics Link sessions to person profiles