1+ import { createHash } from "node:crypto" ;
2+ import { redactLogArgs } from "../utils/logRedact.js" ;
3+
14// ---------------------------------------------------------------------------
25// Types
36// ---------------------------------------------------------------------------
@@ -105,6 +108,18 @@ const retryState = {
105108 nextRetryTime : 0 ,
106109} ;
107110
111+ function logInfo ( ...args : unknown [ ] ) : void {
112+ console . log ( ...redactLogArgs ( args ) ) ;
113+ }
114+
115+ function logWarn ( ...args : unknown [ ] ) : void {
116+ console . warn ( ...redactLogArgs ( args ) ) ;
117+ }
118+
119+ function logError ( ...args : unknown [ ] ) : void {
120+ console . error ( ...redactLogArgs ( args ) ) ;
121+ }
122+
108123// ---------------------------------------------------------------------------
109124// Configuration helpers
110125// ---------------------------------------------------------------------------
@@ -253,7 +268,8 @@ function calculateBackoffDelay(attempt: number, config: HorizonListenerConfig):
253268}
254269
255270function generateEventId ( event : HorizonEvent ) : string {
256- return `${ event . ledger } -${ event . contractId } -${ event . topics . join ( '-' ) } -${ event . data . slice ( 0 , 50 ) } ` ;
271+ const dataHash = createHash ( "sha256" ) . update ( event . data ) . digest ( "hex" ) . slice ( 0 , 16 ) ;
272+ return `${ event . ledger } -${ event . contractId } -${ event . topics . join ( '-' ) } -${ dataHash } ` ;
257273}
258274
259275function isEventProcessed ( eventId : string ) : boolean {
@@ -274,7 +290,7 @@ function markEventProcessed(eventId: string): void {
274290function logMetrics ( config : HorizonListenerConfig ) : void {
275291 if ( ! config . enableMetrics ) return ;
276292
277- console . log ( "[HorizonListener] Metrics:" , {
293+ logInfo ( "[HorizonListener] Metrics:" , {
278294 ...metrics ,
279295 processedEventIdsCount : processedEventIds . size ,
280296 currentLedgerCursor,
@@ -290,7 +306,7 @@ async function dispatchEvent(event: HorizonEvent): Promise<void> {
290306 if ( isEventProcessed ( eventId ) ) {
291307 metrics . eventsDuplicated ++ ;
292308 if ( activeConfig ?. enableMetrics ) {
293- console . log ( "[HorizonListener] Skipping duplicate event:" , eventId ) ;
309+ logInfo ( "[HorizonListener] Skipping duplicate event:" , eventId ) ;
294310 }
295311 return ;
296312 }
@@ -302,7 +318,7 @@ async function dispatchEvent(event: HorizonEvent): Promise<void> {
302318 try {
303319 await handler ( event ) ;
304320 } catch ( err ) {
305- console . error (
321+ logError (
306322 "[HorizonListener] Event handler threw an error:" ,
307323 err ,
308324 ) ;
@@ -420,7 +436,7 @@ async function handleCursorGap(config: HorizonListenerConfig, gapStart: string):
420436 const maxGap = config . maxCursorGap || 100 ;
421437 const startLedger = parseInt ( gapStart ) ;
422438
423- console . log ( `[HorizonListener] Cursor gap detected at ledger ${ gapStart } , attempting recovery` ) ;
439+ logInfo ( `[HorizonListener] Cursor gap detected at ledger ${ gapStart } , attempting recovery` ) ;
424440
425441 // Try to fill the gap by querying individual ledgers
426442 for ( let ledger = startLedger ; ledger < startLedger + maxGap && ledger <= ( currentLedgerCursor || startLedger ) + maxGap ; ledger ++ ) {
@@ -429,13 +445,13 @@ async function handleCursorGap(config: HorizonListenerConfig, gapStart: string):
429445 await new Promise ( resolve => setTimeout ( resolve , 10 ) ) ; // Simulate network delay
430446
431447 if ( Math . random ( ) < 0.1 ) { // 10% chance of finding events in gap
432- console . log ( `[HorizonListener] Recovered events at ledger ${ ledger } ` ) ;
448+ logInfo ( `[HorizonListener] Recovered events at ledger ${ ledger } ` ) ;
433449 metrics . cursorGapsRecovered ++ ;
434450 break ;
435451 }
436452 } catch ( error ) {
437453 // If we can't recover from gap, skip ahead
438- console . warn ( `[HorizonListener] Failed to recover ledger ${ ledger } , skipping` ) ;
454+ logWarn ( `[HorizonListener] Failed to recover ledger ${ ledger } , skipping` ) ;
439455 break ;
440456 }
441457 }
@@ -449,7 +465,7 @@ export async function pollOnce(config: HorizonListenerConfig): Promise<void> {
449465 // Check if we're in a backoff period
450466 if ( retryState . nextRetryTime > Date . now ( ) ) {
451467 if ( config . enableMetrics ) {
452- console . log ( `[HorizonListener] In backoff period, next retry at ${ new Date ( retryState . nextRetryTime ) . toISOString ( ) } ` ) ;
468+ logInfo ( `[HorizonListener] In backoff period, next retry at ${ new Date ( retryState . nextRetryTime ) . toISOString ( ) } ` ) ;
453469 }
454470 return ;
455471 }
@@ -464,7 +480,7 @@ export async function pollOnce(config: HorizonListenerConfig): Promise<void> {
464480 const cursor = currentLedgerCursor ? `${ currentLedgerCursor } ` : config . startLedger ;
465481
466482 if ( config . enableMetrics ) {
467- console . log (
483+ logInfo (
468484 `[HorizonListener] Polling ${ config . horizonUrl } ` +
469485 `(contracts: ${ config . contractIds . length > 0 ? config . contractIds . join ( ", " ) : "none" } , ` +
470486 `cursor: ${ cursor } )` ,
@@ -490,7 +506,7 @@ export async function pollOnce(config: HorizonListenerConfig): Promise<void> {
490506 const rateLimitDelay = config . rateLimitDelayMs || 60000 ;
491507 retryState . nextRetryTime = Date . now ( ) + rateLimitDelay ;
492508
493- console . warn ( `[HorizonListener] Rate limit hit, waiting ${ rateLimitDelay } ms` ) ;
509+ logWarn ( `[HorizonListener] Rate limit hit, waiting ${ rateLimitDelay } ms` ) ;
494510 return ;
495511 }
496512
@@ -510,17 +526,17 @@ export async function pollOnce(config: HorizonListenerConfig): Promise<void> {
510526
511527 metrics . retryAttempts ++ ;
512528
513- console . warn ( `[HorizonListener] Transient error (attempt ${ retryState . attempts } /${ maxRetries } ), retrying in ${ delay } ms:` , classifiedError . message ) ;
529+ logWarn ( `[HorizonListener] Transient error (attempt ${ retryState . attempts } /${ maxRetries } ), retrying in ${ delay } ms:` , classifiedError . message ) ;
514530 return ;
515531 } else {
516- console . error ( `[HorizonListener] Max retries exceeded for transient error:` , classifiedError ) ;
532+ logError ( `[HorizonListener] Max retries exceeded for transient error:` , classifiedError ) ;
517533 retryState . attempts = 0 ; // Reset for next time
518534 return ;
519535 }
520536 }
521537
522538 // Non-transient error - log and continue
523- console . error ( "[HorizonListener] Non-transient error occurred:" , classifiedError ) ;
539+ logError ( "[HorizonListener] Non-transient error occurred:" , classifiedError ) ;
524540 }
525541}
526542
@@ -538,15 +554,15 @@ export function getConfig(): HorizonListenerConfig | null {
538554
539555export async function start ( ) : Promise < void > {
540556 if ( running ) {
541- console . warn ( "[HorizonListener] Already running — ignoring start() call." ) ;
557+ logWarn ( "[HorizonListener] Already running — ignoring start() call." ) ;
542558 return ;
543559 }
544560
545561 const config = resolveConfig ( ) ;
546562 activeConfig = config ;
547563 running = true ;
548564
549- console . log ( "[HorizonListener] Starting with config:" , {
565+ logInfo ( "[HorizonListener] Starting with config:" , {
550566 horizonUrl : config . horizonUrl ,
551567 contractIds : config . contractIds ,
552568 pollIntervalMs : config . pollIntervalMs ,
@@ -559,14 +575,14 @@ export async function start(): Promise<void> {
559575 void pollOnce ( config ) ;
560576 } , config . pollIntervalMs ) ;
561577
562- console . log (
578+ logInfo (
563579 `[HorizonListener] Started. Polling every ${ config . pollIntervalMs } ms.` ,
564580 ) ;
565581}
566582
567583export function stop ( ) : void {
568584 if ( ! running ) {
569- console . warn ( "[HorizonListener] Not running — ignoring stop() call." ) ;
585+ logWarn ( "[HorizonListener] Not running — ignoring stop() call." ) ;
570586 return ;
571587 }
572588
@@ -578,5 +594,5 @@ export function stop(): void {
578594 running = false ;
579595 activeConfig = null ;
580596
581- console . log ( "[HorizonListener] Stopped." ) ;
582- }
597+ logInfo ( "[HorizonListener] Stopped." ) ;
598+ }
0 commit comments