Last active
October 10, 2024 09:03
-
-
Save Akiyamka/db722e918e74bd7cb5e1fa3fd32170fc to your computer and use it in GitHub Desktop.
This logger sends messages in batches using beacon API.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const logger = new Logger('https://api.example.com/logs'); | |
logger.log('Log message'); | |
logger.info('Info message'); | |
logger.warn('Warn message'); | |
logger.error('Error message'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
import { Logger } from './logger'; // Предполагаем, что Logger экспортирован из файла logger.ts | |
describe('Logger', () => { | |
let logger: Logger; | |
let mockBeacon: ReturnType<typeof vi.fn>; | |
beforeEach(() => { | |
vi.useFakeTimers(); | |
mockBeacon = vi.fn().mockReturnValue(true); | |
Object.defineProperty(navigator, 'sendBeacon', { | |
value: mockBeacon, | |
configurable: true, | |
}); | |
logger = new Logger('https://api.example.com/logs', 3, 5000); | |
}); | |
afterEach(() => { | |
vi.useRealTimers(); | |
vi.restoreAllMocks(); | |
}); | |
it('should create log entries correctly', () => { | |
const spy = vi.spyOn(logger as any, 'addToQueue'); | |
logger.log('Test log'); | |
expect(spy).toHaveBeenCalledWith(expect.objectContaining({ | |
level: 'log', | |
message: 'Test log', | |
})); | |
}); | |
it('should flush logs when batch size is reached', () => { | |
logger.log('Log 1'); | |
logger.log('Log 2'); | |
logger.log('Log 3'); | |
expect(mockBeacon).toHaveBeenCalledTimes(1); | |
expect(mockBeacon).toHaveBeenCalledWith( | |
'https://api.example.com/logs', | |
expect.any(Blob) | |
); | |
}); | |
it('should flush logs after max wait time', () => { | |
logger.log('Log 1'); | |
logger.log('Log 2'); | |
vi.advanceTimersByTime(5000); | |
expect(mockBeacon).toHaveBeenCalledTimes(1); | |
}); | |
it('should flush remaining logs on beforeunload', () => { | |
logger.log('Log 1'); | |
logger.log('Log 2'); | |
window.dispatchEvent(new Event('beforeunload')); | |
expect(mockBeacon).toHaveBeenCalledTimes(1); | |
}); | |
it('should not flush if queue is empty', () => { | |
(logger as any).flush(); | |
expect(mockBeacon).not.toHaveBeenCalled(); | |
}); | |
it('should log to console as well', () => { | |
const consoleSpy = vi.spyOn(console, 'log'); | |
logger.log('Console log'); | |
expect(consoleSpy).toHaveBeenCalledWith('Console log'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface LogEntry { | |
level: 'log' | 'info' | 'warn' | 'error'; | |
message: string; | |
timestamp: number; | |
} | |
class Logger { | |
private logQueue: LogEntry[] = []; | |
private readonly batchSize: number; | |
private readonly maxWaitTime: number; | |
private timer: ReturnType<typeof setTimeout> | null = null; | |
private readonly endpoint: string; | |
constructor(endpoint: string, batchSize: number = 10, maxWaitTime: number = 5000) { | |
this.endpoint = endpoint; | |
this.batchSize = batchSize; | |
this.maxWaitTime = maxWaitTime; | |
window.addEventListener('beforeunload', this.flush.bind(this)); | |
} | |
private createLogEntry(level: LogEntry['level'], message: string): LogEntry { | |
return { | |
level, | |
message, | |
timestamp: Date.now(), | |
}; | |
} | |
private addToQueue(entry: LogEntry): void { | |
this.logQueue.push(entry); | |
if (this.logQueue.length >= this.batchSize) { | |
this.flush(); | |
} else if (!this.timer) { | |
this.timer = setTimeout(() => this.flush(), this.maxWaitTime); | |
} | |
} | |
private flush(): void { | |
if (this.logQueue.length === 0) return; | |
if (this.timer) { | |
clearTimeout(this.timer); | |
this.timer = null; | |
} | |
const logsToSend = this.logQueue.splice(0, this.batchSize); | |
const blob = new Blob([JSON.stringify(logsToSend)], { type: 'application/json' }); | |
navigator.sendBeacon(this.endpoint, blob); | |
} | |
log(message: string): void { | |
this.addToQueue(this.createLogEntry('log', message)); | |
console.log(message); | |
} | |
info(message: string): void { | |
this.addToQueue(this.createLogEntry('info', message)); | |
console.info(message); | |
} | |
warn(message: string): void { | |
this.addToQueue(this.createLogEntry('warn', message)); | |
console.warn(message); | |
} | |
error(message: string): void { | |
this.addToQueue(this.createLogEntry('error', message)); | |
console.error(message); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment