feat: 增加debug参数

This commit is contained in:
weidingjian 2026-06-25 00:35:56 +08:00
parent fc95159945
commit e5844bbfe9
11 changed files with 132 additions and 19 deletions

6
dist/index.cjs.js vendored

File diff suppressed because one or more lines are too long

6
dist/index.esm.js vendored

File diff suppressed because one or more lines are too long

6
dist/index.iife.js vendored

File diff suppressed because one or more lines are too long

View File

@ -132,6 +132,7 @@ export interface LightConfig {
release?: string;
environment?: string;
enabled?: boolean;
debug?: boolean;
sampleRate?: number;
maxQueueSize?: number;
flushInterval?: number;

4
dist/utils/logger.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export declare function setDebug(enabled: boolean): void;
export declare function isDebugEnabled(): boolean;
export declare function logDebug(tag: string, message: string, data?: unknown): void;
export declare function logError(tag: string, message: string, error?: unknown): void;

View File

@ -3,6 +3,7 @@ import { parseDSN } from '../utils/dsn';
import { now } from '../utils/helper';
import { computeFingerprint } from '../utils/hash';
import { parseStackTrace } from '../utils/stacktrace';
import { logDebug, setDebug } from '../utils/logger';
import { ConfigManager } from './ConfigManager';
import { EventBus } from './EventBus';
import { EventQueue } from './EventQueue';
@ -33,6 +34,9 @@ class Client implements LightClient {
throw new Error('DSN is required');
}
setDebug(config.debug === true);
logDebug('Client', 'Initializing SDK', { dsn: config.dsn, debug: config.debug, enabled: config.enabled });
this.dsn = parseDSN(config.dsn);
this.configManager = new ConfigManager(config);
this.eventBus = new EventBus();
@ -51,6 +55,7 @@ class Client implements LightClient {
);
this.enabled = this.configManager.get('enabled') !== false;
logDebug('Client', 'SDK initialized', { enabled: this.enabled });
}
init(): void {
@ -60,16 +65,23 @@ class Client implements LightClient {
}
private async flushEvents(events: SentryEvent[]): Promise<void> {
logDebug('Client', 'flushEvents start', { eventCount: events.length });
const processedEvents: SentryEvent[] = [];
for (const event of events) {
const processed = this.pluginManager.applyBeforeReport(event);
if (processed) {
processedEvents.push(processed);
} else {
logDebug('Client', 'Event filtered by beforeReport plugin', { type: event.type, message: 'message' in event ? event.message : undefined });
}
}
if (processedEvents.length === 0) return;
if (processedEvents.length === 0) {
logDebug('Client', 'flushEvents: all events filtered by beforeReport');
return;
}
let isFirstInBatch = true;
const finalEvents: SentryEvent[] = [];
@ -80,14 +92,22 @@ class Client implements LightClient {
const beforeSend = this.configManager.get('beforeSend');
const afterBeforeSend = beforeSend ? beforeSend(withConfig) : withConfig;
if (!afterBeforeSend) continue;
if (!afterBeforeSend) {
logDebug('Client', 'Event filtered by beforeSend', { type: event.type });
continue;
}
finalEvents.push(afterBeforeSend);
}
if (finalEvents.length === 0) return;
if (finalEvents.length === 0) {
logDebug('Client', 'flushEvents: all events filtered by beforeSend');
return;
}
logDebug('Client', 'Reporting events', { count: finalEvents.length });
await this.reporter.report(finalEvents);
logDebug('Client', 'Report completed successfully');
this.configManager.markContextReported();
for (const event of finalEvents) {
@ -140,12 +160,23 @@ class Client implements LightClient {
}
captureException(error: Error | unknown): void {
if (!this.enabled) return;
logDebug('Client', 'captureException called', { error: error instanceof Error ? error.message : String(error) });
if (!this.enabled) {
logDebug('Client', 'captureException skipped: SDK disabled');
return;
}
const errorEvent = this.buildErrorEvent(error);
if (this.configManager.isIgnoredError(errorEvent.message)) return;
logDebug('Client', 'Error event built', { type: errorEvent.type, message: errorEvent.message, fingerprint: (errorEvent as ErrorEvent).fingerprint });
if (this.configManager.isIgnoredError(errorEvent.message)) {
logDebug('Client', 'captureException skipped: error ignored by ignoreErrors');
return;
}
errorEvent.breadcrumbs = [...this.breadcrumbs];
logDebug('Client', 'Enqueue error event', { breadcrumbs: this.breadcrumbs.length });
this.queue.enqueue(errorEvent);
}

View File

@ -1,6 +1,7 @@
import type { SentryEvent, ErrorEvent, CountEvent } from '../types';
import { EventBus } from './EventBus';
import { now } from '../utils/helper';
import { logDebug } from '../utils/logger';
interface DedupeEntry {
count: number;
@ -124,7 +125,12 @@ export class EventQueue {
}
enqueue(event: SentryEvent): void {
if (this.paused) return;
logDebug('EventQueue', 'enqueue called', { type: event.type, level: event.level, message: 'message' in event ? (event as { message: string }).message : undefined });
if (this.paused) {
logDebug('EventQueue', 'enqueue skipped: queue paused (infinite loop detected)');
return;
}
this.cleanupExpiredDedupeEntries();
@ -132,6 +138,7 @@ export class EventQueue {
if (fingerprint) {
if (this.checkInfiniteLoop(fingerprint)) {
logDebug('EventQueue', 'enqueue skipped: infinite loop detected', { fingerprint });
return;
}
@ -148,23 +155,29 @@ export class EventQueue {
pending.count++;
pending.ts_end = currentTime;
}
logDebug('EventQueue', 'enqueue skipped: deduped (count >= 3)', { fingerprint, count: existing.count });
return;
}
existing.count++;
existing.lastTime = currentTime;
logDebug('EventQueue', 'Dedup count incremented', { fingerprint, count: existing.count });
} else {
this.dedupeMap.set(fingerprint, { count: 1, lastTime: currentTime, firstReported: true });
this.pendingCounts.delete(fingerprint);
logDebug('EventQueue', 'Dedup entry reset (expired)', { fingerprint });
}
} else {
this.dedupeMap.set(fingerprint, { count: 1, lastTime: currentTime, firstReported: false });
logDebug('EventQueue', 'New dedup entry created', { fingerprint });
}
}
this.queue.push(event);
logDebug('EventQueue', 'Event added to queue', { queueSize: this.queue.length });
this.eventBus.emit('event', event);
if (this.queue.length >= this.maxSize) {
logDebug('EventQueue', 'Queue reached maxSize, triggering flush', { maxSize: this.maxSize });
this.flush();
}
}
@ -211,8 +224,16 @@ export class EventQueue {
}
async flush(): Promise<void> {
if (this.queue.length === 0 && this.pendingCounts.size === 0) return;
if (this.isFlushing) return;
logDebug('EventQueue', 'flush called', { queueSize: this.queue.length, pendingCounts: this.pendingCounts.size, isFlushing: this.isFlushing });
if (this.queue.length === 0 && this.pendingCounts.size === 0) {
logDebug('EventQueue', 'flush skipped: queue is empty');
return;
}
if (this.isFlushing) {
logDebug('EventQueue', 'flush skipped: already flushing');
return;
}
this.isFlushing = true;
@ -220,14 +241,18 @@ export class EventQueue {
const countEvents = this.buildCountEvents();
const allEvents = [...events, ...countEvents];
logDebug('EventQueue', 'flush events to callback', { total: allEvents.length, events: events.length, countEvents: countEvents.length });
this.lastFlushTime = now();
this.eventBus.emit('report', allEvents);
try {
await this.flushCallback(allEvents);
this.pendingCounts.clear();
logDebug('EventQueue', 'flush completed successfully');
this.eventBus.emit('reported', allEvents);
} catch (e) {
logDebug('EventQueue', 'flush failed, returning events to queue', { error: e instanceof Error ? e.message : String(e) });
events.forEach(evt => this.queue.unshift(evt));
throw e;
} finally {

View File

@ -2,6 +2,7 @@ import type { DSNInfo, SentryEvent, ErrorEvent } from '../types';
import { getEnvelopeUrl } from '../utils/dsn';
import { now, generateEventId } from '../utils/helper';
import { encodeFields, FIELD_ENCODE_MAP } from '../utils/field-encoder';
import { logDebug, logError } from '../utils/logger';
export class Reporter {
private dsn: DSNInfo;
@ -16,25 +17,37 @@ export class Reporter {
}
async report(events: SentryEvent[]): Promise<void> {
if (events.length === 0) return;
logDebug('Reporter', 'report called', { eventCount: events.length });
if (events.length === 0) {
logDebug('Reporter', 'report skipped: empty events');
return;
}
const envelope = this.buildEnvelope(events);
const url = getEnvelopeUrl(this.dsn);
logDebug('Reporter', 'Envelope built', { envelopeSize: envelope.length, url });
let retries = 0;
while (retries <= this.maxRetries) {
try {
logDebug('Reporter', `Sending request (attempt ${retries + 1}/${this.maxRetries + 1})`);
await this.send(envelope, false);
logDebug('Reporter', 'Request succeeded');
return;
} catch (e) {
const status = (e as { status?: number }).status;
logError('Reporter', `Request failed (attempt ${retries + 1})`, { status, error: e });
if (status && status >= 400 && status < 500) {
throw e;
}
retries++;
if (retries > this.maxRetries) {
logError('Reporter', 'All retries exhausted');
throw e;
}
const backoff = Math.min(this.retryDelay * Math.pow(2, retries - 1), 5000);
logDebug('Reporter', `Retrying in ${backoff}ms`);
await this.delay(backoff);
}
}

View File

@ -1,5 +1,6 @@
import type { LightPlugin, LightClient, SentryEvent, LightConfig } from '../types';
import { isObject } from '../utils/helper';
import { logDebug } from '../utils/logger';
interface SampleRateConfig {
error?: number;
@ -73,7 +74,13 @@ class SamplingPlugin implements LightPlugin {
}
beforeReport(event: SentryEvent): SentryEvent | null {
if (!this.shouldSample(event)) {
const sampled = this.shouldSample(event);
if (!sampled) {
logDebug('SamplingPlugin', 'Event dropped by sampling', {
type: event.type,
globalRate: (this.client.config as LightConfig).sampleRate ?? 1,
typeRate: this.getTypeRate(event),
});
return null;
}
return event;

View File

@ -153,6 +153,7 @@ export interface LightConfig {
release?: string;
environment?: string;
enabled?: boolean;
debug?: boolean;
sampleRate?: number;
maxQueueSize?: number;
flushInterval?: number;

31
src/utils/logger.ts Normal file
View File

@ -0,0 +1,31 @@
let debugEnabled = false;
export function setDebug(enabled: boolean): void {
debugEnabled = enabled;
}
export function isDebugEnabled(): boolean {
return debugEnabled;
}
export function logDebug(tag: string, message: string, data?: unknown): void {
if (!debugEnabled) return;
const timestamp = new Date().toISOString();
const prefix = `[LightSDK][${tag}]`;
if (data !== undefined) {
console.log(`${prefix} ${timestamp} ${message}`, data);
} else {
console.log(`${prefix} ${timestamp} ${message}`);
}
}
export function logError(tag: string, message: string, error?: unknown): void {
if (!debugEnabled) return;
const timestamp = new Date().toISOString();
const prefix = `[LightSDK][${tag}][ERROR]`;
if (error !== undefined) {
console.error(`${prefix} ${timestamp} ${message}`, error);
} else {
console.error(`${prefix} ${timestamp} ${message}`);
}
}