@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
88 lines (87 loc) • 3.5 kB
JavaScript
import { _ms } from '../datetime/time.util.js';
import { _assert } from '../error/assert.js';
import { SimpleMovingAverage } from '../math/sma.js';
import { _stringify } from '../string/stringify.js';
import { _getArgsSignature, _getMethodSignature } from './decorator.util.js';
/**
* Console-logs when method had started, when it finished, time taken and if error happened.
* Supports both sync and async methods.
* Awaits if method returns a Promise.
*
* @example output:
*
* >> syncMethodSuccess()
* << syncMethodSuccess() took 124 ms
*
* >> asyncMethod()
* << asyncMethodThrow() took 10 ms ERROR: MyError
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export function _LogMethod(opt = {}) {
return (_target, key, descriptor) => {
_assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods');
const originalFn = descriptor.value;
const keyStr = String(key);
const { avg, logArgs = true, logStart, logResult, logResultLength = true, logger = console, } = opt;
let { logResultFn } = opt;
if (!logResultFn) {
if (logResult) {
logResultFn = r => ['result:', _stringify(r)];
}
else if (logResultLength) {
logResultFn = r => (Array.isArray(r) ? [`result: ${r.length} items`] : []);
}
}
const sma = avg ? new SimpleMovingAverage(avg) : undefined;
let count = 0;
descriptor.value = function (...args) {
const started = Date.now();
const ctx = this;
// e.g `NameOfYourClass.methodName`
// or `NameOfYourClass(instanceId).methodName`
const methodSignature = _getMethodSignature(ctx, keyStr);
const argsStr = _getArgsSignature(args, logArgs);
const callSignature = `${methodSignature}(${argsStr}) #${++count}`;
if (logStart)
logger.log(`>> ${callSignature}`);
try {
const res = originalFn.apply(ctx, args);
if (res && typeof res.then === 'function') {
// Result is a Promise, will wait for resolution or rejection
return res
.then((r) => {
logFinished(logger, callSignature, started, sma, logResultFn, r);
return r;
})
.catch((err) => {
logFinished(logger, callSignature, started, sma, logResultFn, undefined, err);
throw err;
});
}
// not a Promise
logFinished(logger, callSignature, started, sma, logResultFn, res);
return res;
}
catch (err) {
logFinished(logger, callSignature, started, sma, logResultFn, undefined, err);
throw err; // rethrow
}
};
return descriptor;
};
}
// eslint-disable-next-line max-params
function logFinished(logger, callSignature, started, sma, logResultFn, res, err) {
const millis = Date.now() - started;
const t = ['<<', callSignature, 'took', _ms(millis)];
if (sma) {
t.push(`(avg ${_ms(sma.pushGetAvg(millis))})`);
}
if (err !== undefined) {
t.push('ERROR:', err);
}
else if (logResultFn) {
t.push(...logResultFn(res));
}
logger.log(...t.filter(Boolean));
}