Продолжаем марафон универсальных утилит на JavaScript, которые я использую от проекта к проекту независимо от стека.
import { clsx } from './string-ops';
import { getReadableTimeDiff } from './time-ops';
export const debugFactory = <TEvent, TError>({ label: header }: { label: string }) => {
let counter = 0;
const auxCountersState: { [key: string]: number } = {};
const getServiceMsg = () => {
const msgs = [];
if (auxCountersState._unknown > 0 || auxCountersState._others > 0) {
msgs.push('About:');
if (auxCountersState._unknown > 0) {
msgs.push('_unknown - Те ивенты что хотели посчитать, но не смогли идентифицировать');
}
if (auxCountersState._others > 0) {
msgs.push('_others - Те ивенты что не планировали посчитать, но лог был вызван, поэтому счетчик на всякий случай заведен (просто для информации)');
}
}
return msgs.join('\n');
};
const _auxCountersStateInc = ({ key }: { key: string | undefined }) => {
if (typeof key === 'string') {
if (typeof auxCountersState[key] === 'number') {
auxCountersState[key] += 1;
} else {
auxCountersState[key] = 1;
}
} else if (typeof auxCountersState._unknown === 'number') {
auxCountersState._unknown += 1;
} else {
auxCountersState._unknown = 1;
}
};
let firstTs: number | undefined;
let oldTs: number | undefined = firstTs;
return {
_auxCountersStateInc,
log: ({
evt, err, label, msgs, auxStateSelector
}: {
label: string;
evt: TEvent;
err: TError;
msgs?: string[];
auxStateSelector?: (e: TEvent) => string | undefined;
}) => {
const currentTs = new Date().getTime();
if (typeof firstTs !== 'number') {
firstTs = currentTs;
}
console.groupCollapsed(
clsx(
header,
`[${counter}]`,
typeof oldTs === 'number' ? `[+${getReadableTimeDiff({ diffInMs: currentTs - oldTs }).humanized}]` : undefined,
label,
)
);
oldTs = currentTs;
counter += 1;
// - NOTE: Exp
if (typeof auxStateSelector === 'function') {
const key = auxStateSelector(evt);
_auxCountersStateInc({ key });
} else {
_auxCountersStateInc({ key: '_others' });
}
// -
if (Array.isArray(msgs) && msgs?.length > 0) {
msgs.forEach((msg) => console.log(msg));
}
console.dir({
evt, err
});
console.log(JSON.stringify(auxCountersState, null, 2));
console.log(`${getReadableTimeDiff({ diffInMs: currentTs - firstTs }).humanized} since first`);
const serviceMsg = getServiceMsg();
if (serviceMsg) { console.log(serviceMsg); }
console.groupEnd();
},
counter,
};
};Utils
~/utils/string-ops/clsx.ts
function toVal(mix: any) {
let k; let y; let
str = '';
if (typeof mix === 'string' || typeof mix === 'number') {
str += mix;
} else if (typeof mix === 'object') {
if (Array.isArray(mix)) {
const len = mix.length;
for (k = 0; k < len; k++) {
if (mix[k]) {
if (y = toVal(mix[k])) {
str && (str += ' ');
str += y;
}
}
}
} else {
for (y in mix) {
if (mix[y]) {
str && (str += ' ');
str += y;
}
}
}
}
return str;
}
export function clsx(...args: any) {
let i = 0; let tmp; let x; let str = ''; const
len = args.length;
for (; i < len; i++) {
if (tmp = args[i]) {
if (x = toVal(tmp)) {
str && (str += ' ');
str += x;
}
}
}
return str;
}~/utils/time-ops/getReadableTimeDiff.ts
import { getPadStart } from '~/utils/number-ops';
export const getReadableTimeDiff = ({ diffInMs: ms }: { diffInMs: number }): {
humanized: string;
} => {
const secs = Math.floor(Math.abs(ms) / 1000);
const mins = Math.floor(secs / 60);
const mscs = Math.floor(Math.abs(ms) % 1000);
return {
get humanized() {
return [
[
getPadStart({
value: mins, standardLength: 2
}),
getPadStart({
value: secs, standardLength: 2
}),
].join(':'),
getPadStart({
value: mscs, standardLength: 3
}),
].join('.');
}
};
};~/utils/number-ops/getPadStart.ts
export const getPadStart = ({
value, standardLength
}: {
value: number;
standardLength: number;
}): string => String(value).padStart(standardLength, '0');Usage example
const yourLogger = debugFactory({ label: 'YOUR LOGGER HEADER' });
yourLogger.log({ event: e, err, label: '🟢 validated.ok' });
yourLogger.log({
event: e,
err,
label: '🔴 !validated.ok',
auxStateSelector: (ev) => `ERR:${ev?.origin || 'others'}:${ev?.data?.topic || `topic-is-${typeof ev?.data?.topic}`}`,
});