当前位置: 首页 > news >正文

NestJS——重构日志、数据库、配置

个人简介

👀个人主页: 前端杂货铺
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🎨100个小功能 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

内容参考链接
NestJS(一)Docker入门
NestJS(二)NestJS——创建项目、编写User模块
NestJS(三)TypeScript入门
NestJS(四)编程思想——FP、OOP、FRP、AOP、IOC、DI、MVC、DTO、DAO
NestJS(五)NestJS——多环境配置方案(dotenv、config、@nestjs/config、joi配置校验)
NestJS(六)NestJS——使用TypeORM连接MySQL数据库(Docker拉取镜像、多环境适配)
NestJS(七)NestJS——使用TypeORM操作数据库、增删改查、关联查询、QueryBuilder
NestJS(八)NestJS——日志、NestJS-logger、pino、winston、全局异常过滤器

文章目录

    • 重构 logger 配置
    • 数据库配置重构
    • 重构数据库、配置
    • 总结

重构 logger 配置

其实前文中我们对于 logger 的配置不太合理,接下来,我们进行重构。

GitHub 提交记录

创建 logger 模块。

nest g mo logs

简化 main.ts 文件,把日志相关配置抽离出去。

import { NestFactory } from "@nestjs/core"; // 导入 NestFactory,用于创建 Nest 应用实例
import { AppModule } from "./app.module"; // 导入应用的根模块
import { WINSTON_MODULE_NEST_PROVIDER } from "nest-winston";/*** 应用程序的入口文件* - 使用 NestFactory 创建应用实例* - 配置全局设置并启动应用*/
async function bootstrap() {// 创建应用实例,并可选配置日志级别const app = await NestFactory.create(AppModule, {// 日志级别配置(可选)// logger: ["error", "warn"], // 仅记录错误和警告日志});app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER)); // 使用 Winston 日志记录器// 设置全局路由前缀app.setGlobalPrefix("api"); // 所有路由将以 "api" 为前缀,例如 /api/userconst port = 3000; // 定义应用监听的端口号// 启动应用并监听指定端口await app.listen(port);
}// 启动应用
bootstrap();

logs.module.ts 中进行日志相关配置。

