yuheng/doc/fastify-docs/docs/Logging.md
2025-03-19 15:54:28 +08:00

168 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h1 align="center">Fastify</h1>
## 日志
日志默认关闭,你可以在创建 Fastify 实例时传入 `{ logger: true }` 或者 `{ logger: { level: 'info' } }` 选项来开启它。要注意的是,日志无法在运行时启用。为此,我们使用了
[abstract-logging](https://www.npmjs.com/package/abstract-logging)。
Fastify 专注于性能,因此使用了 [pino](https://github.com/pinojs/pino) 作为日志工具。默认的日志级别为 `'info'`
开启日志相当简单:
```js
const fastify = require("fastify")({
logger: true,
});
fastify.get("/", options, function (request, reply) {
request.log.info("Some info about the current request");
reply.send({ hello: "world" });
});
```
在路由函数之外,你可以通过 Fastify 实例上挂载的 Pino 实例来记录日志:
```js
fastify.log.info("Something important happened!");
```
如果你想为日志配置选项,直接将选项传递给 Fastify 实例就可以了。
你可以在 [Pino 的文档](https://github.com/pinojs/pino/blob/master/docs/api.md#pinooptions-stream)中找到全部选项。如果你想指定文件地址,可以:
```js
const fastify = require("fastify")({
logger: {
level: "info",
file: "/path/to/file", // 将调用 pino.destination()
},
});
fastify.get("/", options, function (request, reply) {
request.log.info("Some info about the current request");
reply.send({ hello: "world" });
});
```
如果需要向 Pino 传送自定义流 (stream),仅需在 `logger` 对象中添加 `stream` 一项即可。
```js
const split = require("split2");
const stream = split(JSON.parse);
const fastify = require("fastify")({
logger: {
level: "info",
stream: stream,
},
});
```
<a name="logging-request-id"></a>
默认情况下Fastify 给每个请求分配了一个 ID 以便跟踪。如果头部存在 "request-id" 即使用该值,否则会生成一个新的增量 ID。你可以通过 Fastify 工厂函数的 [`requestIdHeader`](Server.md#factory-request-id-header) 与 [`genReqId`](Server.md#genreqid) 来进行自定义。
默认的日志工具使用标准的序列化工具,生成包括 `req`、`res` 与 `err` 属性在内的序列化对象。`req` 对象是 Fastify [`Request`](./Request.md) 对象,而 `res` 则是 Fastify [`Reply`](./Reply.md) 对象。可以借由指定自定义的序列化工具来改变这一行为。
```js
const fastify = require("fastify")({
logger: {
serializers: {
req(request) {
return { url: request.url };
},
},
},
});
```
响应的 payload 与 header 可以按如下方式记录日志 (即便这是*不推荐*的做法)
```js
const fastify = require("fastify")({
logger: {
prettyPrint: true,
serializers: {
res(reply) {
// 默认
return {
statusCode: reply.statusCode,
};
},
req(request) {
return {
method: request.method,
url: request.url,
path: request.path,
parameters: request.parameters,
// 记录 header 可能会触犯隐私法律,例如 GDPR (译注General Data Protection Regulation)。你应该用 "redact" 选项来移除敏感的字段。此外,验证数据也可能在日志中泄露。
headers: request.headers,
};
},
},
},
});
```
**注**:在 `req` 方法中body 无法被序列化。因为请求是在创建子日志时就序列化了,而此时 body 尚未被解析。
以下是记录 `req.body` 的一个方法
```js
app.addHook("preHandler", function (req, reply, done) {
if (req.body) {
req.log.info({ body: req.body }, "parsed body");
}
done();
});
```
_Pino 之外的日志工具会忽略该选项。_
你还可以提供自定义的日志实例。将实例传入,取代配置选项即可。提供的示例必须实现 Pino 的接口,换句话说,便是拥有下列方法:
`info`、`error`、`debug`、`fatal`、`warn`、`trace`、`child`。
示例:
```js
const log = require("pino")({ level: "info" });
const fastify = require("fastify")({ logger: log });
log.info("does not have request information");
fastify.get("/", function (request, reply) {
request.log.info(
"includes request information, but is the same logger instance as `log`",
);
reply.send({ hello: "world" });
});
```
_当前请求的日志实例在[生命周期](Lifecycle.md)的各部分均可使用。_
## 日志修订
[Pino](https://getpino.io) 支持低开销的日志修订,以隐藏特定内容。
举例来说,出于安全方面的考虑,我们也许想在 HTTP header 的日志中隐藏 `Authorization` 这一个 header
```js
const fastify = Fastify({
logger: {
stream: stream,
redact: ["req.headers.authorization"],
level: "info",
serializers: {
req(request) {
return {
method: request.method,
url: request.url,
headers: request.headers,
hostname: request.hostname,
remoteAddress: request.ip,
remotePort: request.socket.remotePort,
};
},
},
},
});
```
更多信息请看 https://getpino.io/#/docs/redaction。