# 日志输出组

说明

本章中使用的宏变量存储在Core.Macros中。

日志输出组提供了将一个或多个日志输出器进行组合的能力。在本章中,我们将讨论日志输出组的使用方式。

我们已经知道,日志输出组无法通过实例化直接创建实例,而是依赖LoggerCore执行createGroupLogger()间接执行实例化。

说明

在设计上,日志输出组使用了典型的工厂模式,即LoggerCore是生产日志输出组的工厂。

因此,我们在使用日志输出组进行组合日志输出需要:

  • 将需要组合的日志输出器组成LoggerCore的配置参数创建LoggerCore实例。

  • 使用LoggerCore实例执行createGroupLogger()并传入期望的日志输出组类型和配置参数,即可得到对应的日志输出组实例。

# 行为控制

在引入了LoggerCore日志输出组后,我们无法直接触发日志输出组组合的日志输出器的构建、启动和关闭动作。而是在实例化LoggerCore使用的配置参数中指定这些动作的触发方式,使LoggerCore日志输出组在恰当的时间点自动执行这些动作以满足日志收集的场景。

# 输出器构建

我们在LoggerCore构建时的配置参数中指定每个日志输出器buildTrigger以控制何时创建这些日志输出器的实例:

  • init:在LoggerCore实例化时触发日志输出器的构建动作。通常,持续型输出器使用此方式触发构建,比如:日期输出器

  • create:在日志输出组实例化时触发日志输出器的构建动作,即:LoggerCore实例执行createGroupLogger()时。通常,临时型输出器使用此方式触发构建,比如:文件输出器

提示

原则上,LoggerCore实例化动作仅执行一次。因此,我们可以将期望被LoggerCore创建的所有日志输出组实例共享的日志输出器使用init方式触发构建。

比如对一些频次型业务产生的日志进行收集时,我们可能希望将每次业务执行时的基础信息通过相同的日志输出器进行收集,而每次业务执行链路产生的日志使用不同的日志输出器收集。此时,我们可以:

  • 指定日志输出组共享的日志输出器使用init方式触发构建。

  • 指定日志输出组非共享的日志输出器使用create方式触发构建。

  • 每次业务执行时,使用LoggerCore实例执行createGroupLogger()创建新的日志输出组实例用于链路中的日志收集。

# 输出器启动

同样,我们可以在LoggerCore构建时的配置参数中指定每个日志输出器startTrigger以控制何时启动这些日志输出器的实例:

  • noneLoggerCore日志输出组不执日志输出器的启动动作。通常,支持自动启动的输出器使用此方式触发启动,比如:日期输出器、自动模式下的文件输出器

  • normal:在日志输出组实例执行start()时触发日志输出器的启动动作,即:日志输出器伴随日志输出组启动。通常,不支持自动启动的输出器使用此方式触发启动,比如:手动模式下的文件输出器

# 输出器关闭

同样,我们可以在LoggerCore构建时的配置参数中指定每个日志输出器closeTrigger以控制何时关闭这些日志输出器的实例:

  • noneLoggerCore日志输出组不执日志输出器的关闭动作。通常,支持自动关闭的输出器使用此方式触发关闭,比如:自动模式下的文件输出器

  • normal:在日志输出组实例执行close()时触发日志输出器的关闭动作,即:日志输出器伴随日志输出组关闭。通常,不支持自动关闭的输出器使用此方式触发关闭,比如:日期输出器、手动模式下的文件输出器

# LoggerCore

我们已经知道,LoggerCore是生产日志输出组的工厂。

通常,我们把LoggerCore实例在相同的日志收集场景中共享。在业务执行时,使用createGroupLogger()创建日志输出组实例用于跟踪业务链路中产生的日志。


LoggerCore实例创建日志输出组实例实际上执行了两个动作:

  1. LoggerCore实例根据实例化时应用的配置参数,创建需要组合的日志输出器实例并组成待植入日志输出组实例的日志输出器实例列表

  2. 实例化createGroupLogger()指定的日志输出组,得到对应的日志输出组实例。日志输出组实例化时会应用①中构造的日志输出器实例列表

说明

日志输出器实例列表中的元素其实并不仅仅是日志输出器实例本身,而是一个包含了日志输出器的行为触发方式的Object,其结构为:

  • logger日志输出器实例。

  • startTrigger日志输出器的启动触发方式。

  • closeTrigger日志输出器的关闭触发方式。

日志输出组将根据行为触发方式在恰当的时间点执行日志输出器实例的相关动作。

