@ dotcom- relisity- kit/ logger (英语)
一个基于 皮诺将FT偏好烤入其中 此模块是 FT.com 可靠性工具包。 。 。 .
- 使用量
- 移动
- 捐款
- 许可证
使用量
安装 @dotcom-reliability-kit/logger 作为依附关系:
npm install --save @dotcom-reliability-kit/logger
在代码中包含 :
import logger from '@dotcom-reliability-kit/logger';
[TIP] 如果您使用此软件包并带有 TypeScript , 我们建议您使用以下设置:
tsconfig.json避免类型错误的文件 :{ "esModuleInterop": true, "module": "nodenext", "moduleResolution": "nodenext" }
使用同名方法在不同级别进行记录:
// Info-level log with a message
logger.info('Saying hello');
// Warning-level log with a message
logger.warn('Everything’s mostly cool');
// Error-level log with a message and additional data
logger.error('Uh-oh', { field: 'some value' });
// Info-level log with no message but some additional data
logger.info({ event: 'UPDATE_NOTIFICATION', data: data });
// Error-level log with an error object and additional data
const error = new Error('Whoops!');
logger.error('Uh-oh', error, { extra_field: 'boo' });
日志级别
了解日志级别是使用日志的关键。 设置日志关卡向正在调试您的应用程序的人解释他们正在工作的信息关卡(或错误的严重程度) .
有效等级为:
-
debug: 最低的日志级别。 这是用于高量信息,对于监控和运行应用程序来说并不关键,但在本地开发中调试时可能有用. 例如,伐木在渲染之前改变了文章信息. -
info: 最高 资料 日志级别。 这是用于关键信息,而关键信息对于在生产过程中调试应用程序很重要。 例如,当申请准备接受HTTP流量时. -
warn: 最低值 错误 日志级别。 这是当某事 可能 引起问题,但对最终用户没有重大影响。 例如,正在使用一种贬值的方法,在下一次更新依赖性时可能会被删除. -
error: 将军 错误 日志级别。 用于发生影响最终用户的错误。 例如,上游服务失败,因此无法提供页面. -
fatal: 最高 错误 日志级别。 这是当错误发生,而且严重到应用程序无法恢复时使用。 这应当始终伴随着申请程序被退出.
Logger
默认导出 @dotcom-reliability-kit/logger 是一个实例 Logger 课有一些合理的配置。 您也可以通过导入此类来创建自己的登录器, 并用此类自行快速创建 一些选项。 。 。 .
import { Logger } from '@dotcom-reliability-kit/logger';
const myLogger = new Logger({
// options go here
});
Logger 配置选项
配置选项可以传递到 Logger 用于改变日志的行为的构造器 .
options.baseLogData
基准日志数据,该数据将添加到该日志的每个日志输出中 伐木方法。这使得您可以在日志中添加一致的数据,例如关于应用程序的信息。 这一定是个 Object 但它可以拥有任何属性。 举个例子.
const logger = new Logger({
baseLogData: {
app: {
version: '1.2.3'
}
}
});
logger.info('This is a log');
// Outputs:
// {
// "level": "info",
// "message": "This is a log",
// "app": {
// "version": '1.2.3'
// }
// }
options.logLevel
记录时输出的最大日志级别 。 下方的日志会被忽略 。 此选项必须是 String 被设定为其中之一 支持的日志级别。 。 。 .
const logger = new Logger({
logLevel: 'warn'
});
logger.info('This is some info'); // Outputs: nothing
logger.warn('This is a warning'); // Outputs: the message
也可以将此选项设置为环境变量,这就是您如何配置默认的日志器. 以下环境变量来自:
LOG_LEVEL: 在环境变量中设置日志级别的优先方式SPLUNK_LOG_LEVEL: 设置日志级别、 保持兼容性的遗留方式 n- logger 键
options.serializers
可以通过指定序列器来自定义日志将某些属性转换为JSON的方式. 这使得您可以只提取您需要的信息,或者完全转换出单个属性值.
此选项必须是对象 。 每个密钥都对应您想要序列化的日志属性,每个值必须是执行序列化的函数. 以类型脚本类型表示 :
type Serializer = (value, propertyName) => any
当您定义一个属性的序列器时, 每次遇到该属性, 您的序列器函数都会被调用 最高层 表示日志数据对象。 举个例子.
const fruitEmoji = {
apple: '🍏',
banana: '🍌',
coconut: '🥥'
};
function emojifyFruit(value) {
return fruitEmoji[value] || value;
}
const logger = new Logger({
serializers: {
// If a "snack" property is found in a log, this function will be called
snack: emojifyFruit
}
});
logger.info({
message: 'Hello World',
snack: 'banana'
});
// Outputs:
// {
// "level": "info",
// "message": "Hello World",
// "snack": "🍌"
// }
无法以这种方式序列化一些属性以维护一致的日志 :
err, (中文(简体) ), error, (中文(简体) ), level, (中文(简体) ), message,以及 time 全部保留。 这些属性所配置的序列器将被忽略 .
[尖叫声]
在日志序列器中正确处理出乎意料的数据是你的责任. 您最好能打个字来避免日志发送失败 。 如果在序列器中遇到出乎意料的错误,你会看到LOG_METHOD_FAILURE日志中出现错误 .举个例子,我们可能会确保 我们工作的财产是一个字符串:
function emojifyFruit(value) { if (typeof value === 'string' && fruitEmoji[value]) { return fruitEmoji[value]; } // Always return the original value if you can't process it, so you don't lose log data return value; }
options.transforms
在日志输出前调用日志数据的一系列函数 。 这使得您可以在发送后对最终日志对象应用转换.
每个日志转换必须是一个接受单一对象参数并返回对象的函数。 以类型脚本类型表示 :
type LogData = {[x: string]: any};
type Transform = (logData: LogData) => LogData
你可以通过你需要的很多变换,尽管你必须考虑性能 — — 每一个函数都会被调用在发送的日志上.
function uppercaseProperties(logData) {
const entries = Object.entries(logData).map(([property, value]) => {
return [property.toUpperCase(), value];
});
return Object.fromEntries(entries);
}
const logger = new Logger({
transforms: [
uppercaseProperties
]
});
logger.info({
message: 'Hello World',
example: true
});
// Outputs:
// {
// "LEVEL": "info",
// "MESSAGE": "This is a log",
// "EXAMPLE": true
// }
您还可以使用 内置变换 做像面具敏感数据.
options.withPrettifier
如果可用, 是否发送预设日志 。 如果您有 NODE_ENV 环境变量设置为 production (单位:千美元)prod 或者说 p 也参与工作。 见 当地发展用途 更多信息.
一定是个 Boolean 默认为 true。 。 。 .
也可以将此选项设置为环境变量,这就是您如何配置默认的日志器. 设定 LOG_DISABLE_PRETTIFIER 环境变量改为 true 如果您想要强迫预装器不装入.
logger.log() 和快捷键方法
这个 log 方法 Logger 可用于将 JSON 格式化的日志发送到 stdout 并附有信息和补充资料。 这种方法要求 level 参数设置为有效 日志级别它可以接受任何其他论点,这些论点必须是: String,以及 Object,或 Error。 。 。 .
logger.log('debug', 'This is a message', { extraData: 123 }, new Error('Oops'));
使用一种快捷方式比使用一种快捷方式更容易,也不太容易出错 log(level)。每个日志级别都有快捷方式:
logger.debug(...logData): 日志含有“ 调试” 级别logger.info(...logData): 以“ info”级别登录logger.error(...logData): 有“ 错误” 级的日志logger.warn(...logData): 有"警告"级的日志logger.fatal(...logData): 以“ fatal” 级别登录
logger.flush()
大多数日志被排出队列并同步发送,因为这样可以保持应用程序的性能,并最小化日志的影响. 有时(非常少)你可能需要手动冲洗日志的队列. 通过使用 flush 方法 :
logger.flush();
如果应用程序停止运行,日志会被自动冲出,以确保日志信息不会在应用程序崩溃时丢失.
logger.createChildLogger()
创建一个与当前日志相同配置选项的新日志, 但附加 基准日志数据 合并为. 这使得您可以创建日志, 根据代码库中的位置添加额外信息 。 你必须通过一个 Object 设置儿童记录器的基准日志数据.
在快捷路线内使用应用程序日志和孩子日志的快捷示例 :
const app = express();
const appLogger = new Logger({
baseLogData: {
appName: 'my-app'
}
});
app.use((request, response) => {
request.log = appLogger.createChildLogger({
requestUrl: request.url
});
});
app.get('/mock-url', (request, response) => {
request.log.info('We got a request');
// Outputs:
// {
// "level": "info",
// "message": "We got a request",
// "appName": "my-app",
// "requestUrl": "/mock-url"
// }
});
内置变换
除了写你自己 日志转换,您可以使用作为本套件的一部分提供的。 所有内置变换器均作为电源的属性提供 transforms 对象 :
import { transforms } from '@dotcom-reliability-kit/logger';
legacyMask 转换
后遗症口罩变换对记录器采用相同的口罩行为 n- msk- 搜索器复制行为 它掩盖了您指定的字段列表,这样我们不会意外地在应用程序中登录敏感信息. 你可以这样使用:
import { Logger, transforms } from '@dotcom-reliability-kit/logger';
const logger = new Logger({
transforms: [
transforms.legacyMask()
]
});
logger.info({
email: '[email protected]'
});
// Outputs:
// {
// "level": "info",
// "email": "*****"
// }
您可以在选项对象中通过 :
const logger = new Logger({
transforms: [
transforms.legacyMask({
// options go here
})
]
});
legacyMaskOptions.denyList
显示日志中应当隐藏其值的属性名称的一系列字符串 。 在拒绝列表中添加新项目不会改变默认的一组属性名称 : firstName, (中文(简体) ), ft-backend-key, (中文(简体) ), ft-session-id, (中文(简体) ), FTSession_s, (中文(简体) ), FTSession, (中文(简体) ), lastName, (中文(简体) ), password, (中文(简体) ), phone, (中文(简体) ), postcode, (中文(简体) ), primaryTelephone, (中文(简体) ), session,以及 sessionId数字 :
transforms.legacyMask({
denyList: [ 'myEmailProperty', 'myPasswordProperty' ]
})
legacyMaskOptions.allowList
显示日志中属性名称的字符串数组 没有 他们的价值观被掩盖。 用于在您想要登录潜在敏感数据时覆盖默认字段 。 例如,如果你 email 属性实际包含 a boolean 显示用户是否被选入电子邮件,然后您可能想要这样做:
transforms.legacyMask({
allowList: [ 'email' ]
})
legacyMaskOptions.maskString
当财产被掩盖时用作替换的字符串 。 此默认为 *****数字 :
transforms.legacyMask({
maskString: '🙈🙉🙊'
})
日志数据序列化
记录器可以接受多种格式的数据,它将所有你给出的不同数据合并为一个对象,然后被字符串为JSON. 每种记录方法都可以接受任何数量的对象、字符串和错误作为日志数据:
logger.info('This is a string', { thisIsData: true }, { thisIsMoreData: true });
// Outputs:
// {
// "level": "info",
// "message": "This is a string",
// "thisIsData": true,
// "thisIsMoreData": true
// }
不同数据是如何序列化的
不同类型的数据在作为JSON输出前会有不同的序列:
-
对象 他们的地产都被提取出来 被记录为普通的JSON 举个例子.
logger.info({ hello: 'world' }); // Outputs: // { // "level": "info", // "hello": "world" // }[尖叫声]
需要注意的是,只有属性是 以 JSON 为序列 可以登录。 任何不可序列属性(例如函数)都不会是输出. -
字符串 移动到
message输出日志属性。 举个例子.logger.info('Hello world'); // Outputs: // { // "level": "info", // "message": "Hello World!" // } -
错误 移动到
error输出日志的属性,并使用 可靠性套件serializeError方法. e.g. (中文(简体) ).logger.info(new Error('Oops')); // Outputs: // { // "level": "info", // "error": { // "name": "Error", // "message": "Oops", // ...etc // } // }在日志数据的子属性中发现的错误有: 没有 由于性能原因, 序列化为这样: 环绕每个嵌入的属性以检查它是否错误是昂贵的( do) 没有 做到这一点。 同时,注意用自定义错误属性(例如)来传递错误.
myErrorProperty因为你们要像下面那样 连载它们logger.info({ myErrorProperty: serializeError(new Error('Oops')) });...如果没有,你会得到以下输出:
logger.info({ myErrorProperty: new Error('Oops') }); // Outputs: // { // "level": "info", // "myErrorProperty": {} // }例外是如果次级财产名称是
error或者说err,因为这些是自动序列化的。 举个例子.logger.info({ error: new Error('Oops') }); // Outputs: // { // "level": "info", // "error": { // "cause": null, // "code": "UNKNOWN", // "data": {}, // "isOperational": false, // "message": "Oops", // "name": "Error", // "relatesToSystems": [], // "statusCode": null // } // }
先后次序
日志数据项的顺序是有意义的,当遇到财产时,将使用第一审。 这是与JavaScript相反的行为 Object.assign数字 :
logger.info({ example: 1 }, { example: 2 }, { example: 3 });
// Outputs:
// {
// "level": "info",
// "example": 1
// }
当地开发使用情况
地方发展 @dotcom-reliability-kit/logger 不默认格式化或色彩化日志。 您可能想要让本地开发中的读取日志更加容易 .
要在当地获得格式化和有色日志,您需要满足两个条件:
-
拿着
NODE_ENV环境变量设置为development或者完全没有设定. -
保证您不会通过
withPrettifier选项。 。 。 .
生产使用
使用 @dotcom-reliability-kit/logger 正在生产中,需要您的应用程序处理日志到 stdout 把这些日志寄到更永久的地方 在赫鲁库,这意味着你必须拥有 迁移到日志排水沟在 AWS Lambda 上,这意味着您必须发送日志到 Cloud Watch(见技术中心文档) (中文(简体) ).
测试
如果你在用的话 欢乐 为了测试你的代码, 你可能会遇到一些问题 测试没有退出 当你嘲笑 @dotcom-reliability-kit/logger你不能依赖杰斯特的自嘲:
jest.mock('@dotcom-reliability-kit/logger');
这是因为,为了嘲弄日志机,杰斯仍然会装入原始模块,该模块将创建一个装有捆绑的全正版日志机 process.stdout。您可以通过提供自己的手动模拟记录器来绕过这个过程,也可以作为第二个参数 jest.mock 或作为文件在 __mocks__/@dotcom-reliability-kit/logger.js. e.g. (中文(简体) ).
const mockedLogger = {
debug: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
};
jest.mock('@dotcom-reliability-kit/logger', () => ({
...mockedLogger,
Logger: jest.fn(() => mockedLogger),
}));
兼容性
@dotcom-reliability-kit/logger 与大多数使用的 n- logger 键, (中文(简体) ), n- msk- 搜索器,以及 n- 无服务器搜索器我们努力使这些图书馆的移民尽可能容易。 差异的完整清单载于 移徙指南 还有关于移民的提示.
移动
咨询 移徙指南 如果你想要迁移到这个软件包的后期主要版本.
捐款
见 可靠性工具包中心贡献指南。 。 。 .
许可证
凭 麻省理工学院 执照.
版权:"2022","金融时报"有限公司.