Improve multi-attempt logging

This commit is contained in:
mmorrison 2019-02-13 22:46:13 -06:00
parent ab6e9064d2
commit dd5dce21db
3 changed files with 34 additions and 42 deletions

View file

@ -3,6 +3,7 @@ const HexUtil = require('./HexUtil');
class Logger { class Logger {
constructor() { constructor() {
this.debugEnabled = false; this.debugEnabled = false;
this.prefix = '';
} }
debug(...args) { debug(...args) {
@ -14,6 +15,9 @@ class Logger {
try { try {
const strings = this._convertArgsToStrings(...args); const strings = this._convertArgsToStrings(...args);
if (strings.length) { if (strings.length) {
if (this.prefix) {
strings.unshift(this.prefix);
}
console.log(...strings); console.log(...strings);
} }
} catch(e) { } catch(e) {

View file

@ -70,37 +70,34 @@ class QueryRunner {
throw new Error("Could not determine port to query. Did you provide a port or gameid?"); throw new Error("Could not determine port to query. Did you provide a port or gameid?");
} }
if (attempts.length === 1) { const numRetries = userOptions.maxAttempts || gameOptions.maxAttempts || defaultOptions.maxAttempts;
return await this._attempt(attempts[0]);
} else { let attemptNum = 0;
const errors = []; const errors = [];
for (const attempt of attempts) { for (const attempt of attempts) {
for (let retry = 0; retry < numRetries; retry++) {
attemptNum++;
try { try {
return await this._attempt(attempt); return await this._attempt(attempt);
} catch(e) { } catch (e) {
const e2 = new Error('Failed to query port ' + attempt.port); e.stack = 'Attempt #' + attemptNum + ' - Port=' + attempt.port + ' Retry=' + (retry) + ':\n' + e.stack;
e2.stack += "\nCaused by:\n" + e.stack; errors.push(e);
errors.push(e2);
} }
} }
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) { 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); const core = this.protocolResolver.create(options.protocol);
core.options = options; core.options = options;
core.udpSocket = this.udpSocket; core.udpSocket = this.udpSocket;
return await core.runAllAttempts(); return await core.runOnceSafe();
} }
} }

View file

@ -7,6 +7,8 @@ const EventEmitter = require('events').EventEmitter,
Logger = require('../lib/Logger'), Logger = require('../lib/Logger'),
DnsResolver = require('../lib/DnsResolver'); DnsResolver = require('../lib/DnsResolver');
let uid = 0;
class Core extends EventEmitter { class Core extends EventEmitter {
constructor() { constructor() {
super(); super();
@ -26,31 +28,15 @@ class Core extends EventEmitter {
this.usedTcp = false; this.usedTcp = false;
} }
async runAllAttempts() { // Runs a single attempt with a timeout and cleans up afterward
async runOnceSafe() {
if (this.options.debug) { if (this.options.debug) {
this.logger.debugEnabled = true; this.logger.debugEnabled = true;
} }
this.logger.prefix = 'Q#' + (uid++);
let result = null; this.logger.debug("Query is running with options:", this.options);
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;
}
}
if (result === null) {
throw lastError;
}
return result;
}
// Runs a single attempt with a timeout and cleans up afterward
async runOnceSafe() {
let abortCall = null; let abortCall = null;
this.abortedPromise = new Promise((resolve,reject) => { this.abortedPromise = new Promise((resolve,reject) => {
abortCall = () => reject("Query is finished -- cancelling outstanding promises"); abortCall = () => reject("Query is finished -- cancelling outstanding promises");
@ -63,13 +49,18 @@ class Core extends EventEmitter {
try { try {
const promise = this.runOnce(); const promise = this.runOnce();
timeout = Promises.createTimeout(this.options.attemptTimeout, "Attempt"); 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 { } finally {
timeout && timeout.cancel(); timeout && timeout.cancel();
try { try {
abortCall(); abortCall();
} catch(e) { } catch(e) {
this.debugLog("Error during abort cleanup: " + e.stack); this.logger.debug("Error during abort cleanup: " + e.stack);
} }
} }
} }