# 配置说明

通常,实例化LoggerCore需要使用由标识ID输出器基础配置输出器配置列表构成的配置对象,即:{ id, env, level, params, loggers }

  • idLoggerCore的唯一标识,默认值为`LoggerCore_${generateRandomString(6, 'uln')}`

提示

我们可以通过在应用程序中使用唯一的id标识和存储LoggerCore实例以实现在相同日志收集场景中共享LoggerCore实例。

  • envLoggerCore的运行环境/日志输出器的基础运行环境,默认值为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT

    需要注意的是,运行环境将影响LoggerCore的行为:

    • 当运行环境为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT时,LoggerCore创建日志输出器实例时将忽略配置对象中指定的输出器配置列表

    • 当运行环境不为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT时,LoggerCore创建日志输出器实例时将应用配置对象中实际指定的输出器配置列表

    说明

    当运行环境为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT时,LoggerCore将不再使用配置对象中指定的输出器配置列表创建日志输出器实例,而是使用一个基础输出器将日志单点输出至控制台,其触发方式为:

    • 构建触发方式init

    • 启动触发方式none

    • 关闭触发方式none


    另外,此配置将作为在输出器配置列表中指定日志输出器运行环境env时的默认值:

    • 未指定日志输出器env时,使用此配置。

    • 指定了日志输出器env时,使用实际配置。

  • level日志输出器的基础最小日志输出等级的名称或别名。

    日志输出器相同,此配置的默认值依赖于当前LoggerCore运行环境

    • 当运行环境为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT时,默认值为:all

    • 当运行环境不为BASE_LOGGER_DEVELOPMENT_ENVIRONMENT时,默认值为:error

    此配置的意义在于,将作为在输出器配置列表中指定日志输出器最小日志输出等级level时的默认值:

    • 未指定日志输出器level时,使用此配置。

    • 指定了日志输出器level时,使用实际配置。

  • params日志输出器的基础功能配置参数。

  • loggers日志输出器的配置列表,其元素结构为:{ type, buildTrigger, startTrigger, closeTrigger, env, level, params }

    • type日志输出器的类型,可以使用Corejs内置的日志输出器自定义输出器,默认值为null

      注意

      LoggerCore创建日志输出器实例时将对使用的type进行校验。如果指定了未继承自Core.BaseLogger自定义输出器时,将认为日志输出器的类型无效,不再执行实际构建动作。

    • buildTrigger日志输出器构建触发方式,默认值为'init'

    • startTrigger日志输出器启动触发方式,默认值为'none'

    • closeTrigger日志输出器关闭触发方式,默认值为'none'

    提示

    关于日志输出器的行为触发方式,我们可以参考行为控制一节。

    • env日志输出器运行环境,默认值为LoggerCore运行环境

    • level日志输出器最小日志输出等级的名称或别名,默认值为LoggerCore的最小日志输出等级名称或别名。

    • params日志输出器的功能配置参数。

    提示

    关于日志输出器运行环境最小输出等级功能配置参数,我们可以参考日志输出器功能配置一节。

# 配置参数

LoggerCore创建日志输出器实例时,应用的配置参数通过将个性化配置参数合并至默认配置参数生成:

  • 个性化配置参数即是在输出器配置列表日志输出器指定的配置参数

  • 默认配置参数可能由以下两个部分组成:

    1. LoggerCore实例化时指定的envlevelparams组成的配置参数

    2. LoggerCore实例执行createGroupLogger()时传入的配置参数

# 默认配置参数


  • 使用init触发创建行为的日志输出器,其实例化时使用的默认配置参数将仅由LoggerCore实例化时指定的envlevelparams组成。

  • 使用create触发创建行为的日志输出器,对于其使用的默认配置参数

    1. 使用Object.assign()createGroupLogger()时传入配置参数的功能配置参数paramsLoggerCore实例化时指定的基础功能配置参数params合并,得到{ params }

    2. 使用Object.assign()createGroupLogger()时传入配置参数运行环境最小输出等级组成{ env, level }并与LoggerCore实例化时指定envlevel合并,得到{ env, level }

    3. 使用Object.assign()合并①和②得到默认配置参数

# 应用配置参数


在创建日志输出器实例时,LoggerCore使用与默认配置参数类似的方式合并个性化配置参数默认配置参数生成最终应用于日志输出器的配置参数。因此,配置参数的优先级顺序从高到低依次为:

  • 输出器配置列表中指定的配置参数

  • createGroupLogger()时指定的配置参数

  • LoggerCore实例化参数中指定的配置参数

