Architecture
This document outlines the architecture of Interactive Logger and explains how it works internally.
Overviewβ
Interactive Logger is built with a modular architecture that separates concerns into distinct components:
- InteractiveLogger: Main entry point and coordinator
- StorageAdapter: Abstraction layer for storage operations
- IndexedDBAdapter: IndexedDB implementation of storage
- LoggerInstance: Individual logger instances
- UIButton: Download button component
Component Architectureβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β InteractiveLogger β
β (Main coordinator, configuration, instance management) β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββ΄βββββββββ¬βββββββββββββββ¬βββββββββββββββ
β β β β
ββββββββΌβββββββ ββββββββΌβββββββ βββββΌβββββββ βββββΌβββββββ
βStorageAdapterβ βLoggerInstanceβ β UIButton β βConsole β
β β β β β β βIntercept β
ββββββββ¬ββββββββ ββββββββββββββββ ββββββββββββ ββββββββββββ
β
ββββββββΌβββββββββββ
βIndexedDBAdapter β
β β
β - Database β
β - Object Store β
β - Indexes β
βββββββββββββββββββ
Storage Architectureβ
IndexedDB Structureβ
Interactive Logger uses IndexedDB for persistent storage with the following structure:
Database Name: __illogger__
Object Store: logs
Key Path: id (auto-incrementing)
Indexes:
timestamp: For efficient querying by timestampname: For efficient querying by logger name
Log Entry Formatβ
Each log entry stored in IndexedDB has the following structure:
{
id?: number; // Auto-incrementing ID (internal)
name: string; // Logger instance name
message: string; // Serialized log message
timestamp?: string; // ISO timestamp (if timestamps enabled)
}
Storage Operationsβ
The storage adapter provides the following operations:
writeLog(name: string, message: string, timestamp?: string): Write a log entrygetLogs(name?: string, limit?: number): Retrieve logs (optionally filtered by name)clear(): Clear all logsgetStats(): Get storage statisticstrimLogs(maxLogs: number): Remove oldest logs when limit is exceeded
Logging Flowβ
Write Flowβ
1. User calls loggerInstance.writeLog(...args)
β
ββ> Serialize arguments (strings, objects, errors)
β
ββ> Check if logging is enabled
β
ββ> Add timestamp (if enabled)
β
ββ> Queue log entry for batch write
β
ββ> If console logging enabled, also log to console
2. Batch processor (every 100ms)
β
ββ> Collect all queued entries
β
ββ> Check if trimming is needed
β
ββ> Write batch to IndexedDB
β
ββ> Clear queue
Read Flowβ
1. User triggers download
β
ββ> Query all logs from IndexedDB
β
ββ> Group by logger name (if multi-file mode)
β
ββ> Format logs with timestamps
β
ββ> Add session separators (if enabled)
β
ββ> Create file(s) or ZIP
β
ββ> Trigger download
Performance Optimizationsβ
Batched Writesβ
Log entries are batched and written every 100ms to reduce IndexedDB transactions. This prevents performance issues when logging at high frequency.
// Logs are queued
logger.writeLog('Message 1');
logger.writeLog('Message 2');
logger.writeLog('Message 3');
// After 100ms, all three are written in a single transaction
Automatic Trimmingβ
When the log limit is reached, the system automatically removes the oldest entries:
- Before writing new logs, check current count
- If count >= maxLogs, calculate how many to remove
- Query oldest entries by timestamp
- Delete oldest entries
- Write new entries
This ensures the storage never exceeds the configured limit.
Proactive Trimmingβ
The system checks if trimming is needed before adding new entries, preventing storage from growing beyond limits.
Session Managementβ
Session Detectionβ
Sessions are detected using the sessionStorage API:
- On initialization, check if a session marker exists
- If not, create a new session marker
- Add a session separator to logs
- If session marker exists, continue with existing session
Session Separatorsβ
Session separators are added to log files to mark when a new browser session/tab was opened:
============================================================
[2024-01-15T10:30:00.000Z] New Session
============================================================
This helps distinguish between different user sessions in log files.
Console Interceptionβ
How It Worksβ
Console interception wraps the native console methods:
// Store original methods
const originalLog = console.log;
const originalError = console.error;
// ... etc
// Wrap with interception
console.log = (...args) => {
originalLog(...args); // Still log to console
logger.writeLog('[LOG]', ...args); // Also log to storage
};
Intercepted Methodsβ
console.logconsole.errorconsole.warnconsole.infoconsole.debugconsole.trace
All intercepted calls are stored with the logger name __console__ and include log level prefixes.
Download Mechanismβ
Single File Modeβ
When singleFile: true:
- Query all logs from IndexedDB
- Sort by timestamp
- Format with logger name prefix:
[logger-name] message - Add session separators at appropriate positions
- Create a single
.logfile - Trigger download using FileSaver.js
Multi-File Mode (ZIP)β
When singleFile: false:
- Query all logs from IndexedDB
- Group by logger name
- For each logger:
- Sort logs by timestamp
- Format with timestamps (if enabled)
- Add session separators
- Create a
.logfile
- Create a ZIP file using JSZip
- Add all log files to ZIP
- Trigger download
UI Button Componentβ
Featuresβ
- Draggable: Can be repositioned anywhere on the page
- Persistent Position: Position saved in localStorage
- Viewport Constraints: Automatically constrained to viewport bounds
- Responsive: Adjusts position on window resize
- Customizable: Text and styling can be customized
Implementationβ
The button is implemented as a floating DOM element with:
- Drag event handlers (mousedown, mousemove, mouseup)
- Touch event handlers for mobile (touchstart, touchmove, touchend)
- Position persistence using localStorage
- Viewport boundary checking
Error Handlingβ
Storage Errorsβ
Storage errors are caught and logged to the console but don't crash the application:
try {
await storageAdapter.writeLog(...);
} catch (error) {
console.error('Failed to write log:', error);
// Application continues to work
}
Graceful Degradationβ
If IndexedDB is unavailable:
- Errors are logged to console
- Application continues to work
- Logging operations are silently ignored
- Download operations return empty files
Thread Safetyβ
All operations are asynchronous and non-blocking:
- Log writes are fire-and-forget
- Storage operations use async/await
- Batch processing runs on a timer
- No blocking operations on the main thread
Browser Compatibilityβ
Interactive Logger requires:
- IndexedDB support (all modern browsers)
- ES2019+ JavaScript features
- File API (for downloads)
Compatible with:
- Chrome/Edge 79+
- Firefox 78+
- Safari 14+
- Opera 66+
Future Considerationsβ
Potential improvements for future versions:
- Web Worker support for heavy operations
- Compression for large log files
- Remote storage backends (optional)
- Real-time log streaming
- Log filtering and search
- Export to different formats (JSON, CSV)