import { Module } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as winston from "winston";
import { utilities, WinstonModule, WinstonModuleOptions } from "nest-winston"; // 导入 nest-winston,用于 Nest 集成 Winston 日志库
import { Console } from "winston/lib/winston/transports";
import * as DailyRotateFile from "winston-daily-rotate-file";
import { LogEnum } from "src/enum/config.const";/*** 日志模块* - 集成 winston 日志库,支持控制台输出和文件轮转* - 日志级别、是否开启文件日志等参数可通过配置动态控制*/
@Module({imports: [// 异步注册 Winston 日志模块,支持依赖注入 ConfigService 动态配置WinstonModule.forRootAsync({inject: [ConfigService], // 注入配置服务useFactory: (configSerivice: ConfigService) => {// 控制台日志输出配置const consoleTransports = new Console({level: "info", // 日志级别为 infoformat: winston.format.combine(winston.format.timestamp(), // 添加时间戳utilities.format.nestLike() // 格式化日志为 Nest 风格),});// 按天轮转的 warn 级别日志文件配置const dailyTransports = new DailyRotateFile({level: "warn", // 日志级别为 warndirname: "logs", // 日志文件存储目录filename: "application-%DATE%.log", // 日志文件名,包含日期占位符datePattern: "YYYY-MM-DD-HH", // 日期格式zippedArchive: true, // 启用压缩存档maxSize: "20m", // 每个日志文件的最大大小为 20MBmaxFiles: "14d", // 保留日志文件的天数为 14 天format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.simple() // 简单格式化日志),});// 按天轮转的 info 级别日志文件配置,日志级别可通过配置动态获取const dailyInfoTransports = new DailyRotateFile({level: configSerivice.get(LogEnum.LOG_LEVEL), // 日志级别通过配置获取dirname: "logs", // 日志文件存储目录filename: "info-%DATE%.log", // 日志文件名,包含日期占位符datePattern: "YYYY-MM-DD-HH", // 日期格式zippedArchive: true, // 启用压缩存档maxSize: "20m", // 每个日志文件的最大大小为 20MBmaxFiles: "14d", // 保留日志文件的天数为 14 天format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.simple() // 简单格式化日志),});// 根据配置决定是否启用文件日志return {transports: [consoleTransports,...(configSerivice.get(LogEnum.LOG_ON)? [dailyTransports, dailyInfoTransports]: []), // 如果开启日志文件,则添加文件日志输出],} as WinstonModuleOptions;},}),],
})
export class LogsModule {}

扩充 .env 配置文件,分别控制日志的打开日志等级

LOG_ON=true
LOG_LEVEL=info

扩充 config.const.ts 文件,添加枚举。

export enum LogEnum {LOG_LEVEL = "LOG_LEVEL",LOG_ON = "LOG_ON",
}

user.controller.ts 中使用。

import {Controller,Get,HttpException,HttpStatus,Inject,LoggerService,Post,
} from "@nestjs/common"; // 导入控制器和 HTTP 请求装饰器
import { WINSTON_MODULE_NEST_PROVIDER } from "nest-winston";
/*** 用户控制器类* - 定义与用户相关的 HTTP 路由和处理逻辑* - 使用 `UserService` 提供的业务逻辑操作用户数据*/
@Controller("user") // 定义控制器的路由前缀为 "user"
export class UserController {// private logger = new Logger(UserController.name); // 创建 Logger 实例,用于记录日志/*** 构造函数* - 注入用户服务* @param userService 用户服务*/constructor(private userService: UserService,@Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService) {this.logger.log("UserController initialized"); // 控制器初始化时记录日志}...
}

在这里插入图片描述


数据库配置重构

接下来,我们需要把数据库的相关配置放入一个单独的 module 中去管理。

我们先进行一次版本控制。

git init git add ./git commit -m "first commit"

安装 ts-node,它是一个 TypeScript 执行引擎,能让我们在 Node.js 环境下直接运行 TypeScript 代码。

pnpm install ts-node --save-dev

修改 package.json 文件,添加如下内容。

   "scripts": {..."typeorm": "typeorm-ts-node-commonjs"},

GitHub 提交记录

接下来,我们创建 ormconfig.ts 文件,把 app.module.ts 的相关配置迁移至此文件。

ormconfig.ts

import { DataSource, DataSourceOptions } from "typeorm";
import { Logs } from "./src/logs/logs.entity";
import { Roles } from "./src/roles/roles.entity";
import { Profile } from "./src/user/profile.entity";
import { User } from "./src/user/user.entity";
import { TypeOrmModuleOptions } from "@nestjs/typeorm";/*** TypeORM 连接参数配置* - 配置数据库连接信息、实体、同步选项等*/
export const connectionParams = {type: "mysql", // 数据库类型host: "127.0.0.1", // 数据库主机地址port: 3090, // 数据库端口username: "root", // 数据库用户名password: "example", // 数据库密码database: "test", // 数据库名称entities: [User, Profile, Roles, Logs], // 实体类数组,自动映射到数据库表synchronize: true, // 是否自动同步数据库结构(开发环境建议开启,生产环境建议关闭)// logging: ["error", "warn"], // 日志级别(可选,记录错误和警告)// logging: process.env.NODE_ENV === "development", // 仅开发环境开启日志logging: false, // 是否开启 SQL 日志
} as TypeOrmModuleOptions;/*** 导出 DataSource 实例* - 用于 TypeORM CLI 和运行时的数据源管理* - 包含迁移和订阅者配置*/
export default new DataSource({...connectionParams, // 继承上面的数据库连接参数migrations: ["src/migrations/**"], // 迁移文件路径subscribers: [], // 订阅者(可选)
} as DataSourceOptions);

GitHub 提交记录

初始化一个TypeORM项目,使用MySQL数据库,并选择mysql2作为驱动程序。

npx typeorm init --database mysql2

解决一些报错问题后,我们运行:

npx ts-node src/index.ts

即可得到数据库中的相关数据。

在这里插入图片描述


重构数据库、配置

GitHub 提交记录

修改 ormconfig.ts 文件

import { DataSource, DataSourceOptions } from "typeorm";
import { Logs } from "./src/logs/logs.entity";
import { Roles } from "./src/roles/roles.entity";
import { Profile } from "./src/user/profile.entity";
import { User } from "./src/user/user.entity";
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
import * as fs from "fs";
import * as dotenv from "dotenv";
import { ConfigEnum } from "./src/enum/config.const";// 通过环境变量读取不同的.env文件
function getEnv(env: string): Record<string, unknown> {if (fs.existsSync(env)) {return dotenv.parse(fs.readFileSync(env));}return {};
}// 通过 dotenv 来解析不同的配置
function buildConnectionOptions() {const defaultConfig = getEnv(".env");const envConfig = getEnv(`.env.${process.env.NODE_ENV || "development"}`);const config = { ...defaultConfig, ...envConfig };return {type: config[ConfigEnum.DB_TYPE], // 数据库类型host: config[ConfigEnum.DB_HOST], // 数据库主机地址port: config[ConfigEnum.DB_PORT], // 数据库端口username: config[ConfigEnum.DB_USERNAME], // 数据库用户名password: config[ConfigEnum.DB_PASSWORD], // 数据库密码database: config[ConfigEnum.DB_DATABASE], // 数据库名称entities: [User, Profile, Roles, Logs], // 实体类数组,自动映射到数据库表synchronize: true, // 是否自动同步数据库结构(开发环境建议开启,生产环境建议关闭)// logging: ["error", "warn"], // 日志级别(可选,记录错误和警告)// logging: process.env.NODE_ENV === "development", // 仅开发环境开启日志logging: false, // 是否开启 SQL 日志} as TypeOrmModuleOptions;
}/*** TypeORM 连接参数配置* - 配置数据库连接信息、实体、同步选项等*/
export const connectionParams = buildConnectionOptions();/*** 导出 DataSource 实例* - 用于 TypeORM CLI 和运行时的数据源管理* - 包含迁移和订阅者配置*/
export default new DataSource({...connectionParams, // 继承上面的数据库连接参数migrations: ["src/migrations/**"], // 迁移文件路径subscribers: [], // 订阅者(可选)
} as DataSourceOptions);

修改 pageage.json 文件

   "scripts": {..."build": "cross-env NODE_ENV=production nest build","start:prod": "cross-env NODE_ENV=production node dist/src/main",}

修改 tsconfig.json 文件

{"compilerOptions": {"module": "commonjs","declaration": true,"removeComments": true,"emitDecoratorMetadata": true,"experimentalDecorators": true,"allowSyntheticDefaultImports": true,"target": "ES2021","sourceMap": true,"outDir": "./dist","baseUrl": "./","incremental": true,"skipLibCheck": true,"strictNullChecks": false,"noImplicitAny": false,"strictBindCallApply": false,"forceConsistentCasingInFileNames": false,"noFallthroughCasesInSwitch": false}
}

修改 app.module.ts 文件的中的 Joi 校验规则。

      validationSchema: Joi.object({NODE_ENV: Joi.string().valid("development", "production", "test") // 限制 NODE_ENV 的合法值.default("development"), // 默认值为 "development"DB_PORT: Joi.number().default(3306), // 数据库端口,默认值为 3306DB_HOST: Joi.alternatives().try(Joi.string().ip(),Joi.string().domain()), // 数据库主机地址,必须是合法的 IP 地址DB_TYPE: Joi.string().valid("mysql", "postgres"), // 数据库类型,不能为空DB_DATABASE: Joi.string().required(), // 数据库名称,不能为空DB_USERNAME: Joi.string().required(), // 数据库用户名,不能为空DB_PASSWORD: Joi.string().required(), // 数据库密码,不能为空DB_SYNC: Joi.boolean().default(false), // 是否自动同步数据库结构,默认值为 falseLOG_ON: Joi.boolean(),LOG_LEVEL: Joi.string(),}),

修改 logs.module.ts 文件,添加 createDailyRotateFileTransport 方法,控制日志的相关配置。抽离出来的原因为控制 logs 日志文件的生成。

import { Module } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as winston from "winston";
import { utilities, WinstonModule, WinstonModuleOptions } from "nest-winston"; // 导入 nest-winston,用于 Nest 集成 Winston 日志库
import { Console } from "winston/lib/winston/transports";
import * as DailyRotateFile from "winston-daily-rotate-file";
import { LogEnum } from "../enum/config.const";function createDailyRotateFileTransport(filename: string,level: string
): DailyRotateFile {return new DailyRotateFile({level, // 日志级别通过配置获取dirname: "logs", // 日志文件存储目录filename: `${filename}-%DATE%.log`, // 日志文件名,包含日期占位符datePattern: "YYYY-MM-DD-HH", // 日期格式zippedArchive: true, // 启用压缩存档maxSize: "20m", // 每个日志文件的最大大小为 20MBmaxFiles: "14d", // 保留日志文件的天数为 14 天format: winston.format.combine(winston.format.timestamp(), // 添加时间戳winston.format.simple() // 简单格式化日志),});
}/*** 日志模块* - 集成 winston 日志库,支持控制台输出和文件轮转* - 日志级别、是否开启文件日志等参数可通过配置动态控制*/
@Module({imports: [// 异步注册 Winston 日志模块,支持依赖注入 ConfigService 动态配置WinstonModule.forRootAsync({inject: [ConfigService], // 注入配置服务useFactory: (configSerivice: ConfigService) => {// 控制台日志输出配置const consoleTransports = new Console({level: "info", // 日志级别为 infoformat: winston.format.combine(winston.format.timestamp(), // 添加时间戳utilities.format.nestLike() // 格式化日志为 Nest 风格),});// 根据配置决定是否启用文件日志return {transports: [consoleTransports,...(configSerivice.get(LogEnum.LOG_ON)? [createDailyRotateFileTransport("info", "application"),createDailyRotateFileTransport("warn", "error"),]: []), // 如果开启日志文件,则添加文件日志输出],} as WinstonModuleOptions;},}),],
})
export class LogsModule {}

由于实体类将会继续扩充,因此我们可以采用获取文件目录的方式获取实体类数组。

  const entitiesDir =process.env.NODE_ENV === "test"? [__dirname + "/**/*/entity.ts"]: [__dirname + "/**/*.entity{.js,.ts}"];return {...entities: entitiesDir, // 实体类数组,自动映射到数据库表} as TypeOrmModuleOptions;

至此,完成重构。


总结

本篇文章,我们进行了日志、数据库和相关配置的重构,使得我们的项目构成更加合理。

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. DeepSeek
  2. NestJS 从入门到实战

在这里插入图片描述

相关文章:

  • Javase 基础加强 —— 08 IO流
  • 【Python 命名元祖】collections.namedtuple 学习指南
  • Java中关于数组的使用(下)
  • springboot中过滤器配置使用
  • 《爱的艺术》
  • python打卡训练营打卡记录day36
  • 电梯调度算法详解与Python实现
  • 简单数学板子和例题
  • 【短距离通信】【WiFi】WiFi7关键技术之4096-QAM、MRU
  • LLMs之Qwen:《Qwen3 Technical Report》翻译与解读
  • 用python实现中国象棋
  • PDF 编辑批量拆分合并OCR 识别
  • TCP 协议的相关特性
  • 识别速度快且精准的OCR工具
  • java每日精进 5.25【Redis缓存】
  • 【ExcelVBA 】类模块学习从入门到放弃
  • SHAP分析+贝叶斯优化BP神经网络+新数据预测+K折交叉验证+相关性分析+孤立森林异常值处理,Matlab代码实现,作者:机器学习之心!
  • 【MPC控制】番外篇:MPC 与 机器学习/深度学习 —— 双雄会的相似与不同
  • ADS学习笔记(三) 瞬态仿真
  • C++学习之STL学习:string类常用接口的模拟实现
  • 一个服务器可以做两个网站/沧州网站建设推广
  • 衢州市哪里都网站建设公司比较好/推广赚佣金
  • 以百度云做网站空间/产品推销
  • 金属材料网站建设/2022最新时事新闻及点评
  • 做游戏视频网站用什么程序好/网站推广的100种方法
  • 网站建设需要哪些人员/seo优化教程培训