-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogify.class.ts
More file actions
110 lines (94 loc) · 3.46 KB
/
Copy pathlogify.class.ts
File metadata and controls
110 lines (94 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { Immutable } from '#library/class.library.js';
import { sym, markConfig } from '#library/symbol.library.js';
import { asType } from '#library/type.library.js';
import { isObject, isEmpty } from '#library/assertion.library.js';
import type { ValueOf } from '#library/type.library.js';
const Method = {
Log: 'log',
Info: 'info',
Warn: 'warn',
Debug: 'debug',
Error: 'error',
} as const
const Level = {
[Method.Error]: 1,
[Method.Warn]: 2,
[Method.Info]: 3,
[Method.Log]: 3,
[Method.Debug]: 4,
} as const;
/**
* provide standard logging methods to the console for a class
*/
@Immutable
export class Logify {
#name: string;
#opts: Logify.Constructor = { [sym.$Logify]: true };
/**
* if {catch:true} then show a warning on the console and return
* otherwise show an error on the console and re-throw the error
*/
#trap(method: Logify.Method, ...msg: any[]) {
const config = (isObject(msg[0]) && (msg[0] as any)[sym.$Logify] === true) ? msg.shift() : this.#opts;
const currentLevel = (typeof config.debug === 'number') ? config.debug : (config.debug ? Level[Method.Debug] : Level[Method.Error]);
const methodLevel = Level[method] ?? 0;
if (methodLevel > currentLevel) return;
const output = msg.map(m => {
if (m instanceof Error) return m.message;
if (isObject(m)) {
try {
const name = m.constructor?.name ?? 'Object'; // avoiding JSON.stringify (expensive)
if (name === 'Object') {
const keys = Object.keys(m);
const summary = keys.slice(0, 3).join(', ');
return `{ ${summary}${keys.length > 3 ? `, ... (+${keys.length - 3} more)` : ''} }`;
}
return `[${name}]`;
} catch { return '[Object]'; }
}
return String(m);
}).filter(s => !isEmpty(s)).join(' ');
if (!config.silent && !isEmpty(output))
(console as any)[method](`${this.#name}: ${output}`);
if (method === Method.Error && !config.catch) {
const e = msg.find(m => m instanceof Error);
const message = `${this.#name}: ${output}`;
if (e) {
e.message = message;
throw e;
}
throw new Error(message);
}
}
/** console.log */ log = (...msg: any[]) => this.#trap(Method.Log, ...msg);
/** console.info */ info = (...msg: any[]) => this.#trap(Method.Info, ...msg);
/** console.warn */ warn = (...msg: any[]) => this.#trap(Method.Warn, ...msg);
/** console.debug */ debug = (...msg: any[]) => this.#trap(Method.Debug, ...msg);
/** console.error */ error = (...msg: any[]) => this.#trap(Method.Error, ...msg);
constructor(self?: Logify.Constructor | string, opts = {} as Logify.Constructor) {
opts = { ...opts }; // defensive copy of the options
const arg = asType(self);
this.#name = (arg.type === 'String')
? arg.value
: (self as any)?.constructor?.name
?? 'Logify';
if (arg.type === 'Object') {
const cfg = { ...arg.value as object };
markConfig(cfg); // auto-mark if it's a config object
Object.assign(opts, cfg);
}
markConfig(opts); // auto-mark the options object
this.#opts.debug = opts.debug ?? false; // default debug to 'false'
this.#opts.catch = opts.catch ?? false; // default catch to 'false'
this.#opts.silent = opts.silent ?? false; // default silent to 'false'
}
}
export namespace Logify {
export type Method = ValueOf<typeof Method>
export interface Constructor {
debug?: boolean | number | undefined,
catch?: boolean | undefined,
silent?: boolean | undefined,
[sym.$Logify]?: boolean | undefined
}
}