
您是否还在为每次打喷嚏而写logger.info('ServiceName.methodName.')
和logger.info('ServiceName.methodName -> done.')
感到厌倦? 也许您像我一样,一再考虑过自动化这项业务? 在本文中,我们将讨论类记录器 ,它是仅用两个装饰器解决问题的方法之一。
为什么甚至有必要?
并非所有的完美主义者都是工程师,但是所有的工程师都是完美主义者(嗯,几乎)。 我们喜欢美丽的简洁抽象。 我们在鳄鱼丛中看到了美丽,一个人没有准备好并且无法阅读。 我们喜欢感觉自己像神,创造出按照我们的规则生活的微型宇宙。 据推测,所有这些品质都源于我们的懒惰。 不,工程师不怕上课,但是他非常讨厌双臂重复进行自动化的例行重复动作。
编写了数千行仅用于日志记录的代码后,通常我们会创建一些自己的模式和标准来编写消息。 不幸的是,我们仍然必须手动应用这些模式。 类记录器的主要“原因”是提供一种声明式,可配置的方法,以在创建类和调用其方法时轻松记录模板消息。
跳棋裸露!
不用四处走动,让我们直接看一下代码。
import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } }
该服务将创建三个日志条目:
- 在创建带有传递给构造函数的参数列表之前。
- 在调用
eat
之前先列出参数列表。 - 调用
eat
之后,并带有一个参数列表和执行结果。
用代码的话:
// ServiceCats // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.`
戳它直播 。
可以承诺的事件的完整列表:
- 在创建课程之前。
- 在调用同步和异步方法和功能属性(静态和非静态)之前。
- 调用同步和异步方法以及静态和非静态的函数属性后。
- 调用同步和异步方法以及静态和非静态功能属性时出错。
功能属性是分配给属性的箭头函数( class ServiceCats { private meow = () => null }
)。 最常用于维护执行上下文( this
)。
我们被承诺以“可配置”的方式记录日志
类记录器分为三个层次的配置层次结构:
调用每个方法时,所有三个级别合并在一起。 该库提供了合理的全局默认配置,因此无需任何预先配置即可使用它。
全局配置
它是为整个应用程序创建的。 可以使用setConfig
覆盖它。
import { setConfig } from 'class-logger' setConfig({ log: console.info, })
类配置
它对于每个类都是唯一的,并且适用于该类的所有方法。 可能会覆盖全局配置。
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats {}
方法配置
仅适用于方法本身。 它优先于类config和global config。
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // log: console.warn, }) eat(food: string) { return 'purr' } // `console.debug` sleep() { this.energy += 100 } }
戳实况
可以配置什么?
我们研究了如何更改配置,但仍未弄清楚可以在其中进行哪些更改。
在这里,您可以找到针对不同情况的许多配置对象示例 。
如果您对TypeScript有更好的了解,请链接到该界面。 比俄语:)
配置对象具有以下属性:
日志
这是一个实际上处理日志的功能。 她在输入处收到一个格式化的消息作为字符串。 用于记录以下事件:
- 在创建课程之前。
- 在调用同步和异步方法和功能属性(静态和非静态)之前。
- 调用同步和异步方法以及静态和非静态的函数属性后。
默认情况下, console.log
。
logError
此功能还处理日志记录,但仅处理错误消息。 她还收到一个格式化的消息作为字符串。 用于记录以下事件:
- 调用同步和异步方法以及静态和非静态功能属性时出错。
默认值为console.error
。
这是一个具有两种方法的对象: start
和end
。 这些方法创建相同格式的消息。
start
为以下事件创建消息:
- 在创建课程之前。
- 在调用同步和异步方法和功能属性(静态和非静态)之前。
end
为以下事件生成消息:
- 调用同步和异步方法以及静态和非静态的函数属性后。
- 调用同步和异步方法以及静态和非静态功能属性时出错。
默认情况下, new ClassLoggerFormatterService()
。
包括
最终消息中应包含的配置。
args
它可以是布尔值或对象。
如果它是boolean
,则它设置是否在所有消息( start
和end
)中包括一个参数列表(还记得Args: [milk]
?)。
如果它是一个对象,则它必须具有两个布尔属性: start
和end
。 start
指定是否在start
消息中包含参数列表,在end
消息中包含参数列表。
默认值为true
。
构筑
这是一个boolean
,用于控制是否记录类的创建。
默认值为true
。
结果
这是一个boolean
,用于控制方法返回值的记录。 值得注意的是,返回值不仅是方法成功执行后返回的值,而且还包括执行失败时所引发的错误。 还记得Res: purr
吗? 如果此标志为false
,则不会有Res: purr
。
默认值为true
。
classInstance
它可以是布尔值或对象。 该概念与include.args
相同。
将类的序列化实例视图添加到帖子中。 换句话说,如果您的类具有任何属性,它们将以JSON序列化并添加到日志中。
并非所有属性都可序列化。 类记录器使用以下逻辑:
- 我们采用intansa的所有自己的属性(不在原型中的属性)。
- 怎么了 原型很少会动态更改,因此记录其内容几乎没有意义。
- 我们把所有类型
function
都扔掉了。
- 怎么了 通常,类型
function
属性只是简单的箭头函数,而常规方法没有使用该箭头函数来维护上下文( this
)。 它们很少具有动态特性,因此记录它们毫无意义。
- 我们将所有不是简单对象的对象从其中丢弃。
- 这些简单的对象是什么? 如果
Object.prototype
原型是Object.prototype
则将其视为简单对象。 - 怎么了 通常,其他服务类的实例充当属性。 如果我们开始序列化,我们的日志将以最丑陋的方式膨胀。
- 使用JSON字符串序列化剩下的所有内容。
class ServiceA {} @LogClass({ include: { classInstance: true, }, }) class Test { private serviceA = new ServiceA() private prop1 = 42 private prop2 = { test: 42 } private method1 = () => null @Log() public method2() { return 42 } } // `Test` // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // `method2` // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // `method2` // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.'
默认值为false
。
如果您喜欢这个主意,但是您对美丽的主意坚持使用不同的消息行格式怎么办? 您可以通过传递自己的格式化程序来完全控制消息格式化。
您可以从头开始编写自己的格式化程序。 当然可以 但是此选项不会包含在此框架中(如果您对此特定选项感兴趣,请查看README中的“格式设置”部分)。
覆盖格式的最快,最简单的方法是从默认格式化程序ClassLoggerFormatterService
继承您的格式化程序。
ClassLoggerFormatterService
具有以下protected
方法,这些方法创建较小的最终消息块:
base
- 返回类名和方法名。 例如:
ServiceCats.eat
。
operation
- 根据方法是否成功
-> done
返回-> done
或-> error
。
args
- 返回序列化的参数列表。 示例:
. Args: [milk]
。 它使用快速安全字符串化来序列化引擎盖下的对象。
classInstance
- 返回该类的序列化实例。 示例:
. Class instance: {"prop1":42,"prop2":{"test":42}}
。 如果在配置中包括了include.classInstance
,但是由于某种原因,实例本身在记录时不可用(例如,对于静态方法或在创建类之前),则返回N/A
result
- 返回序列化的执行结果或序列化的错误。 对引擎盖下的对象使用快速安全字符串化 。 序列化的错误包括以下属性:
- 用于创建错误的类(函数)的名称(
error.constructor.name
)。 - 代码(
error.code
)。 - 消息(
error.message
)。 - 名称(
error.name
)。 - 堆栈跟踪(
error.stack
)。
final
start
消息包括:
base
args
classInstance
final
end
消息包括:
base
operation
args
classInstance
result
final
您只能覆盖所需的基本方法。
让我们看看如何为所有帖子添加时间戳。
我并不是说这应该在实际项目中完成。 pino , winston和大多数其他记录器可以自行执行此操作。 本示例仅用于教育目的。
import { ClassLoggerFormatterService, IClassLoggerFormatterStartData, setConfig, } from 'class-logger' class ClassLoggerTimestampFormatterService extends ClassLoggerFormatterService { protected base(data: IClassLoggerFormatterStartData) { const baseSuper = super.base(data) const timestamp = Date.now() const baseWithTimestamp = `${timestamp}:${baseSuper}` return baseWithTimestamp } } setConfig({ formatter: new ClassLoggerTimestampFormatterService(), })
戳实况
结论
在项目上使用此库之前,请记住遵循安装说明并熟悉要求 。
希望您不要白白浪费时间,并且这篇文章至少对您有用。 请踢并批评。 我们将学习一起更好地编码。