Emitting Events

Master the three telemetry emit modes — direct, session, and scoped execution. Learn attributes, levels, error details, source metadata, and event envelope structure.

Emitting Events

The emit() method is the primary way to send telemetry events. It supports three distinct patterns — choose the right one for each scenario.


The Three Emit Modes

Mode A: Direct Emit

No session required. Each event stands alone. Best for background jobs, startup events, or one-off emissions.

telemetry.emit("service.booted", {
  attributes: { "ctx.service.startup_ms": 142 },
});

telemetry.emit("cache.warmed", {
  level: "debug",
  attributes: { "ctx.cache.keys_loaded": 1200 },
});

Mode B: Manual Session Handle

Create a session, bind actor/scope, emit events, and end it. Best for request handlers that need explicit session control.

const session = telemetry.session()
  .actor("user", "usr_123", { role: "admin" })
  .scope("organization", "org_456")
  .attributes({ "ctx.request.id": "req_abc" });

session.emit("user.profile.updated", {
  attributes: { "ctx.user.field": "email" },
});

session.emit("user.notification.sent", {
  attributes: { "ctx.notification.type": "email" },
});

await session.end();

Mode C: Scoped Execution

All events inside run() automatically inherit the session context via AsyncLocalStorage. Best for HTTP request handlers — no manual context passing.

await telemetry.session()
  .actor("user", "usr_123")
  .scope("organization", "org_456")
  .run(async () => {
    // These events automatically carry user + org context
    telemetry.emit("request.started", { ... });
    await doWork();
    telemetry.emit("request.completed", { ... });
  });

Mode C is the recommended approach for request-response cycles. It uses Node.js AsyncLocalStorage to maintain session context across async boundaries without explicit passing.


Event Levels

Telemetry supports five severity levels, matching OpenTelemetry conventions:

LevelTypical UseDefault Sampling
debugVerbose diagnostic information1%
infoGeneral operational events10%
warnUnexpected but handled situations100%
errorFailures that need attention100%
fatalCritical failures requiring immediate action100%
// Default level is "info"
telemetry.emit("user.login.succeeded", { ... });

// Explicit levels
telemetry.emit("cache.miss", { level: "debug", ... });
telemetry.emit("payment.processed", { level: "info", ... });
telemetry.emit("rate_limit.approaching", { level: "warn", ... });
telemetry.emit("payment.failed", { level: "error", ... });
telemetry.emit("database.corrupted", { level: "fatal", ... });

Attributes

Attributes are a flat key-value map attached to every event. They provide the structured data that makes telemetry queryable.

Naming Convention

Use ctx. prefix with dot-separated names for consistency:

telemetry.emit("order.shipped", {
  attributes: {
    "ctx.order.id": "ord_789",
    "ctx.order.total": 14999,
    "ctx.order.currency": "usd",
    "ctx.shipment.carrier": "fedex",
    "ctx.shipment.tracking_number": "1Z999AA10123456784",
    "ctx.shipment.weight_kg": 2.3,
  },
});

Merging with Session Attributes

Event attributes are merged with session attributes — event-level values override session values:

const session = telemetry.session()
  .attributes({
    "ctx.request.id": "req_abc",
    "ctx.request.method": "POST",
  });

// Event attributes override session attributes
session.emit("request.completed", {
  attributes: {
    "ctx.request.status": 200,         // Added
    "ctx.request.method": "GET",       // Overrides session "POST"
  },
});
// Result: { "ctx.request.id": "req_abc", "ctx.request.method": "GET", "ctx.request.status": 200 }

Error Details

Pass structured error information alongside events using the error field:

telemetry.emit("payment.failed", {
  level: "error",
  error: {
    name: "PaymentError",
    message: "Card declined by issuer",
    code: "CARD_DECLINED",
    stack: undefined,  // Optional stack trace
  },
  attributes: {
    "ctx.payment.id": "pay_456",
    "ctx.payment.provider": "stripe",
  },
});

Error Object Structure

FieldTypeDescription
namestringError class name (e.g., "PaymentError")
messagestringHuman-readable error description
codestring?Machine-readable error code
stackstring?Stack trace (optional)

Error details are preserved in the envelope and can be used by transports like Sentry, Slack, or Discord for rich error reporting.


Source Metadata

Tag events with their origin in the codebase using the source field:

telemetry.emit("feature.flag.evaluated", {
  source: {
    causer: "@myapp/flags",
    file: "flags/evaluate.ts",
    line: 142,
  },
  attributes: {
    "ctx.flag.key": "new-dashboard",
    "ctx.flag.value": true,
  },
});
FieldTypeDescription
causerstring?Package or module responsible
filestring?Source file path
linenumber?Line number

The Event Envelope

Every emit() call produces an envelope — a normalized payload sent to all transports:

interface IgniterTelemetryEnvelope {
  name: string;              // Event name
  time: string;              // ISO 8601 timestamp
  level: string;             // debug | info | warn | error | fatal
  service: string;           // From withService()
  environment: string;       // From withEnvironment()
  version?: string;          // From withVersion()
  sessionId: string;         // Auto-generated or from session
  actor?: {                  // From session or emit input
    type: string;
    id?: string;
    tags?: Record<string, unknown>;
  };
  scope?: {                  // From session or emit input
    type: string;
    id: string;
    tags?: Record<string, unknown>;
  };
  attributes?: Record<string, unknown>;  // Redacted
  error?: {
    name: string;
    message: string;
    code?: string;
    stack?: string;
  };
  source?: {
    causer?: string;
    file?: string;
    line?: number;
  };
}

Emit with Custom Session ID

Override the auto-generated session ID:

telemetry.emit("request.started", {
  sessionId: "trace_abc_123",  // Custom session ID
  attributes: {
    "ctx.request.path": "/api/orders",
  },
});

Emit with Custom Timestamp

Override the auto-generated timestamp:

telemetry.emit("historical.event", {
  time: "2024-01-15T10:30:00.000Z",  // Custom timestamp
  attributes: {
    "ctx.event.recorded_at": "2024-01-15T10:30:00.000Z",
  },
});

Flushing and Shutdown

For transports that buffer events (like HTTP with retries), you can explicitly flush:

// Flush pending events without shutting down
await telemetry.flush();

// Graceful shutdown: flush + clean up
await telemetry.shutdown();

Always call shutdown() before process exit. Transports may have buffered events or open connections that need cleanup.


Next Steps