1 | 'use strict'
|
2 |
|
3 | const flatstr = require('flatstr')
|
4 | const {
|
5 | lsCacheSym,
|
6 | levelValSym,
|
7 | useOnlyCustomLevelsSym,
|
8 | streamSym,
|
9 | formattersSym,
|
10 | hooksSym
|
11 | } = require('./symbols')
|
12 | const { noop, genLog } = require('./tools')
|
13 |
|
14 | const levels = {
|
15 | trace: 10,
|
16 | debug: 20,
|
17 | info: 30,
|
18 | warn: 40,
|
19 | error: 50,
|
20 | fatal: 60
|
21 | }
|
22 | const levelMethods = {
|
23 | fatal: (hook) => {
|
24 | const logFatal = genLog(levels.fatal, hook)
|
25 | return function (...args) {
|
26 | const stream = this[streamSym]
|
27 | logFatal.call(this, ...args)
|
28 | if (typeof stream.flushSync === 'function') {
|
29 | try {
|
30 | stream.flushSync()
|
31 | } catch (e) {
|
32 |
|
33 | }
|
34 | }
|
35 | }
|
36 | },
|
37 | error: (hook) => genLog(levels.error, hook),
|
38 | warn: (hook) => genLog(levels.warn, hook),
|
39 | info: (hook) => genLog(levels.info, hook),
|
40 | debug: (hook) => genLog(levels.debug, hook),
|
41 | trace: (hook) => genLog(levels.trace, hook)
|
42 | }
|
43 |
|
44 | const nums = Object.keys(levels).reduce((o, k) => {
|
45 | o[levels[k]] = k
|
46 | return o
|
47 | }, {})
|
48 |
|
49 | const initialLsCache = Object.keys(nums).reduce((o, k) => {
|
50 | o[k] = flatstr('{"level":' + Number(k))
|
51 | return o
|
52 | }, {})
|
53 |
|
54 | function genLsCache (instance) {
|
55 | const formatter = instance[formattersSym].level
|
56 | const { labels } = instance.levels
|
57 | const cache = {}
|
58 | for (const label in labels) {
|
59 | const level = formatter(labels[label], Number(label))
|
60 | cache[label] = JSON.stringify(level).slice(0, -1)
|
61 | }
|
62 | instance[lsCacheSym] = cache
|
63 | return instance
|
64 | }
|
65 |
|
66 | function isStandardLevel (level, useOnlyCustomLevels) {
|
67 | if (useOnlyCustomLevels) {
|
68 | return false
|
69 | }
|
70 |
|
71 | switch (level) {
|
72 | case 'fatal':
|
73 | case 'error':
|
74 | case 'warn':
|
75 | case 'info':
|
76 | case 'debug':
|
77 | case 'trace':
|
78 | return true
|
79 | default:
|
80 | return false
|
81 | }
|
82 | }
|
83 |
|
84 | function setLevel (level) {
|
85 | const { labels, values } = this.levels
|
86 | if (typeof level === 'number') {
|
87 | if (labels[level] === undefined) throw Error('unknown level value' + level)
|
88 | level = labels[level]
|
89 | }
|
90 | if (values[level] === undefined) throw Error('unknown level ' + level)
|
91 | const preLevelVal = this[levelValSym]
|
92 | const levelVal = this[levelValSym] = values[level]
|
93 | const useOnlyCustomLevelsVal = this[useOnlyCustomLevelsSym]
|
94 | const hook = this[hooksSym].logMethod
|
95 |
|
96 | for (var key in values) {
|
97 | if (levelVal > values[key]) {
|
98 | this[key] = noop
|
99 | continue
|
100 | }
|
101 | this[key] = isStandardLevel(key, useOnlyCustomLevelsVal) ? levelMethods[key](hook) : genLog(values[key], hook)
|
102 | }
|
103 |
|
104 | this.emit(
|
105 | 'level-change',
|
106 | level,
|
107 | levelVal,
|
108 | labels[preLevelVal],
|
109 | preLevelVal
|
110 | )
|
111 | }
|
112 |
|
113 | function getLevel (level) {
|
114 | const { levels, levelVal } = this
|
115 | return levels.labels[levelVal]
|
116 | }
|
117 |
|
118 | function isLevelEnabled (logLevel) {
|
119 | const { values } = this.levels
|
120 | const logLevelVal = values[logLevel]
|
121 | return logLevelVal !== undefined && (logLevelVal >= this[levelValSym])
|
122 | }
|
123 |
|
124 | function mappings (customLevels = null, useOnlyCustomLevels = false) {
|
125 | const customNums = customLevels ? Object.keys(customLevels).reduce((o, k) => {
|
126 | o[customLevels[k]] = k
|
127 | return o
|
128 | }, {}) : null
|
129 |
|
130 | const labels = Object.assign(
|
131 | Object.create(Object.prototype, { Infinity: { value: 'silent' } }),
|
132 | useOnlyCustomLevels ? null : nums,
|
133 | customNums
|
134 | )
|
135 | const values = Object.assign(
|
136 | Object.create(Object.prototype, { silent: { value: Infinity } }),
|
137 | useOnlyCustomLevels ? null : levels,
|
138 | customLevels
|
139 | )
|
140 | return { labels, values }
|
141 | }
|
142 |
|
143 | function assertDefaultLevelFound (defaultLevel, customLevels, useOnlyCustomLevels) {
|
144 | if (typeof defaultLevel === 'number') {
|
145 | const values = [].concat(
|
146 | Object.keys(customLevels || {}).map(key => customLevels[key]),
|
147 | useOnlyCustomLevels ? [] : Object.keys(nums).map(level => +level),
|
148 | Infinity
|
149 | )
|
150 | if (!values.includes(defaultLevel)) {
|
151 | throw Error(`default level:${defaultLevel} must be included in custom levels`)
|
152 | }
|
153 | return
|
154 | }
|
155 |
|
156 | const labels = Object.assign(
|
157 | Object.create(Object.prototype, { silent: { value: Infinity } }),
|
158 | useOnlyCustomLevels ? null : levels,
|
159 | customLevels
|
160 | )
|
161 | if (!(defaultLevel in labels)) {
|
162 | throw Error(`default level:${defaultLevel} must be included in custom levels`)
|
163 | }
|
164 | }
|
165 |
|
166 | function assertNoLevelCollisions (levels, customLevels) {
|
167 | const { labels, values } = levels
|
168 | for (const k in customLevels) {
|
169 | if (k in values) {
|
170 | throw Error('levels cannot be overridden')
|
171 | }
|
172 | if (customLevels[k] in labels) {
|
173 | throw Error('pre-existing level values cannot be used for new levels')
|
174 | }
|
175 | }
|
176 | }
|
177 |
|
178 | module.exports = {
|
179 | initialLsCache,
|
180 | genLsCache,
|
181 | levelMethods,
|
182 | getLevel,
|
183 | setLevel,
|
184 | isLevelEnabled,
|
185 | mappings,
|
186 | assertNoLevelCollisions,
|
187 | assertDefaultLevelFound
|
188 | }
|