Monitor Class
The Monitor class manages buffering, batching, and flushing of traces and spans to the Adaline API. It handles automatic retries, background flushing, and failure tracking.
Overview
The Monitor acts as a central coordinator for all observability operations:
Buffers traces and spans in memory
Batches multiple entries for efficient API calls
Flushes automatically based on time intervals or buffer size
Retries failed requests with exponential backoff
Tracks failed entries for debugging and monitoring
Creation
Create a Monitor using the Adaline.initMonitor() method:
import { Adaline } from '@adaline/client' ;
const adaline = new Adaline ();
const monitor = adaline . initMonitor ({
projectId: 'your-project-id' ,
flushInterval: 5 , // flush every 5 seconds
maxBufferSize: 10 , // or when 10 items buffered
maxContinuousFlushFailures: 3
});
Properties
buffer
In-memory array storing traces and spans waiting to be flushed.
Items are added when you call logTrace() or logSpan(), and removed after successful flush.
Example:
console . log ( `Buffer size: ${ monitor . buffer . length } ` );
// Inspect buffered items
monitor . buffer . forEach ( entry => {
if ( entry . category === 'trace' ) {
console . log ( 'Trace:' , entry . data . trace . trace . name );
} else {
console . log ( 'Span:' , entry . data . span . span . name );
}
});
failedFlushEntries
failedFlushEntries : BufferedEntry []
Array storing entries that failed to flush after exhausting retry attempts.
Example:
// Check for failed entries
if ( monitor . failedFlushEntries . length > 0 ) {
console . error ( ` ${ monitor . failedFlushEntries . length } entries failed to flush` );
monitor . failedFlushEntries . forEach ( entry => {
console . error ( 'Failed entry:' , entry . category , entry . data );
});
}
flushStatus
flushStatus : {
stopped : boolean ;
consecutiveFailures : number ;
lastError : Error | null ;
lastFlushed : Date | null ;
}
Current status of the background flush process.
Properties:
stopped - Whether the background flush has been stopped (manually or due to failures)
consecutiveFailures - Number of consecutive failed flush attempts
lastError - Most recent flush error
lastFlushed - Timestamp of last successful flush
Example:
// Monitor health check
setInterval (() => {
const status = monitor . flushStatus ;
if ( status . stopped ) {
console . error ( '⛔ Background flush stopped!' );
} else if ( status . consecutiveFailures > 0 ) {
console . warn ( `⚠️ ${ status . consecutiveFailures } consecutive failures` );
} else {
console . log ( `✅ Healthy - Last flush: ${ status . lastFlushed } ` );
}
}, 30000 );
projectId
The project ID that all traces and spans are associated with.
Methods
logTrace()
Create a new trace and add it to the buffer.
logTrace ( options : LogTraceOptions ): Trace
Parameters
Human-readable name for this trace (e.g., “User Login”, “Generate Report”).
status
TraceStatus
default: "unknown"
Initial status: 'success' | 'failure' | 'aborted' | 'cancelled' | 'pending' | 'unknown'
Session identifier to group related traces (e.g., user session ID).
Custom reference ID for this trace. Auto-generated UUID if not provided.
Array of tags for categorization and filtering (e.g., ['api', 'production']).
attributes
Record<string, string | number | boolean>
Key-value metadata for additional context (e.g., { userId: '123', region: 'us-east' }).
Returns
Examples
Basic
With Session
Pending Operation
Custom Reference ID
const trace = monitor . logTrace ({
name: 'API Request'
});
// Do work...
trace . end ();
flush()
Manually flush all ready entries in the buffer to the API.
async flush (): Promise < void >
This method is automatically called on a timer (based on flushInterval) and when the buffer reaches maxBufferSize. Manual calls are typically only needed during shutdown (especially in serverless environments) or testing.
Behavior
Skips if a flush is already in progress (prevents concurrent flushes)
Filters for entries marked as ready (via trace.end() or span.end())
Sends each entry to the API with automatic retry
Updates traceId on traces after successful creation
Removes successfully flushed entries from buffer
Tracks failed entries in failedFlushEntries
Updates flushStatus
Retry Logic
5xx errors : Retry with exponential backoff (up to 10 retries, 20s total)
4xx errors : Fail immediately (no retry)
Network errors : Retry with exponential backoff
Examples
Manual Flush
Graceful Shutdown
Testing
Error Handling
const trace = monitor . logTrace ({ name: 'Important Event' });
trace . end ();
// Ensure it's sent immediately
await monitor . flush ();
stop()
Stop the background flush timer.
After calling stop(), the monitor will no longer automatically flush. You must manually call flush() to send buffered entries.
Example
const monitor = adaline . initMonitor ({ projectId: 'proj_123' });
// Use the monitor...
// Stop when done (e.g., during shutdown)
monitor . stop ();
// Final flush
await monitor . flush ();
autoFlushIfNeeded()
Automatically triggers a flush if the buffer has reached maxBufferSize.
autoFlushIfNeeded (): void
This method is called automatically by logTrace() and logSpan(). You typically don’t need to call it manually.
Example
// This is done automatically:
const trace = monitor . logTrace ({ name: 'Request' });
// → autoFlushIfNeeded() is called
// → Flushes if buffer.length >= maxBufferSize
Complete Examples
Basic Usage
import { Adaline } from '@adaline/client' ;
const adaline = new Adaline ();
const monitor = adaline . initMonitor ({
projectId: 'my-project' ,
flushInterval: 5 ,
maxBufferSize: 10
});
async function handleRequest ( userId : string ) {
const trace = monitor . logTrace ({
name: 'User Request' ,
sessionId: userId ,
tags: [ 'api' ]
});
const span = trace . logSpan ({
name: 'Process Data' ,
tags: [ 'processing' ]
});
// Do work...
await processData ();
span . update ({ status: 'success' });
span . end ();
trace . update ({ status: 'success' });
trace . end ();
// Automatically flushed based on timer/buffer size
}
Production Setup with Health Monitoring
import { Adaline } from '@adaline/client' ;
const adaline = new Adaline ({
logger : ( msg , err ) => console . log ( `[Adaline] ${ msg } ` , err )
});
const monitor = adaline . initMonitor ({
projectId: process . env . PROJECT_ID ! ,
flushInterval: 5 ,
maxBufferSize: 20 ,
maxContinuousFlushFailures: 5
});
// Health check
setInterval (() => {
const status = monitor . flushStatus ;
const bufferSize = monitor . buffer . length ;
const failedCount = monitor . failedFlushEntries . length ;
console . log ( 'Monitor Health:' , {
stopped: status . stopped ,
consecutiveFailures: status . consecutiveFailures ,
bufferSize ,
failedCount ,
lastFlushed: status . lastFlushed
});
// Alert if unhealthy
if ( status . stopped ) {
alertOps ( 'Monitor flush stopped!' , status . lastError );
} else if ( status . consecutiveFailures >= 3 ) {
alertOps ( 'Monitor having issues' , {
failures: status . consecutiveFailures ,
error: status . lastError
});
} else if ( bufferSize > 50 ) {
alertOps ( 'Monitor buffer growing' , { bufferSize });
}
}, 60000 ); // Check every minute
// Graceful shutdown
process . on ( 'SIGTERM' , async () => {
console . log ( 'Shutting down...' );
try {
await monitor . flush ();
console . log ( 'Flushed remaining entries' );
} catch ( error ) {
console . error ( 'Error during final flush:' , error );
}
monitor . stop ();
process . exit ( 0 );
});
function alertOps ( message : string , context ?: any ) {
// Send to your monitoring system
console . error ( `[ALERT] ${ message } ` , context );
}
High-Volume Application
import { Adaline } from '@adaline/client' ;
const adaline = new Adaline ();
// Optimize for high volume
const monitor = adaline . initMonitor ({
projectId: 'high-volume-app' ,
flushInterval: 2 , // Flush more frequently
maxBufferSize: 50 , // Allow larger buffer
maxContinuousFlushFailures: 10
});
// Monitor buffer usage
setInterval (() => {
const bufferSize = monitor . buffer . length ;
// Warn if buffer is growing
if ( bufferSize > 30 ) {
console . warn ( `Buffer size: ${ bufferSize } (high volume)` );
}
// Force flush if critical
if ( bufferSize > 45 ) {
console . warn ( 'Forcing flush due to high buffer size' );
monitor . flush (). catch ( err => {
console . error ( 'Forced flush failed:' , err );
});
}
}, 1000 );
// Request handler
async function handleRequest ( req : Request ) {
const trace = monitor . logTrace ({
name: req . url ,
sessionId: req . headers . get ( 'session-id' ) || undefined ,
tags: [ 'api' , 'high-volume' ],
attributes: {
method: req . method ,
path: req . url ,
userAgent: req . headers . get ( 'user-agent' ) || 'unknown'
}
});
try {
const result = await processRequest ( req );
trace . update ({ status: 'success' });
return result ;
} catch ( error ) {
trace . update ({ status: 'failure' });
throw error ;
} finally {
trace . end ();
}
}
Testing with Monitor
import { Adaline } from '@adaline/client' ;
import { describe , test , expect , beforeEach , afterEach } from '@jest/globals' ;
describe ( 'Monitor Tests' , () => {
let adaline : Adaline ;
let monitor : Monitor ;
beforeEach (() => {
adaline = new Adaline ();
monitor = adaline . initMonitor ({
projectId: 'test-project' ,
flushInterval: 999999 // Don't auto-flush during tests
});
});
afterEach ( async () => {
await monitor . flush ();
monitor . stop ();
});
test ( 'creates trace and span' , async () => {
const trace = monitor . logTrace ({ name: 'Test Trace' });
const span = trace . logSpan ({ name: 'Test Span' });
expect ( monitor . buffer . length ). toBe ( 2 );
span . end ();
trace . end ();
expect ( monitor . buffer . filter ( e => e . ready ). length ). toBe ( 2 );
await monitor . flush ();
expect ( trace . traceId ). toBeDefined ();
expect ( monitor . buffer . length ). toBe ( 0 );
});
test ( 'handles flush failures' , async () => {
// Create invalid trace
const trace = monitor . logTrace ({ name: 'Invalid' });
trace . end ();
// Mock API to fail
// ... your mocking code ...
await monitor . flush ();
expect ( monitor . failedFlushEntries . length ). toBeGreaterThan ( 0 );
expect ( monitor . flushStatus . consecutiveFailures ). toBeGreaterThan ( 0 );
});
test ( 'respects buffer size limit' , () => {
const smallMonitor = adaline . initMonitor ({
projectId: 'test' ,
maxBufferSize: 2 ,
flushInterval: 999999
});
const trace1 = smallMonitor . logTrace ({ name: 'T1' });
const trace2 = smallMonitor . logTrace ({ name: 'T2' });
const trace3 = smallMonitor . logTrace ({ name: 'T3' });
trace1 . end ();
trace2 . end ();
trace3 . end ();
// Should have triggered auto-flush
// ... assertions ...
});
});
Type Definitions
interface LogTraceOptions {
name : string ;
status ?: TraceStatus ;
sessionId ?: string ;
referenceId ?: string ;
tags ?: string [];
attributes ?: Record < string , string | number | boolean >;
}
type TraceStatus = 'success' | 'failure' | 'aborted' | 'cancelled' | 'pending' | 'unknown' ;
type BufferedEntry =
| { ready : boolean ; category : 'trace' ; data : Trace }
| { ready : boolean ; category : 'span' ; data : Span };
interface FlushStatus {
stopped : boolean ;
consecutiveFailures : number ;
lastError : Error | null ;
lastFlushed : Date | null ;
}