@@ -183,6 +183,19 @@ class HTTPFetcher extends EventEmitter {
183183 return null ;
184184 }
185185
186+ /**
187+ * Returns a shortened version of the URL for log messages.
188+ * @returns {string } Shortened URL
189+ */
190+ #shortenUrl ( ) {
191+ try {
192+ const urlObj = new URL ( this . url ) ;
193+ return `${ urlObj . origin } ${ urlObj . pathname } ${ urlObj . search . length > 50 ? "?..." : urlObj . search } ` ;
194+ } catch {
195+ return this . url ;
196+ }
197+ }
198+
186199 /**
187200 * Determines the retry delay for a non-ok response
188201 * @param {Response } response - The fetch Response object
@@ -198,28 +211,35 @@ class HTTPFetcher extends EventEmitter {
198211 errorType = "AUTH_FAILURE" ;
199212 delay = Math . max ( this . reloadInterval * 5 , THIRTY_MINUTES ) ;
200213 message = `Authentication failed (${ status } ). Check your API key. Waiting ${ Math . round ( delay / 60000 ) } minutes before retry.` ;
201- Log . error ( `${ this . logContext } ${ this . url } - ${ message } ` ) ;
214+ Log . error ( `${ this . logContext } ${ this . #shortenUrl ( ) } - ${ message } ` ) ;
202215 } else if ( status === 429 ) {
203216 errorType = "RATE_LIMITED" ;
204217 const retryAfter = response . headers . get ( "retry-after" ) ;
205218 const parsed = retryAfter ? this . #parseRetryAfter( retryAfter ) : null ;
206219 delay = parsed !== null ? Math . max ( parsed , this . reloadInterval ) : Math . max ( this . reloadInterval * 2 , FIFTEEN_MINUTES ) ;
207220 message = `Rate limited (429). Retrying in ${ Math . round ( delay / 60000 ) } minutes.` ;
208- Log . warn ( `${ this . logContext } ${ this . url } - ${ message } ` ) ;
221+ Log . warn ( `${ this . logContext } ${ this . #shortenUrl ( ) } - ${ message } ` ) ;
209222 } else if ( status >= 500 ) {
210223 errorType = "SERVER_ERROR" ;
211224 this . serverErrorCount = Math . min ( this . serverErrorCount + 1 , this . maxRetries ) ;
212- delay = this . reloadInterval * Math . pow ( 2 , this . serverErrorCount ) ;
213- message = `Server error (${ status } ). Retry #${ this . serverErrorCount } in ${ Math . round ( delay / 60000 ) } minutes.` ;
214- Log . error ( `${ this . logContext } ${ this . url } - ${ message } ` ) ;
225+ if ( this . serverErrorCount >= this . maxRetries ) {
226+ delay = this . reloadInterval ;
227+ message = `Server error (${ status } ). Max retries reached, retrying at configured interval (${ Math . round ( delay / 1000 ) } s).` ;
228+ } else {
229+ delay = HTTPFetcher . calculateBackoffDelay ( this . serverErrorCount , {
230+ maxDelay : this . reloadInterval
231+ } ) ;
232+ message = `Server error (${ status } ). Retry #${ this . serverErrorCount } in ${ Math . round ( delay / 1000 ) } s.` ;
233+ }
234+ Log . error ( `${ this . logContext } ${ this . #shortenUrl( ) } - ${ message } ` ) ;
215235 } else if ( status >= 400 ) {
216236 errorType = "CLIENT_ERROR" ;
217237 delay = Math . max ( this . reloadInterval * 2 , FIFTEEN_MINUTES ) ;
218238 message = `Client error (${ status } ). Retrying in ${ Math . round ( delay / 60000 ) } minutes.` ;
219- Log . error ( `${ this . logContext } ${ this . url } - ${ message } ` ) ;
239+ Log . error ( `${ this . logContext } ${ this . #shortenUrl ( ) } - ${ message } ` ) ;
220240 } else {
221241 message = `Unexpected HTTP status ${ status } .` ;
222- Log . error ( `${ this . logContext } ${ this . url } - ${ message } ` ) ;
242+ Log . error ( `${ this . logContext } ${ this . #shortenUrl ( ) } - ${ message } ` ) ;
223243 }
224244
225245 return {
@@ -293,28 +313,22 @@ class HTTPFetcher extends EventEmitter {
293313 const isTimeout = error . name === "AbortError" ;
294314 const message = isTimeout ? `Request timeout after ${ this . timeout } ms` : `Network error: ${ error . message } ` ;
295315
296- // Apply exponential backoff for network errors
297316 this . networkErrorCount = Math . min ( this . networkErrorCount + 1 , this . maxRetries ) ;
298- const backoffDelay = HTTPFetcher . calculateBackoffDelay ( this . networkErrorCount , {
299- maxDelay : this . reloadInterval
300- } ) ;
301- nextDelay = backoffDelay ;
302-
303- // Truncate URL for cleaner logs
304- let shortUrl = this . url ;
305- try {
306- const urlObj = new URL ( this . url ) ;
307- shortUrl = `${ urlObj . origin } ${ urlObj . pathname } ${ urlObj . search . length > 50 ? "?..." : urlObj . search } ` ;
308- } catch {
309- // If URL parsing fails, use original URL
310- }
317+ const exhausted = this . networkErrorCount >= this . maxRetries ;
311318
312- // Gradual log-level escalation: WARN for first 2 attempts, ERROR after
313- const retryMessage = `Retry #${ this . networkErrorCount } in ${ Math . round ( nextDelay / 1000 ) } s.` ;
314- if ( this . networkErrorCount <= 2 ) {
315- Log . warn ( `${ this . logContext } ${ shortUrl } - ${ message } ${ retryMessage } ` ) ;
319+ if ( exhausted ) {
320+ nextDelay = this . reloadInterval ;
321+ Log . error ( `${ this . logContext } ${ this . #shortenUrl( ) } - ${ message } Max retries reached, retrying at configured interval (${ Math . round ( nextDelay / 1000 ) } s).` ) ;
316322 } else {
317- Log . error ( `${ this . logContext } ${ shortUrl } - ${ message } ${ retryMessage } ` ) ;
323+ nextDelay = HTTPFetcher . calculateBackoffDelay ( this . networkErrorCount , {
324+ maxDelay : this . reloadInterval
325+ } ) ;
326+ const retryMsg = `${ this . logContext } ${ this . #shortenUrl( ) } - ${ message } Retry #${ this . networkErrorCount } in ${ Math . round ( nextDelay / 1000 ) } s.` ;
327+ if ( this . networkErrorCount <= 2 ) {
328+ Log . warn ( retryMsg ) ;
329+ } else {
330+ Log . error ( retryMsg ) ;
331+ }
318332 }
319333
320334 const errorInfo = this . #createErrorInfo(
@@ -324,18 +338,6 @@ class HTTPFetcher extends EventEmitter {
324338 nextDelay ,
325339 error
326340 ) ;
327-
328- /**
329- * Error event - fired when fetch fails
330- * @event HTTPFetcher#error
331- * @type {object }
332- * @property {string } message - Error description
333- * @property {number|null } statusCode - HTTP status or null for network errors
334- * @property {number } retryDelay - Ms until next retry
335- * @property {number } retryCount - Number of consecutive server errors
336- * @property {string } url - The URL that was fetched
337- * @property {Error|null } originalError - The original error
338- */
339341 this . emit ( "error" , errorInfo ) ;
340342 } finally {
341343 clearTimeout ( timeoutId ) ;
0 commit comments