refactor: improve libsignal error logging and deduplication

Implements 4 key improvements to reduce log noise and duplicates:

1.  Remove console.log interceptor
   - Libsignal only uses console.error for errors
   - Eliminates duplicate interception (was logging 2x per error)
   - Reduces code by 50+ lines

2.  Increase deduplication window (100ms → 150ms)
   - Better coverage for rapid sequential errors
   - Configurable via DEDUP_WINDOW_MS constant

3.  Type + JID tracking for smart deduplication
   - Uses Map<string, number> instead of single variable
   - Deduplication key: "errorType:maskedJID"
   - Prevents false positives (different error types now tracked separately)
   - Memory-safe: auto-cleanup keeps last 50 entries max

4.  Concise session recreation summary
   - Before: Verbose multi-line debug logs
   - After: Single line "🔄 Session Reset | JID: 4680****_1.0 | Cleared: 6 devices"
   - Shows masked JID, deleted count, and next action
   - cleanupCorruptedSession() now returns deletion count

Expected log improvement:
- Before: 3-9 duplicate logs per error burst
- After: 1 log per unique error type per contact

https://claude.ai/code/session_01TvSrN9JmHZDdKMZDFWEcUS
This commit is contained in:
Claude
2026-02-12 20:36:45 +00:00
parent 6c6f49d93f
commit 1ef4095fee
2 changed files with 33 additions and 70 deletions
+22 -61
View File
@@ -5,62 +5,9 @@
const _origConsoleLog = console.log
const _origConsoleError = console.error
// Track last error to avoid duplicates
let _lastErrorMsg = ''
let _lastErrorTime = 0
console.log = function(...args: unknown[]) {
if (args.length > 0 && typeof args[0] === 'string') {
const msg = args[0]
const stack = new Error().stack || ''
const isFromLibsignal = stack.includes('libsignal') || stack.includes('session_cipher')
if (isFromLibsignal) {
// Suppress session lifecycle dumps
if (msg.startsWith('Closing session')) {
return
}
// Format session errors cleanly
if (
msg.includes('Session error') ||
msg.includes('Bad MAC') ||
msg.includes('MessageCounterError') ||
msg.includes('Key used already') ||
msg.includes('Failed to decrypt')
) {
// Extract error type
let errorType = '⚠️ Session Error'
if (msg.includes('Bad MAC')) errorType = '🔐 Bad MAC Error'
else if (msg.includes('MessageCounterError') || msg.includes('Key used already')) errorType = '🔢 Counter Error'
else if (msg.includes('Failed to decrypt')) errorType = '🔌 Decryption Failed'
// Extract JID if present (format: 46802258641027_1.0 or similar)
const jidMatch = msg.match(/(\d{10,}(?:_\d+\.\d+)?)/);
const jid = jidMatch ? jidMatch[1] : null
const maskedJid = jid && jid.length > 8 ? `${jid.substring(0, 4)}****${jid.substring(jid.length - 4)}` : jid
// Format clean message
const cleanMsg = maskedJid
? `${errorType} | JID: ${maskedJid}`
: errorType
// Avoid duplicate logs (within 100ms)
const now = Date.now()
if (cleanMsg === _lastErrorMsg && now - _lastErrorTime < 100) {
return
}
_lastErrorMsg = cleanMsg
_lastErrorTime = now
_origConsoleError(cleanMsg)
return
}
}
}
_origConsoleLog.apply(console, args)
}
// Track errors by type + JID to avoid duplicates (using Map for better performance)
const _errorTimestamps = new Map<string, number>()
const DEDUP_WINDOW_MS = 150
console.error = function(...args: unknown[]) {
if (args.length > 0 && typeof args[0] === 'string') {
@@ -69,6 +16,11 @@ console.error = function(...args: unknown[]) {
const isFromLibsignal = stack.includes('libsignal') || stack.includes('session_cipher')
if (isFromLibsignal) {
// Suppress session lifecycle logs
if (msg.startsWith('Closing session')) {
return
}
// Format session errors cleanly
if (
msg.includes('Session error') ||
@@ -93,13 +45,22 @@ console.error = function(...args: unknown[]) {
? `${errorType} | JID: ${maskedJid}`
: errorType
// Avoid duplicate logs (within 100ms)
// Deduplication key: type + JID (prevents duplicate logs for same error type on same contact)
const dedupeKey = `${errorType}:${maskedJid || 'unknown'}`
const now = Date.now()
if (cleanMsg === _lastErrorMsg && now - _lastErrorTime < 100) {
return
const lastTime = _errorTimestamps.get(dedupeKey)
if (lastTime && now - lastTime < DEDUP_WINDOW_MS) {
return // Skip duplicate within 150ms window
}
_errorTimestamps.set(dedupeKey, now)
// Cleanup old entries (keep only last 50 to prevent memory leak)
if (_errorTimestamps.size > 50) {
const oldestKey = _errorTimestamps.keys().next().value
if (oldestKey) _errorTimestamps.delete(oldestKey)
}
_lastErrorMsg = cleanMsg
_lastErrorTime = now
_origConsoleError(cleanMsg)
return