ToC
需求
需求是构建一个可以传入预定义好类型级别,得到一个新的函数或对象,可以通过调用其上面的方法来生成当前对象上允许的级别日志。日志级别共分为 4 个级别,分别为:
- silent:静默执行,不会输出任何级别的日志
- error:只会输出 error 级别的日志
- warn:可以输出 error、warn 级别的日志
- info:可以输出 error、warn、info 等任何类型的日志
举个例子,比如传入了 warn
,那么调用其上面的 info
方法则不会有任何输出,但是 error
warn
方法依旧可以输出对应的日志。
这种输入决定输出类型的函数通常会使用使用工厂模式来处理。我们先了解一下工厂模式的实现思路,其实很多设计模式都是听起来很牛x,但实际上这些模式都是我们日常开发中一些很普遍的做法,我们按照需求一步一步去做就有了以下代码:
1function createLogger(level) {2 function error(msg) {3 console.error(`[error] ${msg}`)4 }5
6 function warn(msg) {7 console.warn(`[warn] ${msg}`)8 }9
10 function info(msg) {32 collapsed lines
11 console.info(`[info] ${msg}`)12 }13
14 let logger = {}15 if (level === 'silent') {16 logger = {17 error: () => {},18 warn: () => {},19 info: () => {},20 }21 } else if (level === 'error') {22 logger = {23 error,24 warn: () => {},25 info: () => {},26 }27 } else if (level === 'warn') {28 logger = {29 error,30 warn,31 info: () => {},32 }33 } else if (level === 'info') {34 logger = {35 error,36 warn,37 info,38 }39 }40
41 return logger42}
日志的权重
以上虽然可以实现需求,但这对我们的学习毫无帮助,因为这只不过是将每天都在写的代码又重复了一遍。后来我发现了另一种思路,即使用一个对象来存储日志的级别,用对象的 key 来保存日志的级别,用对象的 value 来保存日志的权重,这样我们就可以通过权重来判断日志是否可以输出了。这样我们就可以重构一下代码:
1const LEVELS = {2 silent: 0,3 error: 1,4 warn: 2,5 info: 3,6}7
8function createLogger(level) {9 const thresh = LogLevels[level]10
14 collapsed lines
11 function print(method) {12 if (thresh < LEVELS[level]) {13 return () => {}14 }15
16 return (msg) => console[method](`[${method}] ${msg}`)17 }18
19 return {20 error: print.bind(null, 'error'),21 warn: print.bind(null, 'warn'),22 info: print.bind(null, 'info'),23 }24}
只不过在使用的时候还是需要判断一下运行环境,比如说在 “TTY”( Teletypes 或 teletypewriters:虚拟控制台)和服务器 CI 环境中就需要谨慎操作了,因为通常这些日志都是需要保留用于排查错误,或是查看系统当前状态的,如果贸然清除打印的日志的话,则会使得出现问题是排查十分困难,所以建议在调用前判断一下 process.stdout.isTTY && !process.env.CI
,如果结果为 true,则表示可以安全调用,否则还是使用一个空函数来替代较为稳妥。
以上。