diff --git a/lib/Logger.js b/lib/Logger.js index 00141ae..2627e46 100644 --- a/lib/Logger.js +++ b/lib/Logger.js @@ -3,6 +3,7 @@ const HexUtil = require('./HexUtil'); class Logger { constructor() { this.debugEnabled = false; + this.prefix = ''; } debug(...args) { @@ -14,6 +15,9 @@ class Logger { try { const strings = this._convertArgsToStrings(...args); if (strings.length) { + if (this.prefix) { + strings.unshift(this.prefix); + } console.log(...strings); } } catch(e) { diff --git a/lib/QueryRunner.js b/lib/QueryRunner.js index f4e152c..54d9a4c 100644 --- a/lib/QueryRunner.js +++ b/lib/QueryRunner.js @@ -70,37 +70,34 @@ class QueryRunner { throw new Error("Could not determine port to query. Did you provide a port or gameid?"); } - if (attempts.length === 1) { - return await this._attempt(attempts[0]); - } else { - const errors = []; - for (const attempt of attempts) { + const numRetries = userOptions.maxAttempts || gameOptions.maxAttempts || defaultOptions.maxAttempts; + + let attemptNum = 0; + const errors = []; + for (const attempt of attempts) { + for (let retry = 0; retry < numRetries; retry++) { + attemptNum++; try { return await this._attempt(attempt); - } catch(e) { - const e2 = new Error('Failed to query port ' + attempt.port); - e2.stack += "\nCaused by:\n" + e.stack; - errors.push(e2); + } catch (e) { + e.stack = 'Attempt #' + attemptNum + ' - Port=' + attempt.port + ' Retry=' + (retry) + ':\n' + e.stack; + errors.push(e); } } - - const err = new Error('Failed all port attempts'); - err.stack = errors.map(e => e.stack).join('\n'); - throw err; } + + const err = new Error('Failed all ' + errors.length + ' attempts'); + for (const e of errors) { + err.stack += '\n' + e.stack; + } + throw err; } async _attempt(options) { - if (options.debug) { - const logger = new Logger(); - logger.debugEnabled = true; - logger.debug("Running attempt with options:"); - logger.debug(options); - } const core = this.protocolResolver.create(options.protocol); core.options = options; core.udpSocket = this.udpSocket; - return await core.runAllAttempts(); + return await core.runOnceSafe(); } } diff --git a/protocols/core.js b/protocols/core.js index 92a3b05..2dc536e 100644 --- a/protocols/core.js +++ b/protocols/core.js @@ -7,6 +7,8 @@ const EventEmitter = require('events').EventEmitter, Logger = require('../lib/Logger'), DnsResolver = require('../lib/DnsResolver'); +let uid = 0; + class Core extends EventEmitter { constructor() { super(); @@ -26,31 +28,15 @@ class Core extends EventEmitter { this.usedTcp = false; } - async runAllAttempts() { + // Runs a single attempt with a timeout and cleans up afterward + async runOnceSafe() { if (this.options.debug) { this.logger.debugEnabled = true; } + this.logger.prefix = 'Q#' + (uid++); - let result = null; - let lastError = null; - for (let attempt = 1; attempt <= this.options.maxAttempts; attempt++) { - try { - result = await this.runOnceSafe(); - result.query.attempts = attempt; - break; - } catch (e) { - lastError = e; - } - } + this.logger.debug("Query is running with options:", this.options); - if (result === null) { - throw lastError; - } - return result; - } - - // Runs a single attempt with a timeout and cleans up afterward - async runOnceSafe() { let abortCall = null; this.abortedPromise = new Promise((resolve,reject) => { abortCall = () => reject("Query is finished -- cancelling outstanding promises"); @@ -63,13 +49,18 @@ class Core extends EventEmitter { try { const promise = this.runOnce(); timeout = Promises.createTimeout(this.options.attemptTimeout, "Attempt"); - return await Promise.race([promise,timeout]); + const result = await Promise.race([promise, timeout]); + this.logger.debug("Query was successful"); + return result; + } catch(e) { + this.logger.debug("Query failed with error", e); + throw e; } finally { timeout && timeout.cancel(); try { abortCall(); } catch(e) { - this.debugLog("Error during abort cleanup: " + e.stack); + this.logger.debug("Error during abort cleanup: " + e.stack); } } }