Improve logging of errors through promises

pull/285/head
Kelvin Schoofs 3 years ago
parent c7f1261311
commit 417945d4bb

@ -44,6 +44,10 @@ export interface LoggingOptions {
export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 }; export const LOGGING_NO_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 0 };
export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 }; export const LOGGING_SINGLE_LINE_STACKTRACE: Partial<LoggingOptions> = { callStacktrace: 1 };
function hasPromiseCause(error: Error): error is Error & { promiseCause: string } {
return typeof (error as any).promiseCause === 'string';
}
export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR'; export type LoggerDefaultLevels = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR';
class Logger { class Logger {
protected parent?: Logger; protected parent?: Logger;
@ -95,16 +99,24 @@ class Logger {
if (message instanceof Error && message.stack) { if (message instanceof Error && message.stack) {
let msg = message.message; let msg = message.message;
try { try {
msg += `\nJSON: ${JSON.stringify(message)}`; const json = JSON.stringify(message);
if (json !== '{}') msg += `\nJSON: ${json}`;
} finally { } } finally { }
const { maxErrorStack } = options; const { maxErrorStack } = options;
if (message.stack && maxErrorStack) { if (message.stack && maxErrorStack) {
let { stack } = message; let { stack } = message;
if (maxErrorStack > 0) { if (maxErrorStack > 0) {
stack = stack.split(/\n/g).slice(0, maxErrorStack).join('\n'); stack = stack.split(/\n/g).slice(0, maxErrorStack + 1).join('\n');
} }
msg += '\n' + stack; msg += '\n' + stack;
} }
if (hasPromiseCause(message) && maxErrorStack) {
let { promiseCause } = message;
if (maxErrorStack > 0) {
promiseCause = promiseCause.split(/\n/g).slice(1, maxErrorStack + 1).join('\n');
}
msg += '\nCaused by promise:\n' + promiseCause;
}
message = msg; message = msg;
} }
// Do we need to also output a stacktrace? // Do we need to also output a stacktrace?

@ -1,20 +1,9 @@
import type { EnvironmentVariable } from "./fileSystemConfig"; import type { EnvironmentVariable } from "./fileSystemConfig";
export type toPromiseCallback<T> = (err?: Error | null | void, res?: T) => void;
/** Wrapper around async callback-based functions */
export async function toPromise<T>(func: (cb: toPromiseCallback<T>) => void): Promise<T> {
return new Promise<T>((resolve, reject) => {
try {
func((err, res) => err ? reject(err) : resolve(res!));
} catch (e) {
reject(e);
}
});
}
/** Wrapper around async callback-based functions */ /** Wrapper around async callback-based functions */
export async function catchingPromise<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): Promise<T> { export async function catchingPromise<T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => any): Promise<T> {
const promiseCause = new Error();
Error.captureStackTrace(promiseCause, catchingPromise);
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
try { try {
const p = executor(resolve, reject); const p = executor(resolve, reject);
@ -24,6 +13,27 @@ export async function catchingPromise<T>(executor: (resolve: (value?: T | Promis
} catch (e) { } catch (e) {
reject(e); reject(e);
} }
}).catch(e => {
if (e instanceof Error) {
let t = (e as any).promiseCause;
if (!(t instanceof Error)) t = e;
if (!('promiseCause' in t)) {
Object.defineProperty(e, 'promiseCause', {
value: promiseCause.stack,
configurable: true,
enumerable: false,
});
}
}
throw e;
});
}
export type toPromiseCallback<T> = (err?: Error | null | void, res?: T) => void;
/** Wrapper around async callback-based functions */
export async function toPromise<T>(func: (cb: toPromiseCallback<T>) => void): Promise<T> {
return catchingPromise((resolve, reject) => {
func((err, res) => err ? reject(err) : resolve(res!));
}); });
} }

Loading…
Cancel
Save