irontelemetry-js/dist/cjs/client.js
logikonline 8c300c1f8d Include dist folder for git-based installs
Unignored dist/ so yarn/npm can install from git URL
without needing to build locally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 15:12:47 -05:00

306 lines
9.7 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TelemetryClient = exports.enableDebugLogging = void 0;
exports.setDebugLogging = setDebugLogging;
const config_1 = require("./config");
const transport_1 = require("./transport");
const queue_1 = require("./queue");
const breadcrumbs_1 = require("./breadcrumbs");
const journey_1 = require("./journey");
/**
* Global debug logging flag. When enabled, all IronTelemetry clients
* will output debug information to the console.
*/
exports.enableDebugLogging = false;
/**
* Enable or disable global debug logging for all TelemetryClient instances.
* @param enabled Whether to enable debug logging
*/
function setDebugLogging(enabled) {
exports.enableDebugLogging = enabled;
}
/**
* Main IronTelemetry client class
*/
class TelemetryClient {
constructor(options) {
this.tags = {};
this.extra = {};
this.isInitialized = false;
this.options = (0, config_1.resolveOptions)(options);
this.transport = new transport_1.Transport(this.options.parsedDsn, this.options.apiBaseUrl, this.options.debug);
this.queue = this.options.enableOfflineQueue
? new queue_1.OfflineQueue(this.options.maxOfflineQueueSize, this.options.debug)
: null;
this.breadcrumbs = new breadcrumbs_1.BreadcrumbManager(this.options.maxBreadcrumbs);
this.isInitialized = true;
// Start flush interval for offline queue
if (this.queue) {
this.flushInterval = setInterval(() => this.processQueue(), 30000);
}
if (this.options.debug || exports.enableDebugLogging) {
console.log('[IronTelemetry] Initialized with DSN:', this.options.dsn);
}
}
/**
* Capture an exception
*/
async captureException(error, extra) {
const exception = this.parseException(error);
const event = this.createEvent('error', exception.message, exception);
if (extra) {
event.extra = { ...event.extra, ...extra };
}
return this.sendEvent(event);
}
/**
* Capture a message
*/
async captureMessage(message, level = 'info') {
const event = this.createEvent(level, message);
return this.sendEvent(event);
}
/**
* Log a structured message with title, message, and optional data.
* Useful for structured logging that differentiates the log title from its details.
* @param level The severity level of the log
* @param title A short, descriptive title for the log entry
* @param message Optional detailed message
* @param data Optional additional data to attach to the log
*/
async logMessage(level, title, message, data) {
const fullMessage = message ? `${title}: ${message}` : title;
const event = this.createEvent(level, fullMessage);
if (data) {
event.extra = { ...event.extra, logTitle: title, logData: data };
}
else {
event.extra = { ...event.extra, logTitle: title };
}
return this.sendEvent(event);
}
addBreadcrumb(messageOrBreadcrumb, category, level, data) {
if (typeof messageOrBreadcrumb === 'string') {
this.breadcrumbs.add(messageOrBreadcrumb, category, level, data);
}
else {
this.breadcrumbs.addBreadcrumb(messageOrBreadcrumb);
}
}
/**
* Get a copy of the current breadcrumbs list.
* @returns A read-only array of breadcrumbs
*/
getBreadcrumbs() {
return this.breadcrumbs.getAll();
}
/**
* Clear all breadcrumbs.
*/
clearBreadcrumbs() {
this.breadcrumbs.clear();
}
/**
* Set user context
*/
setUser(id, email, data) {
this.user = { id, email, data };
}
/**
* Clear user context
*/
clearUser() {
this.user = undefined;
}
/**
* Set a tag
*/
setTag(key, value) {
this.tags[key] = value;
}
/**
* Set extra context
*/
setExtra(key, value) {
this.extra[key] = value;
}
/**
* Start a new journey
*/
startJourney(name) {
this.currentJourney = new journey_1.Journey(name);
// Copy user context to journey
if (this.user) {
this.currentJourney.setUser(this.user.id, this.user.email, this.user.data);
}
return new journey_1.JourneyScope(this.currentJourney, () => {
this.currentJourney = undefined;
});
}
/**
* Start a step in the current journey
*/
startStep(name, category) {
if (!this.currentJourney) {
throw new Error('No active journey. Call startJourney() first.');
}
const step = this.currentJourney.startStep(name, category);
return new journey_1.StepScope(step);
}
/**
* Flush pending events
*/
async flush() {
await this.processQueue();
}
/**
* Close the client
*/
close() {
if (this.flushInterval) {
clearInterval(this.flushInterval);
}
}
/**
* Create a telemetry event
*/
createEvent(level, message, exception) {
const event = {
eventId: (0, config_1.generateEventId)(),
timestamp: new Date(),
level,
message,
exception,
user: this.currentJourney?.getUser() ?? this.user,
tags: { ...this.tags },
extra: { ...this.extra },
breadcrumbs: this.breadcrumbs.getAll(),
journey: this.currentJourney?.getContext(),
environment: this.options.environment,
appVersion: this.options.appVersion,
platform: this.getPlatformInfo(),
};
return event;
}
/**
* Send an event
*/
async sendEvent(event) {
// Check sample rate
if (Math.random() > this.options.sampleRate) {
if (this.options.debug || exports.enableDebugLogging) {
console.log('[IronTelemetry] Event dropped due to sample rate');
}
return { success: true, eventId: event.eventId };
}
// Apply beforeSend hook
const beforeSendResult = this.options.beforeSend(event);
if (beforeSendResult === false || beforeSendResult === null) {
if (this.options.debug || exports.enableDebugLogging) {
console.log('[IronTelemetry] Event dropped by beforeSend hook');
}
return { success: true, eventId: event.eventId };
}
const eventToSend = beforeSendResult === true ? event : beforeSendResult;
// Try to send
const result = await this.transport.send(eventToSend);
if (!result.success && this.queue) {
this.queue.enqueue(eventToSend);
return { ...result, queued: true };
}
return result;
}
/**
* Process offline queue
*/
async processQueue() {
if (!this.queue || this.queue.isEmpty) {
return;
}
const isOnline = await this.transport.isOnline();
if (!isOnline) {
return;
}
const events = this.queue.getAll();
for (const event of events) {
const result = await this.transport.send(event);
if (result.success) {
this.queue.remove(event.eventId);
}
}
}
/**
* Parse an error into exception info
*/
parseException(error) {
if (error instanceof Error) {
return {
type: error.name || 'Error',
message: error.message,
stacktrace: this.parseStackTrace(error.stack),
};
}
return {
type: 'Error',
message: String(error),
};
}
/**
* Parse a stack trace string into frames
*/
parseStackTrace(stack) {
if (!stack)
return undefined;
const frames = [];
const lines = stack.split('\n');
for (const line of lines) {
// Chrome/Node format: " at functionName (filename:line:column)"
const chromeMatch = line.match(/^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/);
if (chromeMatch) {
frames.push({
function: chromeMatch[1] || '<anonymous>',
filename: chromeMatch[2],
lineno: parseInt(chromeMatch[3], 10),
colno: parseInt(chromeMatch[4], 10),
});
continue;
}
// Firefox format: "functionName@filename:line:column"
const firefoxMatch = line.match(/^(.*)@(.+?):(\d+):(\d+)$/);
if (firefoxMatch) {
frames.push({
function: firefoxMatch[1] || '<anonymous>',
filename: firefoxMatch[2],
lineno: parseInt(firefoxMatch[3], 10),
colno: parseInt(firefoxMatch[4], 10),
});
}
}
return frames.length > 0 ? frames : undefined;
}
/**
* Get platform information
*/
getPlatformInfo() {
// Check for browser environment
if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
return {
name: 'browser',
userAgent: navigator.userAgent,
};
}
// Check for Node.js environment
if (typeof process !== 'undefined' && process.versions?.node) {
return {
name: 'node',
version: process.versions.node,
os: process.platform,
};
}
return {
name: 'unknown',
};
}
}
exports.TelemetryClient = TelemetryClient;
//# sourceMappingURL=client.js.map