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
111
112
113
114
115
116
117
118
119
120
121
122
|
export type Level = 'debug' | 'info' | 'warn' | 'error';
// Numbers correspond to the levels above, with 0 meaning "no level"
type LevelNum = 4 | 3 | 2 | 1 | 0;
interface Rules {
named?: Record<string, LevelNum>;
defaultLevel?: LevelNum;
}
const LEVEL_TO_NUM: Record<Level | 'off' | '*' | '', LevelNum> = {
'*': 4,
debug: 4,
info: 3,
warn: 2,
error: 1,
off: 0,
'': 0,
};
/**
* Parses log filtering instructions from localStorage.onyxLog.
* The instructions are a series of comma separated directives that restrict
* logging. Restrictions indicate the highest log level that a named logger
* will emit. The name of the logger is the string passed to
* LoggerFactory.loggerFor.
*
* By default (ex. empty rule string), no logs will be emitted.
*
* The format of the directives is NAME=LEVEL. LEVEL can be one of:
*
* - * - all levels are logged (debug, info, warn, error)
* - debug - same as above
* - info - everything but debug is logged
* - warn - everything but info and debug is logged
* - error - only errors are logged
* - off (or empty string, ex. "MyClass=") - nothing will be logged
*
* Some examples:
*
* - '*=*' will emit all log levels from all loggers
* - '*=info,Foo=off' will emit everything but debug except or logs from
* the named logger Foo (which will be entirely suppressed)
* - 'Bar=error,Baz=warn' will emit errors from Bar and Baz and warnings from
* Baz
*
* NOTE: Keep this in sync with README.md!
*/
function parseRules(): Rules {
const onyxLog: string = (() => {
try {
// The typeof check is for SSR
return (
(typeof window !== 'undefined'
? window.localStorage.onyxLog
: '') || ''
);
} catch {
// window.localStorage will throw when referenced (at all) when
// Chrome has it disabled
// See: rdar://93367396 (Guard localStorage and sessionStorage use)
return '';
}
})();
const PRODUCTION_DEFAULT = {}; // no logs unless specified
const DEV_DEFAULT = {
defaultLevel: LEVEL_TO_NUM['*'], // All logs unless specified
};
const isDevelopment = (() => {
// This is a little tricky. The ENV var is not real. It's replaced by
// rollup-plugin-replace. Thus, we can't do the usual of testing for
// the existence of `process` and then doing `process?.env` etc.
// Instead, we just try the whole thing and try/catch. This way,
// rollup-plugin-replace sees that entire string verbatim and can
// replace it with the proper environment.
try {
// @ts-ignore
return process.env.NODE_ENV !== 'production';
} catch {
return false;
}
})();
const defaultRules = isDevelopment ? DEV_DEFAULT : PRODUCTION_DEFAULT;
// If the localStorage is specified, start from a clean slate. Otherwise,
// use the environment default
const rules: Rules = onyxLog.length > 0 ? {} : defaultRules;
for (const directive of onyxLog.split(',').filter((v) => v)) {
// Invalid directive, must be of the form 'name=level'
const parts = directive.split('=');
if (parts.length !== 2) {
continue;
}
const [name, maxLevelName] = parts;
const maxLevel =
LEVEL_TO_NUM[maxLevelName as keyof typeof LEVEL_TO_NUM];
// Invalid level
if (typeof maxLevel === 'undefined') {
continue;
}
if (name === '*') {
rules.defaultLevel = maxLevel;
} else {
rules.named = rules.named ?? {};
rules.named[name] = maxLevel;
}
}
return rules;
}
export function shouldLog(name: string, level: Level): boolean {
const rules = parseRules();
// Rules for the named logger take precedence over the default
const maxLevel = (rules.named || {})[name] ?? rules.defaultLevel ?? 0;
return LEVEL_TO_NUM[level] <= maxLevel;
}
|