超级简单的Javascript记录-两个装饰器,并完成


您是否还在为每次打喷嚏而写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


格式化程序


这是一个具有两种方法的对象: startend 。 这些方法创建相同格式的消息。


start为以下事件创建消息:


  • 在创建课程之前。
  • 在调用同步和异步方法和功能属性(静态和非静态)之前。

end为以下事件生成消息:


  • 调用同步和异步方法以及静态和非静态的函数属性后。
  • 调用同步和异步方法以及静态和非静态功能属性时出错。

默认情况下, new ClassLoggerFormatterService()


包括


最终消息中应包含的配置。


args

它可以是布尔值或对象。


如果它是boolean ,则它设置是否在所有消息( startend )中包括一个参数列表(还记得Args: [milk] ?)。


如果它是一个对象,则它必须具有两个布尔属性: startendstart指定是否在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

您只能覆盖所需的基本方法。


让我们看看如何为所有帖子添加时间戳。


我并不是说这应该在实际项目中完成。 pinowinston和大多数其他记录器可以自行执行此操作。 本示例仅用于教育目的。

 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(), }) 

戳实况


结论


在项目上使用此库之前,请记住遵循安装说明并熟悉要求


希望您不要白白浪费时间,并且这篇文章至少对您有用。 请踢并批评。 我们将学习一起更好地编码。

Source: https://habr.com/ru/post/zh-CN447970/


All Articles