在默认的配置参数应用规则无法满足实际需求时,我们可以修改LoggerCore实例的onBuildLogger属性以定制日志输出器的创建行为,具体将在运行原理一节中讨论。

# 运行原理

我们已经知道,LoggerCore实例将在执行createGroupLogger()时创建日志输出组实例并向其中植入日志输出器实例列表。于是,小朋友你是否有很多问号:

  • 日志输出器实例如何创建?

  • 日志输出器实例何时创建?

在本节中,我们将围绕这两个问题讨论LoggerCore的运行原理。


对于日志输出器实例如何创建,答案非常简单:LoggerCore将在需要创建输出器配置列表某个日志输出器时,提取其在输出器配置列表指定的配置参数并调用实例方法onBuildLogger()

onBuildLogger()接收三个参数用于创建日志输出器实例:

  • type日志输出器的类型。

  • loggerConfigs:在输出器配置列表中指定的个性化配置参数

  • defaultConfigsLoggerCore自动应用的默认配置参数

举一个🌰,有这样一个LoggerCore

const Core = require('node-corejs');

const loggerCore = new Core.LoggerCore({
  env: 'prod',
  level: 'infos',
  params: { sourcePath: './testlogs' },
  loggers: [{
    type: Core.FileLogger,
    buildTrigger: 'create',
    startTrigger: 'normal',
    closeTrigger: 'normal',
    level: 'error',
    params: { auto: false, filePrefix: 'FileLogger_1' }
  }],
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在创建日志输出组时使用了:

loggerCore.createGroupLogger({ level: 'all', params: { fileName: 'LogFile' } });
1

那么,LoggerCore调用onBuildLogger()时带入的参数为:

type = Core.FileLogger;

loggerConfigs = { level: 'error', params: { auto: false, filePrefix: 'FileLogger_1' } };

defaultConfigs = { env: 'prod', level: 'all', params: { sourcePath: './testlogs', fileName: 'LogFile' } };
1
2
3
4
5

接下来,让我们看一看onBuildLogger()的默认合并个性化配置参数默认配置参数的实现:

onBuildLogger(type, loggerConfigs, defaultConfigs) {
  // 将个性化功能配置合并至默认功能配置
  const params = Object.assign({}, defaultConfigs.params, loggerConfigs.params);
  // 将个性化配置合并至默认配置并合并功能配置
  const configs = Object.assign({}, defaultConfigs, loggerConfigs, { params });
  // 执行输出器的实例化
  return new type(configs);
}
1
2
3
4
5
6
7
8

因此,我们可以通过修改LoggerCore实例的onBuildLogger属性进行更改以变更默认的日志输出器创建行为。

const Core = require('node-corejs');

// 创建LoggerCore实例
const loggerCore = new Core.LoggerCore({
  env: 'prod',
  level: 'infos',
  params: { sourcePath: './testlogs' },
  loggers: [{
    type: Core.FileLogger,
    buildTrigger: 'create',
    startTrigger: 'normal',
    closeTrigger: 'normal',
    level: 'error',
    params: { 
      auto: false, 
      filePrefix: 'FileLogger_1',
      filePrefixAsFileName: false,
    }
  }],
});

// 必须在createGroupLogger()前修改onBuildLogger()
loggerCore.onBuildLogger = (type, loggerConfigs, defaultConfigs) => {
  // 在文件名中附加日志输出的次数
  const { fileName, count } = defaultConfigs.params;
  // 应用至原有的参数合并逻辑中
  const params = Object.assign({}, defaultConfigs.params, loggerConfigs.params, { fileName: `${fileName}_${count}` });
  const configs = Object.assign({}, defaultConfigs, loggerConfigs, { params });
  return new type(configs);
};

// 进行日志输出
let count = 0;

setInterval(() => {
  count += 1;
  // 在创建日志输出组时传入count
  const logger = loggerCore.createGroupLogger({
    params: { count, fileName: 'LogFile' }
  });
  logger.start();
  logger.log(new Error('一个🌰日志'));
  logger.close();
}, 500);
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

对于日志输出器实例何时创建LoggerCore将根据在输出器配置列表中指定的构建触发方式决定创建日志输出器实例的时间点。

# 实例化阶段


  1. 根据构建触发方式输出器配置列表中的配置对象进行分类。

  2. 提取分类结果中使用init方式触发构建的配置对象,逐个使用配置对象内指定的配置执行onBuildLogger()创建对应的日志输出器实例并存储在实例属性_baseLoggerObjects中。

# 执行createGroupLogger()


  1. 根据createGroupLogger()传入的配置参数LoggerCore实例的配置参数生成默认配置参数

  2. 提取并迭代分类结果中使用create方式触发构建的配置对象,使用配置对象内指定的配置参数和默认配置参数执行onBuildLogger()创建对应的日志输出器实例。

  3. 合并LoggerCore实例属性_baseLoggerObjects中存储的日志输出器实例和①中创建的日志输出器实例组成待植入日志输出组实例的日志输出器实例列表

  4. 根据createGroupLogger()传入的日志输出组类型,创建对应的日志输出组实例并向其植入②中得到的日志输出器实例列表

# 创建输出组

在实现细节上,日志输出组继承自基础输出器,本质上是一个高阶日志输出器。虽然目前没有开放指定日志输出组功能配置参数的相关API,我们可以通过在启动日志输出组之前修改其实例属性以控制其表现行为,目前支持修改日志输出组的以下属性:

  • _errorHandler
  • _checkStateInLog
  • _checkStateInStart
  • _checkStateInClose

我们可以在基础输出器功能参数一节中根据其同名的功能配置参数了解这些实例属性的作用。


createGroupLogger()支持多种调用方式:

  • createGroupLogger():不指定配置参数创建基础输出组的实例。
  • createGroupLogger(type):不指定配置参数创建指定的日志输出组的实例。
  • createGroupLogger(configs):使用指定配置参数创建基础输出组的实例。
  • createGroupLogger(type, configs):使用指定配置参数创建指定的日志输出组的实例。

注意

createGroupLogger()执行过程中将对使用的日志输出组类型进行校验。出于保证应用程序的可用性考虑,如果使用了无效的日志输出组类型(即:非继承自基础输出组自定义输出组),将默认创建基础输出组的实例。


接下来,我们使用LoggerCore实现一个复杂的组合日志输出场景:

目标1

  • 保留最近5个归档日期内的日志。

  • 将每秒内产生的日志归档至同一日志文件,超出1KB时自动分割。

  • 日志文件按照归档前缀存储,且日志文件名中不展示归档前缀

目标2

  • 保留最近10秒内的日志。

  • 日志文件名中展示归档时间,但不展示归档前缀

  • 日志文件按照归档前缀存储,并将每5秒内产生的日志文件归档至同一文件。

const Core = require('node-corejs');

// 创建LoggerCore
const loggerCore = new Core.LoggerCore({
  env: 'prod',
  level: 'infos',
  params: {
    // 指定统一的归档源目录
    sourcePath: './testlogs',
  },
  loggers: [{
    // 使用共享的日期输出器实现目标1
    type: Core.DateLogger,
    buildTrigger: 'init',                // 在LoggerCore实例化时创建日期输出器
    startTrigger: 'none',                // 日期输出器自动启动,LoggerCore无需控制其启动动作
    closeTrigger: 'none',
    params: {
      filePrefix: 'Cycle_1s',            // 归档前缀
      keepDateNum: 5,                    // 保留最近5个归档日期
      maxSize: 1024,                     // 最大日志文件体积为1K
      dateFormat: 'YYYY-MM-DD_HH_mm_ss', // 以秒作为归档周期
      filePrefixAsFileName: false,       // 日志文件名不附加归档前缀
      filePrefixAsSourcePath: true,      // 在归档源目录中创建归档前缀目录
    }
  }, {
    // 使用非共享的文件输出器实现目标2
    type: Core.FileLogger,
    buildTrigger: 'create',              // 在执行createGroupLogger()时创建新的文件输出器
    startTrigger: 'none',                // 文件输出器自动启动,LoggerCore无需控制其启动动作
    closeTrigger: 'none',                // 文件输出器自动关闭,LoggerCore无需控制其关闭动作
    params: {
      filePrefix: 'Duration_5s',         // 归档前缀
      keepDateNum: 2,                    // 保留最近2个归档日期,2*5=10秒
      fileName: '_',                     // 归档文件名
      filePrefixAsFileName: false,       // 日志文件名不附加归档前缀
      filePrefixAsSourcePath: true,      // 在归档源目录中创建归档前缀目录
      dateFormat: 'YYYY-MM-DD_HH_mm_ss', // 以秒作为归档周期
      dateAsFileName: true,              // 日志文件名附加归档日期
      dateAsSourcePath: false,           // 不使用归档日期分类日志文件
    }
  }],
});

let logger = null;
// 每五秒创建新的日志输出组
function refreshLogger() {
  logger && logger.close();
  logger = loggerCore.createGroupLogger();
  logger.start();
}
refreshLogger();
setInterval(() => refreshLogger(), 5000);

// 每100ms输出一次日志
setInterval(() => {
  logger.log(new Error('一个🌰日志'));
}, 100);
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

# 基础输出组

基础输出组提供了组合日志输出器并控制其行为的基本能力。因此,我们在自定义输出组时需要继承基础输出组

需要注意的是,在LoggerCore实例执行createGroupLogger()时,如果指定了无效的日志输出组类型将默认创建基础输出组的实例。

# 运行原理

基础输出组实质上通过自定义输出器的方式实现,我们将围绕定制部分讨论基础输出组的运行原理。

# 构造函数


我们已经知道,LoggerCore在创建日志输出组时会向其中植入日志输出器实例列表。其实,日志输出组将持有其对应的LoggerCore实例的引用以维持派生关系。因此,日志输出组的构造函数接收两个参数:

  • loggerCore:创建日志输出组LoggerCore实例

  • _loggerObjects日志输出器的实例列表


在构造阶段,日志输出组执行了三个动作:

  1. 将派生日志输出组LoggerCore实例存储至实例属性loggerCore

  2. 日志输出器实例列表作为自定义功能配置参数生成日志输出组的配置对象。

  3. 使用②中得到的配置对象执行super()操作完成实例化。

提示

自定义输出器时执行super()操作将自动引导构造流程进入初始化阶段。

因此,为了避免执行逻辑混乱,在构造函数中仅使用功能配置参数的缓存机制存储日志输出器实例列表,而将实际的构造逻辑放在初始化阶段

# initLogger()


在讨论LoggerCore时我们已经提到过,日志输出器实例列表中的元素其实并不仅仅是日志输出器实例本身,而是一个包含了日志输出器的行为触发方式的Object日志输出组将根据行为触发方式控制其组合的日志输出器执行启动/关闭动作。

因此,出于性能考虑,在初始化阶段中将预先解析日志输出器实例列表提取日志输出器实例完成时空转换:

  • 提取使用normal方式触发启动的日志输出器实例,存储在实例属性_needStartLoggers中。

  • 提取使用normal方式触发关闭的日志输出器实例,存储在实例属性_needCloseLoggers中。

  • 将所有日志输出器实例存储在实例属性_loggers中。

# onStart()


日志输出组启动阶段,不仅需要变更日志输出组为启动状态;对于使用normal作为启动触发方式的日志输出器,还需在此阶段执行启动动作。因此,在启动阶段

  1. 执行super.onStart()尝试变更日志输出组为启动状态,如果变更状态失败则不再执行②。

  2. 迭代实例属性_needStartLoggers,执行每个日志输出器实例的启动逻辑。

注意

指定日志输出组实例的实例属性_checkStateInStart将控制执行super.onStart()时是否在变更日志输出组启动状态前执行状态校验进而影响执行结果

启动阶段的默认行为下,将通过判断super.onStart()执行结果以避免日志输出器的启动逻辑多次执行。因此,请谨慎调整_checkStateInStart

# onClose()


日志输出组启动阶段类似,关闭阶段不仅需要变更日志输出组为关闭状态;对于使用normal作为关闭触发方式的日志输出器,还需在此阶段执行关闭动作。因此,在关闭阶段

  1. 执行super.onClose()尝试变更日志输出组为关闭状态,如果变更状态失败则不再执行②。

  2. 迭代实例属性_needCloseLoggers,执行每个日志输出器实例的关闭逻辑。

注意

指定日志输出组实例的实例属性_checkStateInClose将控制执行super.onClose()时是否在变更日志输出组启动状态前执行状态校验进而影响执行结果

关闭阶段的默认行为下,将通过判断super.onClose()执行结果以避免日志输出器的关闭逻辑多次执行。因此,请谨慎调整_checkStateInClose

# onLog()

日志输出组本身并不产生任何日志,而是通过控制其组合的日志输出器输出日志。因此,日志输出组执行输出动作时:

  1. 使用onCheckState(BASE_LOGGER_STATE_TYPE_CAN_LOG)检测当前状态是否允许日志输出,在日志输出组当前状态不允许日志输出时将抛出异常信息且不再执行②。

  2. 迭代实例属性_loggers,执行每个日志输出器实例的log()进行日志输出。

注意

指定日志输出组实例的实例属性_checkStateInLog将控制执行onCheckState(BASE_LOGGER_STATE_TYPE_CAN_LOG)时是否执行日志输出组的启动状态校验。

在默认的输出行为中,未启动日志输出组时将拒绝执行实际输出动作以保证资源的有效性。因此,请谨慎调整_checkStateInLog

# 定制输出组

在本节中,我们将定制一个简单的日志输出组用于客户端请求处理链路的日志收集:

  • 记录start()close()时间差统计处理耗时。

  • 执行start()close()时自动输出开启和关闭日志。

首先,我们通过继承自Core.GroupLogger的方式实现一个自定义的日志输出组

const Core = require('node-corejs');

/**
 * 自定义日志输出组
 */
class HandlerLogger extends Core.GroupLogger {
  initLogger(configs) {
    super.initLogger(configs);
    this.startTime = null;
  }

  onStart() {
    // 尝试执行原始启动逻辑
    // 在原始启动逻辑执行失败时不再执行后续逻辑
    if (!super.onStart()) {
      return false;
    }

    // 启动成功时记录启动时间并输出日志
    this.startTime = new Date();
    this.log('i', '处理开始', `请求处理的开始时间:[${Core.Utils.formatDate(this.startTime, 'YYYY-MM-DD HH:mm:ss.SSS')}]`);
    return true;
  }

  onClose() {
    // 检测当前状态是否允许输出日志
    // 允许输出日志时将记录关闭时间并输出日志
    if (this.onCheckState(Core.Macros.BASE_LOGGER_CHECK_TYPE_CAN_LOG)) {
      const closeTime = new Date();
      this.log('i', '处理结束', `请求处理的结束时间:[${Core.Utils.formatDate(closeTime, 'YYYY-MM-DD HH:mm:ss.SSS')}}],处理耗时:[${closeTime - this.startTime}]ms`);
    }

    // 执行原始的关闭逻辑
    return super.onClose();
  }
}
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

接下来,我们确定日志收集的场景:

  • 将每次客户端请求处理链路中产生的日志输出至同一文件。

  • 按照请求路径请求日期日志文件进行分类。

  • 日志文件名中需要保留请求方式请求时间

  • 保留最近7天的日志。

因此,我们可以按照以下方式创建LoggerCore

const loggerCore = new Core.LoggerCore({
  env: 'prod',
  level: 'infos',
  params: { sourcePath: './testlogs' },
  loggers: [
    {
      type: Core.FileLogger,
      buildTrigger: 'create',
      startTrigger: 'normal',
      closeTrigger: 'normal',
      params: {
        auto: false,                  // 使用手动模式
        dateFormat: 'YYYY-MM-DD',     // 以天作为归档周期
        keepDateNum: 7,               // 保留最近7个归档日期
        dateAsFileName: false,        // 日志文件名不附加归档日期
        dateAsSourcePath: true,       // 使用归档日期分类日志文件
        filePrefixAsFileName: false,  // 日志文件名不附加归档前缀
        filePrefixAsSourcePath: true, // 使用归档前缀分类日志文件
      }
    }
  ],
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

最后,让我们结合ServiceCore实现对客户端请求处理链路的日志收集:

class TestHandler extends Core.Handler {
  static getRoutePath() {
    return '/Test.do';
  }

  initHandler(req, res) {
    // 在初始化阶段创建日志输出组
    const method = req.method.toUpperCase();
    this.logger = loggerCore.createGroupLogger(HandlerLogger, {
      params: {
        filePrefix: 'Test.do',
        fileName: `${method}-${Core.Utils.formatDate(new Date(), 'YYYY-MM-DD_HH_mm_ss_SSS')}-[%RANDOM_FILE_NAME%]`,
      }
    });
    // 启动日志输出组
    this.logger.start();
  }

  getHandler(req, res, next) {
    // 记录链路日志
    this.logger.log('i', '进入GET处理阶段');
    setTimeout(() => {
      next('Hello World');
      this.logger.log('i', 'GET处理结束,向客户端返回');
    }, 500);
  }

  destroyHandler(req, res) {
    // 在析构阶段关闭日志输出组
    this.logger.close();
  }
}

// 创建ServiceCore
const serviceCore = new Core.ServiceCore();
serviceCore.bind([TestHandler]);
serviceCore.start();
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

现在,我们可以通过访问使用curl http://localhost:3000/Test.do来检验实现成功啦!