Advanced Features
This guide covers Satori’s advanced features for production use.
Circuit Breaker
The circuit breaker pattern protects your system from cascading failures, particularly useful for watchers that may throw errors.
How It Works
- Closed (normal): All operations pass through
- Open (tripped): Operations fail fast without executing
- Half-Open (testing): Limited operations to test recovery
const satori = createSatori({
circuitBreaker: {
enabled: true,
failureThreshold: 5, // Open after 5 failures
resetTimeout: 30000, // Try again after 30 seconds
successThreshold: 3 // Close after 3 successes
}
});
Monitoring Circuit State
const metrics = satori.getMetrics();
console.log(`Circuit state: ${metrics.circuitBreakerState}`);
// "closed" | "open" | "half-open"
Direct Usage
import { CircuitBreaker } from '@nisoku/satori-log';
const cb = new CircuitBreaker({
enabled: true,
failureThreshold: 5,
resetTimeout: 30000,
successThreshold: 3
}, {
onOpen: () => console.log('Circuit opened'),
onClose: () => console.log('Circuit closed'),
onHalfOpen: () => console.log('Circuit half-open')
});
// Execute with circuit breaker protection
try {
await cb.execute(async () => {
return await riskyOperation();
});
} catch (error) {
// Handle error or circuit open
}
Self-Monitoring Metrics
Track Satori’s internal performance:
const satori = createSatori({ enableMetrics: true });
// Get current metrics
const metrics = satori.getMetrics();
console.log({
uptime: metrics.uptime,
eventsPublished: metrics.bus.totalPublished,
eventsDropped: metrics.bus.totalDropped,
eventsDeduplicated: metrics.bus.totalDeduplicated,
bufferSize: metrics.bus.bufferSize,
subscriberCount: metrics.bus.subscriberCount
});
Periodic Metrics Logging
setInterval(() => {
const m = satori.getMetrics();
logger.info('Satori metrics', {
tags: ['metrics', 'self-monitor'],
state: m
});
}, 60000); // Every minute
Persistence Adapters
Satori provides several persistence adapters for storing log events. Pass an adapter instance to the configuration.
Memory Adapter
In-memory storage (non-persistent, useful for testing):
import { createSatori, MemoryAdapter } from '@nisoku/satori-log';
const satori = createSatori({
persistence: {
enabled: true,
adapter: new MemoryAdapter(10000) // maxSize
}
});
LocalStorage Adapter
Browser localStorage persistence:
import { createSatori, LocalStorageAdapter } from '@nisoku/satori-log';
const satori = createSatori({
persistence: {
enabled: true,
adapter: new LocalStorageAdapter('satori-events', 5000) // key, maxSize
}
});
IndexedDB Adapter
Browser IndexedDB for larger datasets:
import { createSatori, IndexedDBAdapter } from '@nisoku/satori-log';
const satori = createSatori({
persistence: {
enabled: true,
adapter: new IndexedDBAdapter('satori-db', 100000) // dbName, maxSize
}
});
Console Adapter
Outputs events to console (useful for debugging):
import { createSatori, ConsoleAdapter } from '@nisoku/satori-log';
const satori = createSatori({
persistence: {
enabled: true,
adapter: new ConsoleAdapter()
}
});
State Selectors
Create typed, reusable state selectors for watching:
import { createStateSelector } from '@nisoku/satori-log';
interface AppState {
users: {
current: { name: string; role: string } | null;
list: User[];
};
settings: {
theme: 'light' | 'dark';
};
}
// Define typed selectors
const currentUser = createStateSelector<AppState>(
(state) => state.users.current
);
const theme = createStateSelector<AppState>(
(state) => state.settings.theme
);
// Use with watch
const appState: AppState = { /* ... */ };
logger.watch(() => currentUser(appState), 'currentUser');
logger.watch(() => theme(appState), 'theme');
Middleware
Add custom middleware to the event pipeline using bus.use():
// Add middleware via the event bus
satori.bus.use((event, next) => {
// Modify or filter events before they reach subscribers
if (event.tags?.includes('internal')) {
return; // Don't pass to next middleware (drops event)
}
next(); // Continue to next middleware/subscribers
});
Replay Buffer
The event bus maintains a replay buffer for debugging. Access it via the bus:
// Get the replay buffer from the bus
const buffer = satori.bus.getReplayBuffer();
// Replay events
for (const event of buffer) {
console.log('Replaying:', event);
}
Deep Equality Utilities
Satori exports its deep equality utilities:
import { deepEqual, deepClone, computeHash } from '@nisoku/satori-log';
// Deep comparison (handles NaN, circular refs, Map, Set)
deepEqual({ a: 1 }, { a: 1 }); // true
deepEqual(NaN, NaN); // true
deepEqual(new Map([['a', 1]]), new Map([['a', 1]])); // true
// Deep clone (safe with circular refs)
const clone = deepClone(complexObject);
// Compute hash for deduplication
const hash = computeHash({ msg: 'test' });
Next Steps
- API Reference: Complete API documentation
- Examples: Real-world patterns