WEBKT

NestJS 日志进阶:Winston 集成 ELK、Graylog 最佳实践

42 0 0 0

为啥要搞日志聚合?

Winston:NestJS 的日志利器

安装 Winston

基本用法

集成 ELK Stack

安装 winston-elasticsearch

配置 winston-elasticsearch

集成 Graylog

安装 winston-graylog2

配置 winston-graylog2

最佳实践

总结

兄弟们,今天咱们聊聊 NestJS 的日志处理,特别是如何用 Winston 这个强大的日志库,把你的 NestJS 应用日志跟 ELK Stack (Elasticsearch, Logstash, Kibana) 和 Graylog 这些流行的日志聚合服务给串起来。别担心,我会一步步带你搞定,保证干货满满,让你看完就能上手!

为啥要搞日志聚合?

在咱开发和运维的过程中,日志可是个宝贝。出了问题,靠它排查;系统表现,靠它监控;用户行为,靠它分析。但是,如果你的应用部署在多个服务器上,或者你的系统变得越来越复杂,直接看分散在各个地方的日志文件,那可就太痛苦了。这时候,日志聚合就派上用场了。

日志聚合,简单来说,就是把各个地方的日志收集到一个统一的地方,方便你集中管理、搜索、分析。ELK Stack 和 Graylog 就是干这个的,它们能帮你把日志这事儿给整得明明白白。

Winston:NestJS 的日志利器

NestJS 默认用的是 ConsoleLogger,简单场景下够用,但要玩转日志聚合,就得请出 Winston 了。Winston 是 Node.js 社区里最流行的日志库之一,功能强大,配置灵活,社区支持也贼好。

安装 Winston

npm install winston

基本用法

在 NestJS 里用 Winston,你可以创建一个自定义的 logger 服务,然后在各个模块里注入使用。下面是一个简单的例子:

// logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
@Injectable()
export class MyLogger implements LoggerService {
private readonly logger: winston.Logger;
constructor() {
this.logger = winston.createLogger({
level: 'info', // 设置日志级别
format: winston.format.combine(
winston.format.timestamp(), // 添加时间戳
winston.format.json() // 输出 JSON 格式
),
transports: [
new winston.transports.Console(), // 输出到控制台
// 可以添加其他 transports,比如输出到文件
],
});
}
log(message: string, context?: string) {
this.logger.info(message, { context });
}
error(message: string, trace: string, context?: string) {
this.logger.error(message, { trace, context });
}
warn(message: string, context?: string) {
this.logger.warn(message, { context });
}
debug(message: string, context?: string) {
this.logger.debug(message, { context });
}
verbose(message: string, context?: string) {
this.logger.verbose(message, { context });
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { MyLogger } from './logger.service';
@Module({
providers: [MyLogger],
exports: [MyLogger], // 导出,方便其他模块使用
})
export class AppModule {}
// 某个 controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
import { MyLogger } from './logger.service';
@Controller('test')
export class TestController {
constructor(private readonly logger: MyLogger) {}
@Get()
getHello(): string {
this.logger.log('Hello World!', TestController.name);
return 'Hello World!';
}
}

集成 ELK Stack

要把 Winston 的日志输出到 ELK Stack,你需要用到一个叫 winston-elasticsearch 的 transport。

安装 winston-elasticsearch

npm install winston-elasticsearch @elastic/elasticsearch

配置 winston-elasticsearch

// logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import * as Elasticsearch from 'winston-elasticsearch';
@Injectable()
export class MyLogger implements LoggerService {
private readonly logger: winston.Logger;
constructor() {
const esTransportOpts = {
level: 'info',
clientOpts: {
node: 'http://your-elasticsearch-host:9200', // Elasticsearch 地址
// 如果有认证,需要配置用户名密码
// auth: {
// username: 'your_username',
// password: 'your_password'
// }
},
indexPrefix: 'nestjs-logs', // 索引前缀
};
this.logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new Elasticsearch(esTransportOpts),
],
});
}
// ...log, error, warn, debug, verbose 方法...
}

注意:

  • your-elasticsearch-host:9200 要替换成你自己的 Elasticsearch 地址。
  • 如果 Elasticsearch 有认证,需要配置 auth 选项。
  • indexPrefix 可以自定义,用于区分不同应用的日志。

