1. 结构化日志
1.1 什么是结构化日志?
结构化日志是一种以 可解析格式(通常为 JSON) 输出,而不仅仅是普通的文本行。这种方式可以方便地解析、过滤、索引和搜索日志,相比于传统的文本日志,结构化日志更适合现代的微服务架构,因为它们能够提供更丰富、更有组织的信息。
1.2 为什么使用结构化日志?
- 自动化分析:日志系统(如 ELK、AWS CloudWatch)可以轻松解析 JSON 格式的日志,自动索引和分析它们。
- 增强的可追踪性:通过结构化字段,能快速筛选出与特定用户或请求相关的日志,方便定位问题。
- 一致性:无论是错误还是性能日志,结构化日志提供统一的格式,便于跨服务、跨团队的协作和分析。
1.3 结构化日志的优势
- 日志可读性和可扩展性:不同的日志系统能够通过结构化数据快速过滤、搜索并关联多个服务中的日志。
- 调试和故障排查:借助丰富的上下文信息,开发者可以快速回溯系统中的具体操作、事件流,以及用户交互行为。
- 自动化监控和告警:结构化日志可以与监控系统集成,实现自动告警,当系统异常时及时通知相关人员。
2. Winston 简介
2.1 什么是 Winston?
Winston 是一个功能强大且灵活的 Node.js 日志库,专为处理多种复杂的日志记录需求而设计。它提供了丰富的配置选项,可以根据不同的环境和需求,灵活管理日志的格式、传输方式和日志级别。
2.2 Winston 的核心功能
-
多级别日志支持:Winston 提供内置的日志级别管理,支持从
error到debug的多种日志级别,方便开发者根据场景设置不同的日志优先级。常见的日志级别包括:error(0) — 严重错误,需要立即关注。warn(1) — 警告,表明存在潜在问题。info(2) — 信息性消息,用于常规系统日志。debug(3) — 调试信息,详细记录系统内部状态。
-
多种传输方式(Transports):Winston 支持将日志输出到不同的目标(称为 "transports"),例如:
- 控制台输出
- 文件存储
- 远程服务(如 AWS CloudWatch、Elasticsearch 等)
-
日志格式化:Winston 提供强大的日志格式化功能,可以通过多种格式(如 JSON、文本、时间戳等)来格式化日志信息。开发者可以通过
combine方法自定义日志的输出格式,满足不同需求。
2.3 为什么选择 Winston?
- 灵活性:Winston 提供丰富的配置选项,可以根据应用的不同需求轻松配置,支持多环境日志管理。
- 可扩展性:支持自定义格式、传输器和插件,适合中大型项目的日志需求。
- 社区支持:Winston 拥有活跃的社区和强大的生态系统,在 Node.js 中被广泛使用,拥有成熟的功能和稳定性。
3. Winston 相关配置
3.1 在 Nuxt3 项目中引入 Winston
在 Nuxt3 项目中,使用 Winston 进行日志记录非常简单。首先需要将 Winston 作为依赖添加到项目中。
1. 安装 Winston
在项目根目录下,运行以下命令来安装 Winston:
npm install winston
或使用 yarn:
yarn add winston
2. 创建 logger.ts 文件
为了组织日志逻辑,可以在 server/utils 目录下创建一个 logger.ts 文件来封装 Winston 的配置和使用逻辑。这个文件将作为日志的全局入口。
mkdir -p server/utils
touch server/utils/logger.ts
3.2 配置 Winston 日志记录
在 logger.ts 文件中,你可以根据 Nuxt3 项目的环境配置 Winston。例如,你可以为开发环境启用彩色控制台输出,而在生产环境中生成结构化的 JSON 日志。
1. 基础配置
以下是 Nuxt3 项目中使用 Winston 的基础配置:
import { createLogger, format, transports } from 'winston';
import { config } from '~/server/config';
const { combine, timestamp, printf, json, colorize } = format;
// 创建日志记录器
export const logger = createLogger({
level: config.env.isDev ? 'debug' : 'info', // 根据环境设置日志级别
format: config.env.isDev
? combine( // 开发环境:彩色输出
colorize(),
timestamp(),
printf(({ timestamp, level, message }) => {
return `[${timestamp}] ${level}: ${message}`;
})
)
: combine( // 生产环境:JSON 格式化
timestamp(),
json()
),
transports: [
new transports.Console(),
new transports.File({ filename: 'logs/app.log' }) // 输出到文件
]
});
2. 日志格式说明
- 开发环境:使用
colorize()将日志级别按颜色区分,方便在控制台中调试。通过printf()自定义日志输出格式,显示时间戳、日志级别和消息内容。 - 生产环境:使用
json()格式化日志为 JSON 格式,便于集成到日志管理系统(如 AWS CloudWatch 或 ELK Stack)。
3. 在项目中使用日志记录
一旦你在 logger.ts 中配置好了 Winston,可以在项目的任何地方引入并使用日志记录器。
服务器端示例:
import { logger } from '~/server/utils/logger';
export default defineEventHandler((event) => {
logger.info('Received request', { url: event.req.url, method: event.req.method });
try {
// 处理业务逻辑
} catch (error) {
logger.error('Error occurred during request processing', { error: error.message });
}
});
3.3 动态日志级别
你可以根据项目的不同环境动态设置日志的级别,确保在开发环境中能够记录详细的日志,而在生产环境中只记录必要的日志:
logger.level = config.env.isDev ? 'debug' : 'info';
这样可以确保在开发环境中记录更多的 debug 日志,而在生产环境中只记录重要的 info 或 error 级别日志,减少冗余日志量。
3.4 总结
- Nuxt3 引入:通过安装 Winston 并在
logger.ts文件中封装配置,可以轻松集成日志系统。 - 灵活配置:根据开发或生产环境的不同需求,可以动态调整日志级别、输出格式以及日志传输的目标(控制台、文件)。
- 使用方式:在项目中随时引入日志记录器,确保对关键事件、错误和调试信息进行追踪。
通过这种方式,你可以在 Nuxt3 项目中实现高效的日志管理,并为生产环境提供必要的监控和调试信息。
4. 日志格式化与自定义
4.1 日志格式化的重要性
格式化是确保日志可读性和可解析性的关键步骤。通过自定义日志格式,开发者可以根据需求为每条日志附加更多的上下文信息,如User ID、IP 地址等。在 Nuxt3 项目中,使用 Winston 提供的灵活格式化选项,可以为控制台输出和 JSON 格式日志创建不同的格式,满足开发和生产环境的不同需求。
4.2 在 Nuxt3 项目中的日志格式化
在 server/utils/logger.ts 文件中,项目已经配置了两种格式化方式:控制台输出格式(consoleFormatter) 和 JSON 输出格式(jsonFormatter)。这两种格式根据开发环境和生产环境的不同需求进行使用。
1. 控制台输出格式(consoleFormatter)
控制台格式使用了 Winston 的 combine 方法,结合了多个格式化器(如 timestamp、colorize 和 printf)。这是开发环境中的常见做法,便于调试时查看彩色、易读的日志信息。
const consoleFormatter = combine(
errors({ stack: true }), // 捕获并打印错误堆栈
format(info => {
info.level = info.level.toUpperCase(); // 将日志级别转换为大写
return info;
})(),
timestamp(), // 添加时间戳
align(), // 日志对齐
colorize({ all: true }), // 使用颜色区分日志级别
splat(), // 支持字符串插值
printf(info => {
const { userId, requestId, scopes, requestIp, transactionId } = requestHandlerSession.getStore() || { scopes: [] };
const formattedRequestId = requestId ? \`[Request:\${requestId}]\` : '';
const formattedRequestIp = requestIp ? \`[IP:\${requestIp}]\` : '';
const formattedTransactionId = transactionId ? \`[TransactionId:\${transactionId}]\` : '';
const formattedUserId = userId ? \`[User:\${userId}]\` : '';
const scopesStr = scopes.length ? \`[Scope:\${scopes.map(scope => scope.name).join('/') }]\` : '';
const requestContextInfo = [formattedRequestId, formattedTransactionId, formattedRequestIp, formattedUserId, scopesStr]
.filter(v => !!v).join(' ');
if (info.stack) {
return \`[\${info.timestamp}] \${requestContextInfo}\n↳\${info.level}: \${info.message}\n--- Error Stack ---\n\${info.stack}\n-------------------\`;
}
return \`[\${info.timestamp}] \${requestContextInfo}\n↳\${info.level}: \${info.message}\`;
})
);
2. JSON 输出格式(jsonFormatter)
生产环境更倾向于使用 结构化日志,这里通过 JSON 格式输出日志信息,确保日志可以被日志聚合系统(如 AWS CloudWatch)解析。
const jsonFormatter = combine(
format.splat(), // 支持字符串插值
format.json(), // JSON 格式化
printf(info => {
const { userId, requestId, scopes, requestIp, transactionId } = requestHandlerSession.getStore() || { scopes: [] };
return JSON.stringify({
level: info.level,
uid: userId,
request: requestId,
transaction: transactionId,
scopes: scopes.length ? scopes.map(scope => scope.name).join('/') : undefined,
ip: requestIp,
message: info.message
});
})
);
4.3 动态选择格式
项目中的日志格式是根据配置动态选择的。在开发环境中,系统选择使用 consoleFormatter,在生产环境中使用 jsonFormatter。
export const logger = createLogger({
level: config.env.isDev ? 'debug' : 'info',
transports: [
new transports.Console({
format: config.logger.mode === 'inline-json' ? jsonFormatter : consoleFormatter,
}),
],
});
4.4 总结
通过 Winston 的 combine 方法,我们可以灵活地组合多个格式化器,根据不同环境需求输出适合的日志格式。开发环境中,我们使用彩色控制台日志,便于调试和实时查看;而在生产环境中,使用结构化的 JSON 日志,便于集成日志聚合系统并进行自动化分析。
5. 集成 AWS 服务
5.1 为什么集成 AWS 服务?
集成 AWS 服务可以将日志存储、分析和监控提升至云端,实现日志的聚合、搜索、过滤和自动化警报功能。
5.2 使用 Winston 集成 AWS CloudWatch Logs
通过 winston-cloudwatch 模块,Winston 可以将日志直接发送到 AWS CloudWatch Logs。以下步骤介绍如何在 Nuxt3 项目中集成 AWS CloudWatch,并将日志推送到云端。
1. 安装依赖
首先,安装 winston-cloudwatch 和 AWS SDK 依赖包:
npm install winston-cloudwatch @aws-sdk/client-cloudwatch-logs
2. 配置 AWS 认证信息
确保在 AWS 账户上创建并分配适当权限的 IAM 角色,或者通过环境变量配置 AWS 认证信息:
export AWS_ACCESS_KEY_ID=your-access-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-access-key
export AWS_REGION=your-region
也可以通过配置文件方式,设置 AWS 凭证和区域:
// ~/.aws/credentials
[default]
aws_access_key_id = your-access-key-id
aws_secret_access_key = your-secret-access-key
region = your-region
3. 创建 CloudWatch 日志传输器
在 logger.ts 文件中,添加对 winston-cloudwatch 的支持,并将日志推送到 CloudWatch:
import CloudWatchTransport from 'winston-cloudwatch';
export const logger = createLogger({
level: 'info',
format: combine(
timestamp(),
json()
),
transports: [
new transports.Console(), // 本地输出
new CloudWatchTransport({
logGroupName: 'your-log-group', // 替换为你的日志组名
logStreamName: 'your-log-stream', // 替换为日志流
awsRegion: 'your-region', // AWS 区域
messageFormatter: ({ level, message, ...meta }) => `[${level}] ${message} ${JSON.stringify(meta)}`
})
]
});
4. 在 CloudWatch 中查看日志
可以登录到 AWS CloudWatch Logs 控制台,找到指定的日志组和日志流,实时查看系统中生成的日志信息。
- 日志组名:
your-log-group - 日志流名:
your-log-stream
在 CloudWatch 中,你可以搜索、过滤日志信息,还可以设置告警规则,根据特定的日志条件触发通知或自动化响应。
5.3 使用 Winston 将日志上传到 S3
除了 CloudWatch,AWS S3 也是一个非常适合存储历史日志的云服务。可以将 Winston 生成的日志定期上传到 S3 进行长期存储和归档。
1. 安装 AWS SDK
使用 AWS SDK 访问 S3 服务:
npm install @aws-sdk/client-s3
2. 创建 S3 日志上传逻辑
在 logger.ts 文件中,可以自定义一个传输,将日志定期或按需上传到 S3:
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const s3Client = new S3Client({ region: 'your-region' });
const uploadToS3 = (logData) => {
const params = {
Bucket: 'your-bucket-name',
Key: `logs/${new Date().toISOString()}.log`, // 每条日志生成唯一的文件名
Body: logData,
ContentType: 'application/json'
};
return s3Client.send(new PutObjectCommand(params));
};
const s3Transport = new transports.Stream({
stream: {
write: (log) => {
uploadToS3(log)
.then(() => console.log('Log uploaded to S3'))
.catch(err => console.error('S3 Upload Error:', err));
}
}
});
export const logger = createLogger({
level: 'info',
format: json(), // 使用 JSON 格式
transports: [s3Transport] // 上传到 S3
});
3. 在 S3 中查看日志
在 AWS S3 控制台,进入指定的 Bucket,你可以查看日志被上传到相应的 logs 目录下。每次日志会按照时间戳生成一个唯一的文件名,便于长期存储和检索。
5.4 总结
通过集成 AWS CloudWatch 和 S3,可以将本地日志的管理扩展到云端,提供更强大的日志分析、监控和存储能力。使用 Winston 与 AWS 服务的结合,能够确保日志在分布式环境中实现集中化管理,并且方便追踪和调试应用的运行状况。