smart-logger-js
v1.0.0
Published
A lightweight, configurable logging utility with colors, timestamps, and context support
Maintainers
Readme
smart-logger-js
A lightweight, zero-dependency, opinionated state-change logger for JavaScript/TypeScript applications. Unlike traditional loggers that output messages, Smart Logger tracks data changes over time with intelligent diffing, debounced output, and visual console summaries.
Why Smart Logger?
The Problem
When debugging complex state changes in applications (React state, Redux stores, API responses), traditional console.log falls short:
// Traditional approach - hard to track what changed
console.log('user data:', userData);
// ... later
console.log('user data:', userData); // What changed? 🤷You end up with walls of JSON in your console, manually comparing objects to find what changed.
The Opinionated Solution
Smart Logger takes a fundamentally different approach:
- Track, Don't Log — Instead of logging snapshots, it tracks a data stream and computes precise diffs
- Identity-Based Array Diffing — Arrays of objects are compared by identity (
id,_id,key, etc.), not position - Debounced Summaries — Multiple rapid changes are batched into readable summaries
- Visual Console Output — Changes are displayed in formatted tables with emojis for quick scanning
import { smartLog } from 'smart-logger-js';
// Just call smartLog with the same ID whenever data changes
smartLog('userData', { name: 'Alice', age: 30 });
// ... later
smartLog('userData', { name: 'Alice', age: 31 });
// Console shows:
// 🚨 [SmartLog] Summary: userData (1 update(s) in last 1s)
// ┌─────────┬──────────┬──────┬────┐
// │ Type │ Path │ From │ To │
// ├─────────┼──────────┼──────┼────┤
// │ ✏️ CHANGE │ age │ 30 │ 31 │
// └─────────┴──────────┴──────┴────┘Installation
pnpm add smart-logger-jsnpm install smart-logger-jsyarn add smart-logger-jsbun add smart-logger-jsQuick Start
Simple API
The easiest way to use Smart Logger:
import { smartLog } from 'smart-logger-js';
// Track any data by giving it an ID
smartLog('myData', { count: 0 });
// Call again with updated data - changes are automatically detected
smartLog('myData', { count: 1 });
smartLog('myData', { count: 2, newField: 'hello' });
// After the debounce period (default 1s), you'll see a summary in the consoleInstance API
For more control, create dedicated logger instances:
import { createSmartLogger } from 'smart-logger-js';
const logger = createSmartLogger({
debounceMs: 500, // Faster summaries
ignoreArrMove: true, // Don't log array reorderings
maxPreviewLength: 100, // Longer value previews
});
logger.log('users', usersArray);API Reference
smartLog(id, data, options?)
The simple, stateless API. Manages logger instances automatically.
| Parameter | Type | Description |
|-----------|------|-------------|
| id | string | Unique identifier for this data stream |
| data | unknown | The data snapshot to track |
| options | SmartLogOptions | Configuration (only used on first call per ID) |
smartLog('users', usersData);
smartLog('settings', settingsData, { debounceMs: 2000 });createSmartLogger(options?)
Creates a new SmartLogger instance with custom configuration.
const logger = createSmartLogger({
debounceMs: 1000,
ignoreArrMove: false,
maxPreviewLength: 50,
arrayIdentifierKeys: ['id', 'uuid'],
});SmartLogger Class
The core logger class with full control.
Methods
| Method | Description |
|--------|-------------|
| log(id, data) | Track a data snapshot |
| getHistory(id) | Get all recorded change batches for a stream |
| clear(id) | Stop tracking and clear history for a stream |
| clearAll() | Stop tracking all streams |
const logger = new SmartLogger();
logger.log('stream1', data);
const history = logger.getHistory('stream1');
logger.clear('stream1');Helper Functions
import { clearSmartLog, clearAllSmartLogs } from 'smart-logger-js';
// Clear a specific stream from the simple API
clearSmartLog('userData');
// Clear all streams
clearAllSmartLogs();Configuration Options
SmartLogOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| debounceMs | number | 1000 | Milliseconds to wait before flushing batched changes to console |
| ignoreArrMove | boolean | false | If true, array MOVE operations won't be logged (useful when order doesn't matter) |
| maxPreviewLength | number | 50 | Maximum characters for string value previews in the table |
| arrayIdentifierKeys | string[] | [] | Custom keys to identify array elements (prioritized over defaults) |
Default Identifier Keys
When comparing arrays of objects, Smart Logger looks for these keys to match elements:
['id', '_id', 'key', 'uuid', 'uid', 'name', 'title']Custom keys are checked first, then defaults.
Change Types
Smart Logger detects and categorizes changes:
| Type | Emoji | Description |
|------|-------|-------------|
| CREATE | 🆕 | New property or value added |
| CHANGE | ✏️ | Existing value modified |
| REMOVE | 🗑️ | Property deleted |
| ADD | ➕ | New array element |
| ARRAY_REMOVE | ➖ | Array element removed |
| MOVE | ↔️ | Array element moved to different index |
Advanced Examples
Tracking React State
import { useEffect } from 'react';
import { smartLog } from 'smart-logger-js';
const MyComponent = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
smartLog('users-state', users);
}, [users]);
// ... component logic
};Tracking API Responses
import { smartLog } from 'smart-logger-js';
const fetchUsers = async () => {
const response = await api.getUsers();
smartLog('api-users', response.data, { debounceMs: 0 }); // Immediate logging
return response;
};Ignoring Array Reordering
When tracking data where order doesn't matter (like a set of selected items):
const logger = createSmartLogger({ ignoreArrMove: true });
logger.log('selectedIds', [1, 2, 3]);
logger.log('selectedIds', [3, 1, 2]); // No changes logged - same items
logger.log('selectedIds', [3, 1, 2, 4]); // Only the ADD is loggedCustom Identifier Keys
For APIs with non-standard ID fields:
const logger = createSmartLogger({
arrayIdentifierKeys: ['productCode', 'sku'], // Checked before defaults
});
logger.log('products', [
{ productCode: 'ABC', price: 100 },
{ productCode: 'XYZ', price: 200 },
]);Accessing Change History
const logger = createSmartLogger();
logger.log('data', initialData);
logger.log('data', updatedData);
const history = logger.getHistory('data');
// history contains all ChangeBatch objects with timestamps and changesConsole Output Format
When changes are flushed, you'll see grouped output:
🚨 [SmartLog] Summary: userData (3 update(s) in last 1s)
▶ Update #1 @ 14:32:15.123
State Before: { name: 'Alice', age: 30 }
Changes: [{ type: 'CHANGE', path: [...], ... }]
State After: { name: 'Alice', age: 31 }
┌──────────┬──────┬──────┬────┐
│ Type │ Path │ From │ To │
├──────────┼──────┼──────┼────┤
│ ✏️ CHANGE │ age │ 30 │ 31 │
└──────────┴──────┴──────┴────┘
▶ Update #2 @ 14:32:15.456
...TypeScript Support
Full TypeScript support with exported types:
import type {
SmartLogOptions,
SmartChange,
Difference,
DifferenceCreate,
DifferenceChange,
DifferenceRemove,
ArrayMovement,
ArrayAddition,
ArrayRemoval,
ChangeBatch,
} from 'smart-logger-js';Philosophy & Design Decisions
Why Debouncing?
In real applications, state often changes rapidly (e.g., typing in a form, dragging elements). Logging every micro-change creates noise. Smart Logger batches changes within a time window, giving you a meaningful summary instead of a flood of individual logs.
Why Identity-Based Array Diffing?
Consider tracking a todo list where items can be reordered:
// Traditional diff would see this as "everything changed"
before: [{ id: 1, text: 'A' }, { id: 2, text: 'B' }]
after: [{ id: 2, text: 'B' }, { id: 1, text: 'A' }]
// Smart Logger sees: "item 1 moved from index 0 to 1, item 2 moved from index 1 to 0"This makes debugging drag-and-drop, sorting, and filtering operations much clearer.
Why Opinionated Defaults?
Smart Logger makes assumptions that work for 90% of use cases:
- Objects in arrays usually have
idor similar identifier fields - You usually want to see changes after a brief pause, not during rapid updates
- Visual console tables are more scannable than raw JSON
If these don't fit your use case, everything is configurable.
Comparison with Other Tools
| Feature | console.log | Redux DevTools | Smart Logger | |---------|-------------|----------------|--------------| | Works anywhere | ✅ | ❌ Redux only | ✅ | | Shows diffs | ❌ | ✅ | ✅ | | Identity-based array diff | ❌ | ❌ | ✅ | | Debounced output | ❌ | ❌ | ✅ | | Zero config | ✅ | ❌ | ✅ | | Visual tables | ❌ | ✅ | ✅ |
License
MIT
