NestJs自定义日志服务

winston

Winston是一个Node.js的日志记录库,它可以帮助开发人员记录应用程序中的重要日志信息并进行分析。Winston支持多种日志记录级别,包括调试、信息、警告和错误,并提供多种输出选项,例如控制台输出、文件输出和数据库输出等。Winston还支持自定义日志格式和传输方式,可以与各种日志分析工具和第三方服务集成。因此,它被广泛用于开发Node.js应用程序的日志记录和分析。

winston-daily-rotate-file

winston-daily-rotate-file是Winston的一个扩展,它为Winston提供了一个方便的daily rotating file transport(日志文件轮转传输器)。简单来说,就是每当达到指定时间(通常是每日零点)或者文件大小超过限制时,它会创建一个新的日志文件,从而避免单个日志文件过大,便于管理和长期存储大量日志数据。

安装插件

# 安装日志库
pnpm add winston nest-winston
# 安装winston-daily-rotate-file
pnpm add winston-daily-rotate-file

自定义日志服务

@@filename(logger.service.ts)

import { Injectable } from '@nestjs/common'
import { utilities } from 'nest-winston'
import * as winston from 'winston'
import * as WinstonDailyRotateFile from 'winston-daily-rotate-file'

/**
 * @description: 自定义日志服务
 */
@Injectable()
export class MyLoggerService {
  private logger: winston.Logger

  constructor() {
    const rotateTransport = new WinstonDailyRotateFile({
      // 日志文件名
      filename: 'application-%DATE%.log',
      // 日志文件名日期格式
      datePattern: 'YYYY-MM-DD',
      // 日志目录(tip:开发环境logs目录和src同级,生产环境单独设置目录路径)
      dirname: 'logs',
      zippedArchive: true,
      // 日志文件最大大小
      maxSize: '20m',
      // 日志文件最大保存时间
      maxFiles: '14d',
      // 日志格式
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.ms(),
        utilities.format.nestLike('Blog Server Logger...', {
          colors: true,
          prettyPrint: true,
        })
      )
    })
    
    this.logger = winston.createLogger({
      transports: [ rotateTransport, new winston.transports.Console() ],
    })
  }

  public log(message: string) {
    this.logger.info('  ==> ' + message)
  }
  
  public warn(message: string) {
    this.logger.warn('  ==> ' + message)
  }

  public error(message: string, trace: string) {
    this.logger.error('  ==> ' + message, { trace })
  }

  public debug(message: string) {
    this.logger.debug('  ==> ' + message)
  }

  public verbose(message: string) {
    this.logger.verbose('  ==> ' + message)
  }
}

