feat:add otel logging

This commit is contained in:
2026-03-31 16:45:05 +02:00
parent fcb4cf5fb0
commit 655ea1f87e
23 changed files with 1334 additions and 1 deletions

View File

@@ -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);