Skip to main content

Overview

Emitters define where metric events are sent after each LLM API call. The SDK provides several built-in emitters and supports custom implementations.
type MetricEmitter = (event: MetricEvent) => void | Promise<void>;

Console Emitter

Logs metrics to the console. Perfect for development and debugging.
import { createConsoleEmitter } from "aden";

const emitter = createConsoleEmitter({
  pretty: true,  // Formatted JSON output
});
Output:
{
  "provider": "openai",
  "model": "gpt-4o",
  "input_tokens": 15,
  "output_tokens": 42,
  "latency_ms": 523,
  "timestamp": "2024-01-15T10:30:00.000Z"
}

HTTP Transport

Sends metrics to an HTTP endpoint with batching and retries.
import { createHttpTransport } from "aden";

const emitter = createHttpTransport({
  url: "https://your-backend.com/api/metrics",
  headers: {
    "X-API-Key": process.env.METRICS_API_KEY!,
    "Content-Type": "application/json",
  },
  // Optional configuration
  timeout: 5000,        // Request timeout (ms)
  retries: 3,           // Number of retry attempts
  retryDelay: 1000,     // Base delay between retries (ms)
});

Batch Emitter

Collects events and flushes them in batches for efficiency.
import { createBatchEmitter, createHttpTransport } from "aden";

const emitter = createBatchEmitter({
  handler: createHttpTransport({ url: "..." }),
  batchSize: 50,           // Flush after 50 events
  flushIntervalMs: 5000,   // Or every 5 seconds
  maxQueueSize: 10000,     // Maximum queue size
  onError: (error) => {
    console.error("Batch flush failed:", error);
  },
});

// Manual flush (useful before shutdown)
await emitter.flush();

Multi Emitter

Send metrics to multiple destinations simultaneously.
import { createMultiEmitter, createConsoleEmitter, createHttpTransport } from "aden";

const emitter = createMultiEmitter([
  createConsoleEmitter({ pretty: true }),
  createHttpTransport({ url: "https://backend-a.com/metrics" }),
  createHttpTransport({ url: "https://backend-b.com/metrics" }),
]);

Filtered Emitter

Conditionally emit events based on criteria.
import { createFilteredEmitter, createHttpTransport } from "aden";

const emitter = createFilteredEmitter({
  emitter: createHttpTransport({ url: "..." }),
  filter: (event) => {
    // Only emit events for expensive models
    return ["gpt-4o", "claude-3-5-sonnet"].includes(event.model);
  },
});

Transform Emitter

Modify events before emission.
import { createTransformEmitter, createHttpTransport } from "aden";

const emitter = createTransformEmitter({
  emitter: createHttpTransport({ url: "..." }),
  transform: (event) => ({
    ...event,
    // Add custom fields
    environment: process.env.NODE_ENV,
    service: "my-app",
    // Redact sensitive data
    messages: undefined,
  }),
});

JSON File Emitter

Write metrics to a JSON Lines file.
import { createJsonFileEmitter } from "aden";

const emitter = createJsonFileEmitter({
  path: "./metrics.jsonl",
  append: true,  // Append to existing file
});

Memory Emitter

Store events in memory. Useful for testing.
import { createMemoryEmitter } from "aden";

const emitter = createMemoryEmitter();

// After some API calls...
console.log(emitter.events);
// [{ provider: "openai", ... }, { provider: "anthropic", ... }]

// Clear events
emitter.clear();

Noop Emitter

Discards all events. Useful for performance testing.
import { createNoopEmitter } from "aden";

const emitter = createNoopEmitter();

Custom Emitter

Implement your own emitter:
import type { MetricEmitter, MetricEvent } from "aden";

const customEmitter: MetricEmitter = async (event: MetricEvent) => {
  // Send to your analytics service
  await analytics.track("llm_call", {
    provider: event.provider,
    model: event.model,
    tokens: event.total_tokens,
    cost: calculateCost(event),
  });

  // Store in database
  await db.insert("metrics", event);

  // Send to monitoring
  if (event.error) {
    await alerting.notify("LLM Error", event.error);
  }
};

Combining Emitters

Create sophisticated pipelines:
import {
  createMultiEmitter,
  createFilteredEmitter,
  createBatchEmitter,
  createTransformEmitter,
  createConsoleEmitter,
  createHttpTransport,
} from "aden";

const emitter = createMultiEmitter([
  // Always log to console in development
  process.env.NODE_ENV === "development" && createConsoleEmitter({ pretty: true }),

  // Send all events to primary backend
  createBatchEmitter({
    handler: createHttpTransport({ url: "https://primary.com/metrics" }),
    batchSize: 100,
  }),

  // Send only errors to monitoring
  createFilteredEmitter({
    emitter: createHttpTransport({ url: "https://monitoring.com/errors" }),
    filter: (event) => !!event.error,
  }),

  // Send anonymized data to analytics
  createTransformEmitter({
    emitter: createHttpTransport({ url: "https://analytics.com/events" }),
    transform: (event) => ({
      model: event.model,
      provider: event.provider,
      tokens: event.total_tokens,
      // Remove identifiable information
      trace_id: undefined,
      metadata: undefined,
    }),
  }),
].filter(Boolean));

MetricEvent Schema

Every emitter receives events with this structure:
interface MetricEvent {
  // Identity (OpenTelemetry-compatible)
  trace_id: string;
  span_id: string;
  parent_span_id?: string;
  request_id: string | null;

  // Request details
  provider: "openai" | "anthropic" | "gemini";
  model: string;
  stream: boolean;
  timestamp: string; // ISO 8601

  // Performance
  latency_ms: number;
  status_code?: number;
  error?: string;

  // Token usage
  input_tokens: number;
  output_tokens: number;
  total_tokens: number;
  cached_tokens: number;
  reasoning_tokens: number;

  // Rate limits
  rate_limit_remaining_requests?: number;
  rate_limit_remaining_tokens?: number;

  // Call relationships
  call_sequence?: number;
  agent_stack?: string[];
  call_site_file?: string;
  call_site_line?: number;

  // Tool usage
  tool_call_count?: number;
  tool_names?: string;

  // Custom
  service_tier?: string;
  metadata?: Record<string, string>;
}

Next Steps