使用自定义的日志服务(以UserModule为例)`

user.module.ts

@@filename(user.module.ts)
import { Module } from '@nestjs/common'
import { UserService } from './user.service'
import { UserController } from './user.controller'
import { MyLoggerService } from 'src/libs/logger/logger.service'

/**
 * @description 用户模块
 */
@Module({
  controllers: [ UserController ],
  providers: [ UserService, MyLoggerService ],
  exports: [ UserService ]
})

export class UserModule {}

user.service.ts

@@filename(user.service.ts)
import { Injectable } from '@nestjs/common'
import { MyLoggerService } from 'src/libs/logger/logger.service'

/**
 * @description 用户服务
 */
@Injectable()
export class UserService {
  constructor(
    private readonly logger: MyLoggerService,
  ) {}

  async create() {
    this.logger.log('用户注册成功')
    return 'success'
  }
}

执行成功后会在logs目录下生成两个文件
在这里插入图片描述
.83b687ea5651ad72c72a8164984483a0aca9a3a9-audit.json

@@filename(.83b687ea5651ad72c72a8164984483a0aca9a3a9-audit.json)
{
    "keep": {
        "days": true,
        "amount": 14
    },
    "auditLog": "logs/.83b687ea5651ad72c72a8164984483a0aca9a3a9-audit.json",
    "files": [
        {
            "date": 1715152001987,
            "name": "logs/application-2024-05-08.log",
            "hash": "c84e5cf08751311bc6ce45228fa6e64c1e77a885ee03a7b2c42384a63ef85c1b"
        }
    ],
    "hashType": "sha256"
}

application-2024-05-08.log

[32m[Blog Server Logger...][39m [33mInfo[39m	2024/5/8 19:58:27 [32m  用户注册成功[39m [33m+26s[39m

自定义异常自动写入日志记录

@@filename(http-exception.filter.ts)
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  LoggerService
} from '@nestjs/common'

/**
 * @description HTTP异常过滤器, 用于处理HTTP异常
 */

// 固定注解, 用于捕获异常
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  constructor(private logger?: LoggerService) {}
  // ExceptionFilter 类中有一个 catch 方法, 这里来进行实现
  catch(exception: HttpException, host: ArgumentsHost) {
    // host代表nestjs整个进程
    const ctx = host.switchToHttp()
    const res = ctx.getResponse()
    const req = ctx.getRequest()
    const status = exception.getStatus()
    this.logger.error(`系统错误 =====> ${exception.message}`, exception.stack)
    // 响应给用户
    res.status(status).json({
      status,
      timestamp: new Date().toISOString(),
      url: req.url,
      method: req.method,
      message: exception.message || exception.name
    })
  }
}

使用方式

@@filename(main.ts)
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { NestExpressApplication } from '@nestjs/platform-express'
import { HttpExceptionFilter } from 'src/libs/filters/http-exception/http-exception.filter'
import { MyLoggerService } from 'src/libs/logger/logger.service'

/**
 * The bootstrap function is an async function that creates an instance of the Nest application.
 */
async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule)

  // 异常拦截器
  app.useGlobalFilters(new HttpExceptionFilter(new MyLoggerService))

  await app.listen(3000)
}
bootstrap().then(_ => _)

个人博客:https://www.linmeimei.top/ 欢迎访问


NestJS 中的自定义守卫和自定义本地策略都是用于保护路由的工具,但它们的工作方式略有不同。 自定义守卫是一个类,它实现 `CanActivate` 接口并包含一个名为 `canActivate` 的方法。在 `canActivate` 方法中,您可以编写逻辑来检查用户是否可以访问该路由。如果 `canActivate` 方法返回 `true`,则表示用户可以访问该路由。如果方法返回 `false` 或抛出异常,则表示用户无法访问该路由。 以下是一个示例自定义守卫: ```typescript import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; @Injectable() export class MyGuard implements CanActivate { canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); return request.user && request.user.role === 'admin'; } } ``` 在上面的代码中,我们定义了一个名为 `MyGuard` 的自定义守卫。在 `canActivate` 方法中,我们检查请求对象中的用户对象是否存在,并且用户具有 `admin` 角色。如果这些条件都为真,则返回 `true`,表示用户可以访问该路由。否则,返回 `false`。 自定义本地策略是一个类,它实现 `PassportStrategy` 接口并包含一个名为 `validate` 的方法。在 `validate` 方法中,您可以编写逻辑来验证用户凭据并返回用户对象。如果用户凭据有效,则返回用户对象。如果凭据无效,则抛出异常。 以下是一个示例自定义本地策略: ```typescript import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { AuthService } from './auth.service'; @Injectable() export class MyStrategy extends PassportStrategy(Strategy) { constructor(private readonly authService: AuthService) { super(); } async validate(username: string, password: string) { const user = await this.authService.validateUser(username, password); if (!user) { throw new UnauthorizedException(); } return user; } } ``` 在上面的代码中,我们定义了一个名为 `MyStrategy` 的自定义本地策略。在 `validate` 方法中,我们调用 `AuthService` 中的 `validateUser` 方法来验证用户凭据。如果凭据有效,则返回用户对象。否则,抛出 `UnauthorizedException` 异常。 总的来说,自定义守卫和自定义本地策略都是用于保护路由的工具。自定义守卫用于检查请求对象中的信息,以确定用户是否可以访问该路由。自定义本地策略用于验证用户凭据,并返回用户对象。您可以根据您的需求选择使用其中的一个或两个。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林_深时见鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值