配置完成后,你的 NestJS 应用的日志就会被发送到 Elasticsearch,你可以在 Kibana 里搜索和查看。

集成 Graylog

Graylog 也是一个很受欢迎的日志管理系统。要把 Winston 的日志输出到 Graylog,你可以用 winston-graylog2 这个 transport。

安装 winston-graylog2

npm install winston-graylog2

配置 winston-graylog2

// logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import * as Graylog2 from 'winston-graylog2';
@Injectable()
export class MyLogger implements LoggerService {
private readonly logger: winston.Logger;
constructor() {
const graylogOptions = {
name: 'Graylog',
level: 'info',
graylog: {
servers: [{ host: 'your-graylog-host', port: 12201 }], // Graylog 地址和端口
// hostname: 'my-app', // 可选,主机名
// facility: 'NestJS', // 可选,设施名
},
staticMeta: { service: 'my-nestjs-app' }, // 添加静态元数据
};
this.logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
),
transports: [
new winston.transports.Console(),
new Graylog2(graylogOptions),
],
});
}
// ...log, error, warn, debug, verbose 方法...
}

注意:

  • your-graylog-host12201 要替换成你自己的 Graylog 地址和端口。
  • staticMeta 可以添加一些静态的元数据,方便你在 Graylog 里过滤和搜索。

配置完成后,你的 NestJS 应用的日志就会被发送到 Graylog。

最佳实践

  1. 日志级别控制: 根据环境(开发、测试、生产)设置不同的日志级别。开发环境可以设置成 debug,方便调试;生产环境设置成 infowarn,避免过多日志影响性能。

  2. 结构化日志: 尽量使用 JSON 格式输出日志,方便日志聚合系统解析和处理。添加必要的字段,比如时间戳、请求 ID、用户 ID 等,方便后续分析。

  3. 异常处理: 捕获未处理的异常,记录详细的错误信息和堆栈跟踪,方便排查问题。

    //logger.service.ts
    exceptionHandlers: [
    new winston.transports.File({ filename: 'exceptions.log' })
    ],
    rejectionHandlers: [
    new winston.transports.File({ filename: 'rejections.log' })
    ]
  4. 日志轮转: 对于输出到文件的日志,配置日志轮转,避免单个日志文件过大。可以用 winston-daily-rotate-file 这个 transport。

    npm install winston-daily-rotate-file
    
    // logger.service.ts
    new winston.transports.DailyRotateFile({
    filename: 'application-%DATE%.log',
    datePattern: 'YYYY-MM-DD-HH',
    zippedArchive: true,
    maxSize: '20m',
    maxFiles: '14d'
    })
  5. 异步日志: 对于高并发场景,可以考虑使用异步日志,避免日志写入阻塞主线程。Winston 的 transports 默认是同步的,但你可以通过一些方法(比如使用 async 库)实现异步写入。

  6. 日志脱敏: 对于包含敏感信息(比如密码、密钥)的日志,进行脱敏处理,避免泄露。

  7. 统一日志格式: 为了方便日志的后续处理和分析, 建议在整个应用中, 甚至整个组织内使用统一的日志格式. 可以在winston.format.combine中定义通用的日志格式.

  8. 全局日志实例: 在logger.service.ts中创建的winston实例, 可以在整个应用中共享. 避免在每个模块中都创建新的winston实例, 造成资源浪费.

  9. Context信息: 在调用logger的各个方法时, 传入context参数, 可以帮助你更好地追踪日志的来源. 例如, 可以传入ControllerService的类名.

总结

好了,兄弟们,今天咱们把 NestJS 的日志处理给捋了一遍,从 Winston 的基本用法,到集成 ELK Stack 和 Graylog,再到一些最佳实践,希望对你有所帮助。记住,日志是排查问题、监控系统、分析用户行为的利器,一定要用好它!

如果你还有其他关于 NestJS 日志的问题,或者想了解更多关于 ELK Stack、Graylog 的配置细节,欢迎留言讨论,咱们一起进步!

全栈老王 NestJSWinston日志管理

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/7872