feat:add otel logging
This commit is contained in:
@@ -7,6 +7,8 @@ import type {
|
||||
IGroupPayload as GroupPayload,
|
||||
IIdentifyPayload as IdentifyPayload,
|
||||
IIncrementPayload as IncrementPayload,
|
||||
ILogPayload,
|
||||
ISeverityText,
|
||||
ITrackHandlerPayload as TrackHandlerPayload,
|
||||
ITrackPayload as TrackPayload,
|
||||
} from '@openpanel/validation';
|
||||
@@ -23,6 +25,8 @@ export type {
|
||||
TrackPayload,
|
||||
};
|
||||
|
||||
export type LogProperties = Omit<ILogPayload, 'body' | 'severity'>;
|
||||
|
||||
export interface TrackProperties {
|
||||
[key: string]: unknown;
|
||||
profileId?: string;
|
||||
@@ -48,6 +52,19 @@ export interface OpenPanelOptions {
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
interface LogPayloadForQueue {
|
||||
body: string;
|
||||
severity: ISeverityText;
|
||||
timestamp: string;
|
||||
profileId?: string | number;
|
||||
loggerName?: string;
|
||||
traceId?: string;
|
||||
spanId?: string;
|
||||
traceFlags?: number;
|
||||
attributes?: Record<string, string>;
|
||||
resource?: Record<string, string>;
|
||||
}
|
||||
|
||||
export class OpenPanel {
|
||||
api: Api;
|
||||
options: OpenPanelOptions;
|
||||
@@ -58,6 +75,12 @@ export class OpenPanel {
|
||||
global?: Record<string, unknown>;
|
||||
queue: TrackHandlerPayload[] = [];
|
||||
|
||||
// Log queue for batching
|
||||
private logQueue: LogPayloadForQueue[] = [];
|
||||
private logFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private logFlushIntervalMs = 1000;
|
||||
private logFlushMaxSize = 100;
|
||||
|
||||
constructor(options: OpenPanelOptions) {
|
||||
this.options = options;
|
||||
|
||||
@@ -327,6 +350,67 @@ export class OpenPanel {
|
||||
this.queue = remaining;
|
||||
}
|
||||
|
||||
captureLog(
|
||||
severity: ISeverityText,
|
||||
body: string,
|
||||
properties?: LogProperties,
|
||||
) {
|
||||
if (this.options.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry: LogPayloadForQueue = {
|
||||
body,
|
||||
severity,
|
||||
timestamp: properties?.timestamp ?? new Date().toISOString(),
|
||||
...(this.profileId ? { profileId: this.profileId } : {}),
|
||||
...(properties?.loggerName ? { loggerName: properties.loggerName } : {}),
|
||||
...(properties?.traceId ? { traceId: properties.traceId } : {}),
|
||||
...(properties?.spanId ? { spanId: properties.spanId } : {}),
|
||||
...(properties?.traceFlags !== undefined
|
||||
? { traceFlags: properties.traceFlags }
|
||||
: {}),
|
||||
...(properties?.attributes ? { attributes: properties.attributes } : {}),
|
||||
...(properties?.resource ? { resource: properties.resource } : {}),
|
||||
};
|
||||
|
||||
this.logQueue.push(entry);
|
||||
|
||||
if (this.logQueue.length >= this.logFlushMaxSize) {
|
||||
this.flushLogs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.logFlushTimer) {
|
||||
this.logFlushTimer = setTimeout(() => {
|
||||
this.logFlushTimer = null;
|
||||
this.flushLogs();
|
||||
}, this.logFlushIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private async flushLogs() {
|
||||
if (this.logFlushTimer) {
|
||||
clearTimeout(this.logFlushTimer);
|
||||
this.logFlushTimer = null;
|
||||
}
|
||||
|
||||
if (this.logQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const batch = this.logQueue;
|
||||
this.logQueue = [];
|
||||
|
||||
try {
|
||||
await this.api.fetch('/logs', { logs: batch });
|
||||
} catch (error) {
|
||||
this.log('Failed to flush logs', error);
|
||||
// Re-queue on failure
|
||||
this.logQueue = batch.concat(this.logQueue);
|
||||
}
|
||||
}
|
||||
|
||||
log(...args: any[]) {
|
||||
if (this.options.debug) {
|
||||
console.log('[OpenPanel.dev]', ...args);
|
||||
|
||||
Reference in New Issue
Block a user