Skip to main content

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 buffer management.

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 sent and dropped entries for 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',
});
With custom configuration:
const monitor = adaline.initMonitor({
  projectId: 'your-project-id',
  flushInterval: 5,        // flush every 5 seconds (default: 1)
  maxBufferSize: 100,      // flush when 100 items buffered (default: 1000)
});

Properties

buffer

buffer: BufferedEntry[]
In-memory array of BufferedEntry items 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);
  }
});

projectId

projectId: string
The project ID that all traces and spans are associated with.

sentCount

sentCount: number
Number of entries successfully sent to the API.

droppedCount

droppedCount: number
Number of entries dropped due to buffer overflow or send failure.

defaultContent

defaultContent: LogSpanContent
Default LogSpanContent used when no explicit content is provided. Defaults to { type: 'Other', input: '{}', output: '{}' }.

logger

readonly logger: Logger
Logger instance used for SDK diagnostics.

Methods

logTrace()

Create a new trace and add it to the buffer.
logTrace(options: LogTraceOptions): Trace

Parameters

options
object
required

Returns

trace
Trace
A new Trace instance. See Trace Class for details.

Examples

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

  1. Skips if a flush is already in progress (prevents concurrent flushes)
  2. Filters for entries marked as ready (via trace.end() or span.end())
  3. Sends each entry to the API with automatic retry
  4. Updates traceId on traces after successful creation
  5. Removes successfully flushed entries from buffer
  6. Drops entries that fail after retries and increments droppedCount

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

const trace = monitor.logTrace({ name: 'Important Event' });
trace.end();

// Ensure it's sent immediately
await monitor.flush();

stop()

Stop the background flush timer.
stop(): void
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();

enforceBufferLimit()

Enforces the maxBufferSize limit by dropping the oldest entries when the buffer is full.
enforceBufferLimit(): void
This method is called automatically when entries are added to the buffer. You typically don’t need to call it manually.

Complete Examples

Basic Usage

import { Adaline } from '@adaline/client';

const adaline = new Adaline();
const monitor = adaline.initMonitor({
  projectId: 'my-project',
});

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({ debug: true });

const monitor = adaline.initMonitor({
  projectId: process.env.PROJECT_ID!,
  flushInterval: 5,
  maxBufferSize: 100,
});

// Health check
setInterval(() => {
  const bufferSize = monitor.buffer.length;

  console.log('Monitor Health:', {
    bufferSize,
    sentCount: monitor.sentCount,
    droppedCount: monitor.droppedCount,
  });

  if (monitor.droppedCount > 0) {
    console.warn(`${monitor.droppedCount} entries have been dropped`);
  }

  if (bufferSize > 50) {
    console.warn('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);
});

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,
  maxBufferSize: 500,
});

// Monitor buffer usage
setInterval(() => {
  const bufferSize = monitor.buffer.length;
  
  // Warn if buffer is growing
  if (bufferSize > 300) {
    console.warn(`Buffer size: ${bufferSize} (high volume)`);
  }
  
  // Force flush if critical
  if (bufferSize > 450) {
    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);
    expect(monitor.sentCount).toBeGreaterThan(0);
  });

  test('handles flush failures', async () => {
    const trace = monitor.logTrace({ name: 'Invalid' });
    trace.end();

    // Mock API to fail
    // ... your mocking code ...

    await monitor.flush();

    expect(monitor.droppedCount).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();

    // Buffer should be capped at maxBufferSize
    // ... assertions ...
  });
});

Type Definitions

interface LogTraceOptions {
  name: string;
  status?: TraceStatus;
  sessionId?: string;
  referenceId?: string;
  tags?: string[];
  attributes?: Record<string, LogAttributesValue>;
}

type LogAttributesValue = 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 };
The attributes field accepts Record<string, LogAttributesValue> where LogAttributesValue = string | number | boolean.