refactor(docs): reorganize documentation directory structure
- Create user/ - User guides and configuration documentation - Create features/ - Feature specifications and business flows - Create debugging/ - Debug guides and quick references - Create testing/ - Test infrastructure, reports, and plans - Create internal/ - Internal plans, analyses, and templates - Move cleaner/*.md to cleaner/ directory - Move LOGGING_*.md to developer/guides/ Add docs/README.md as documentation index with category navigation and quick lookup guide. The reorganized structure makes it easier for users and developers to quickly locate relevant documentation.
This commit is contained in:
547
docs/developer/guides/LOGGING_GUIDE.md
Normal file
547
docs/developer/guides/LOGGING_GUIDE.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# ERPAuto 日志查询指南
|
||||
|
||||
## 概述
|
||||
|
||||
> 本指南用于帮助运维和开发人员使用日志系统快速排查问题。
|
||||
>
|
||||
> **P0 升级**:日志系统已增强 requestId 追踪、性能监控、完整错误上下文。
|
||||
|
||||
---
|
||||
|
||||
## 日志字段说明
|
||||
|
||||
### 新增核心字段(P0 升级)
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
| --------------- | ------- | ------------------------- | ---------------------------------------- |
|
||||
| `requestId` | string | 请求唯一标识符(UUID v4) | `"f833980c-7b11-4c13-9c39-7c8890eb8b2f"` |
|
||||
| `userId` | string | 执行操作的用户 ID | `"admin"` |
|
||||
| `operation` | string | 操作类型 | `"extract"`, `"clean"`, `"validate"` |
|
||||
| `duration` | number | 操作耗时(毫秒) | `1523` |
|
||||
| `slow` | boolean | 是否为慢操作(> 阈值) | `true` |
|
||||
| `batchId` | string | 批次 ID | `"B20260404-001"` |
|
||||
| `tableName` | string | 数据库表名 | `"DiscreteMaterialPlan"` |
|
||||
| `operationType` | string | 数据库操作类型 | `"INSERT"`, `"DELETE"`, `"UPDATE"` |
|
||||
| `recordCount` | number | 记录数 | `150` |
|
||||
| `fileSize` | number | 文件大小(字节) | `1048576` |
|
||||
|
||||
### 业务上下文字段
|
||||
|
||||
| 字段 | 场景 | 说明 |
|
||||
| ------------------------ | ----------------- | ---------------------------------- |
|
||||
| `orderNumbers` | Extractor/Cleaner | 订单号列表 |
|
||||
| `materialCodes` | Cleaner | 物料代码列表 |
|
||||
| `downloadDir` | Extractor | 下载目录路径 |
|
||||
| `dryRun` | Cleaner | 是否为干运行模式 |
|
||||
| `mode` | Validation | 验证模式(`database_filtered` 等) |
|
||||
| `useSharedProductionIds` | Validation | 是否使用共享 Production ID |
|
||||
| `configPath` | Config | 配置文件路径 |
|
||||
| `isDev` | Config | 是否为开发环境 |
|
||||
| `version` | Update | 应用版本号 |
|
||||
| `channel` | Update | 更新通道(`stable`/`preview`) |
|
||||
|
||||
---
|
||||
|
||||
## 日志查询工具与脚本
|
||||
|
||||
### PowerShell 查询脚本
|
||||
|
||||
#### 1. 按 requestId 追踪完整请求链路
|
||||
|
||||
```powershell
|
||||
# 查找特定 requestId 的所有日志
|
||||
$requestId = "f833980c-7b11-4c13-9c39-7c8890eb8b2f"
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.requestId -eq $requestId } |
|
||||
Sort-Object timestamp |
|
||||
Format-Table timestamp, level, message, context -AutoSize
|
||||
```
|
||||
|
||||
**用途**:完整追踪一个请求的所有操作
|
||||
|
||||
---
|
||||
|
||||
#### 2. 查找慢操作(> 2 秒)
|
||||
|
||||
```powershell
|
||||
# 查找所有慢操作
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.duration -gt 2000 } |
|
||||
Format-Table timestamp, operation, duration, message -AutoSize
|
||||
```
|
||||
|
||||
**用途**:识别性能瓶颈
|
||||
|
||||
---
|
||||
|
||||
#### 3. 查找特定用户的所有操作
|
||||
|
||||
```powershell
|
||||
# 按 userId 筛选日志
|
||||
$userId = "admin"
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.userId -eq $userId } |
|
||||
Sort-Object timestamp |
|
||||
Format-Table timestamp, operation, level, message -AutoSize
|
||||
```
|
||||
|
||||
**用途**:审计用户操作
|
||||
|
||||
---
|
||||
|
||||
#### 4. 查找特定时间段内的错误
|
||||
|
||||
```powershell
|
||||
# 查找最近 1 小时的错误
|
||||
$startTime = (Get-Date).AddHours(-1)
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { [datetime]::Parse($_.timestamp) -gt $startTime } |
|
||||
Format-Table timestamp, message, error -AutoSize
|
||||
```
|
||||
|
||||
**用途**:故障排查
|
||||
|
||||
---
|
||||
|
||||
#### 5. 按 operation 统计操作频率
|
||||
|
||||
```powershell
|
||||
# 统计各 operation 的执行次数
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.operation } |
|
||||
Group-Object operation |
|
||||
Sort-Object Count -Descending |
|
||||
Format-Table Name, Count -AutoSize
|
||||
```
|
||||
|
||||
**用途**:了解系统使用情况
|
||||
|
||||
---
|
||||
|
||||
### Linux/Mac Bash 查询
|
||||
|
||||
```bash
|
||||
# 按 requestId 过滤
|
||||
cat app-*.log | jq 'select(.requestId == "f833980c-7b11-4c13-9c39-7c8890eb8b2f")'
|
||||
|
||||
# 查找错误日志
|
||||
cat error-*.log | jq '.'
|
||||
|
||||
# 查找慢操作
|
||||
cat app-*.log | jq 'select(.duration > 2000)'
|
||||
|
||||
# 统计 operation 频率
|
||||
cat app-*.log | jq -r '.operation' | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见故障排查场景
|
||||
|
||||
### 场景 1:数据提取失败
|
||||
|
||||
**症状**:用户报告 "提取任务失败"
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[用户报告提取失败] --> B[定位 requestId]
|
||||
B --> C[查看完整请求链路]
|
||||
C --> D{错误类型?}
|
||||
D -->|网络错误 | E[检查 ERP 连接]
|
||||
D -->|数据库错误 | F[检查数据库连接]
|
||||
D -->|文件错误 | G[检查文件权限]
|
||||
E --> H[修复网络问题]
|
||||
F --> H
|
||||
G --> H
|
||||
H --> I[重新执行提取]
|
||||
```
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 找到提取相关的错误日志
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.operation -eq "extract" -and $_.message -like "*失败*" } |
|
||||
Format-List timestamp, requestId, error, orderNumbers
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `operation: "extract"`的日志
|
||||
2. 提取`requestId`用于全链路追踪
|
||||
3. 检查`error`字段的具体错误信息
|
||||
4. 查看`orderNumbers`确定哪些订单失败
|
||||
|
||||
---
|
||||
|
||||
### 场景 2:物料清理执行缓慢
|
||||
|
||||
**症状**:用户报告 "清理任务太慢"
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[清理缓慢报告] --> B[查找慢操作]
|
||||
B --> C{哪个阶段慢?}
|
||||
C -->|批量处理 | D[检查订单数量/物料数量]
|
||||
C -->|重试操作 | E[检查 ERP 响应时间]
|
||||
C -->|数据库操作 | F[检查数据库性能]
|
||||
D --> G[优化批量大小]
|
||||
E --> G
|
||||
F --> G
|
||||
```
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找清理相关的慢操作
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.operation -eq "cleaner" -and $_.duration -gt 5000 } |
|
||||
Format-List timestamp, requestId, duration, slow, totalOrders, totalMaterials
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `duration > 5000ms` 的清理操作
|
||||
2. 检查`totalOrders`和`totalMaterials` 确认数据量
|
||||
3. 查看 `slow: true` 的批处理日志
|
||||
|
||||
---
|
||||
|
||||
### 场景 3:登录失败
|
||||
|
||||
**症状**:用户无法登录
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[登录失败] --> B[查找认证错误]
|
||||
B --> C{错误类型?}
|
||||
C -->|凭证错误 | D[检查用户名/密码]
|
||||
C -->|ERP 连接错误 | E[检查 ERP 服务状态]
|
||||
C -->|会话错误 | F[检查会话管理]
|
||||
D --> G[修正登录信息]
|
||||
E --> G
|
||||
F --> G
|
||||
```
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找认证相关的错误
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.userId -eq "admin" -and $_.message -like "*login*" } |
|
||||
Format-List timestamp, requestId, error, userId, username
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `operation: "login"`或`message` 包含"login"的日志
|
||||
2. 检查 `userId` 和`username`
|
||||
3. 查看`error`字段的具体错误信息
|
||||
|
||||
---
|
||||
|
||||
### 场景 4:数据库插入失败
|
||||
|
||||
**症状**:数据无法保存到数据库
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[数据库插入失败] --> B[查找数据库错误]
|
||||
B --> C{错误类型?}
|
||||
C -->|连接错误 | D[检查数据库服务]
|
||||
C -->|SQL 语法错误 | E[检查 SQL 语句]
|
||||
C -->|约束错误 | F[检查数据完整性]
|
||||
D --> G[修复数据库问题]
|
||||
E --> G
|
||||
F --> G
|
||||
```
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找数据库相关的错误
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.operationType -eq "INSERT" } |
|
||||
Format-List timestamp, requestId, operationType, tableName, error
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `operationType: "INSERT"`的日志
|
||||
2. 检查`tableName` 确定哪个表失败
|
||||
3. 查看`error`字段的具体错误信息
|
||||
|
||||
---
|
||||
|
||||
### 场景 5:配置文件读取失败
|
||||
|
||||
**症状**:应用启动失败,提示配置错误
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[配置读取失败] --> B[查找配置相关错误]
|
||||
B --> C{错误类型?}
|
||||
C -->|文件不存在 | D[检查配置文件路径]
|
||||
C -->|解析错误 | E[检查 YAML 格式]
|
||||
C -->|验证错误 | F[检查配置字段]
|
||||
D --> G[修复配置问题]
|
||||
E --> G
|
||||
F --> G
|
||||
```
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找配置相关的错误
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.configPath } |
|
||||
Format-List timestamp, requestId, configPath, isDev, error
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `configPath` 字段的日志
|
||||
2. 检查 `isDev` 确定环境(开发/生产)
|
||||
3. 查看`error`字段的具体错误信息
|
||||
|
||||
---
|
||||
|
||||
### 场景 6:文件上传失败
|
||||
|
||||
**症状**:文件无法上传到 RustFS
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找上传相关的错误
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\error-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.fileSize -or $_.message -like "*upload*" } |
|
||||
Format-List timestamp, requestId, fileSize, endpoint, bucket, error
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `fileSize` 字段的日志(表示文件操作)
|
||||
2. 检查 `endpoint`和`bucket` 配置
|
||||
3. 查看`error`字段的具体错误信息
|
||||
|
||||
---
|
||||
|
||||
### 场景 7:验证任务无数据返回
|
||||
|
||||
**症状**:验证任务执行成功但无数据
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
**日志查询**:
|
||||
|
||||
```powershell
|
||||
# 1. 查找验证相关的日志
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.operation -eq "validate" } |
|
||||
Format-List timestamp, requestId, mode, useSharedProductionIds, recordCount
|
||||
```
|
||||
|
||||
**排查要点**:
|
||||
|
||||
1. 查找 `operation: "validate"`的日志
|
||||
2. 检查 `mode`字段(数据来源)
|
||||
3. 查看`useSharedProductionIds`和`recordCount`
|
||||
|
||||
---
|
||||
|
||||
## 日志最佳实践
|
||||
|
||||
### 1. 开发环境 vs 生产环境
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[日志级别配置] --> B{环境?}
|
||||
B -->|开发 | C[DEBUG 级别<br/>详细信息]
|
||||
B -->|生产 | D[INFO 级别<br/>业务操作]
|
||||
C --> E[调试问题]
|
||||
D --> F[监控运行]
|
||||
```
|
||||
|
||||
**配置示例**:
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
logging:
|
||||
level: debug # 开发环境
|
||||
# level: info # 生产环境
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 敏感信息保护
|
||||
|
||||
**永远不要记录**:
|
||||
|
||||
- ❌ 密码
|
||||
- ❌ Token/密钥
|
||||
- ❌ 数据库连接字符串
|
||||
- ❌ 用户个人信息
|
||||
|
||||
**正确做法**:
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:记录敏感信息
|
||||
log.error('Login failed', { password: userPassword })
|
||||
|
||||
// ✅ 正确:使用脱敏信息
|
||||
log.error('Login failed', {
|
||||
userId: 'admin',
|
||||
reason: 'invalid_credentials' // 仅记录原因
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 错误日志应该包含
|
||||
|
||||
**完整上下文**:
|
||||
|
||||
```typescript
|
||||
log.error('Database insert failed', {
|
||||
requestId: getRequestId(), // 自动注入
|
||||
operation: 'insert-materials',
|
||||
userId: 'admin',
|
||||
tableName: 'DiscreteMaterialPlan',
|
||||
recordCount: 150,
|
||||
error: error.message,
|
||||
orderNumbers: ['SO001', 'SO002']
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 性能监控
|
||||
|
||||
**关键指标**:
|
||||
|
||||
- `duration > 1000ms`:一般警告
|
||||
- `duration > 5000ms`:严重警告
|
||||
- `duration > 10000ms`:需要立即调查
|
||||
|
||||
**监控脚本**:
|
||||
|
||||
```powershell
|
||||
# 每小时生成性能报告
|
||||
Get-Content "C:\Users\pengq\AppData\Roaming\erpauto\logs\app-*.log" |
|
||||
ConvertFrom-Json |
|
||||
Where-Object { $_.duration -gt 1000 } |
|
||||
Group-Object operation |
|
||||
ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
Operation = $_.Name
|
||||
SlowOperations = $_.Count
|
||||
AvgDuration = [math]::Round(($_.Group | Measure-Object duration -Average).Average, 2)
|
||||
MaxDuration = [math]::Round(($_.Group | Measure-Object duration -Maximum).Maximum, 2)
|
||||
}
|
||||
} | Format-Table -AutoSize
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 日志文件管理
|
||||
|
||||
### 文件位置
|
||||
|
||||
| 环境 | 路径 |
|
||||
| -------- | ------------------------------------------------- |
|
||||
| **开发** | `D:\FileLib\Projects\CodeMigration\ERPAuto\logs\` |
|
||||
| **生产** | `C:\Users\<user>\AppData\Roaming\erpauto\logs\` |
|
||||
|
||||
### 文件命名
|
||||
|
||||
| 类型 | 命名格式 | 说明 |
|
||||
| -------- | ------------------------ | ------------------ |
|
||||
| 应用日志 | `app-YYYY-MM-DD.log` | 所有业务日志 |
|
||||
| 错误日志 | `error-YYYY-MM-DD.log` | 仅错误级别日志 |
|
||||
| 审计日志 | `audit-YYYY-MM-DD.jsonl` | 用户操作审计 |
|
||||
| 压缩归档 | `*.log.gz` | 超过保留期限的日志 |
|
||||
|
||||
### 保留策略
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
logging:
|
||||
appRetention: 14 # 应用日志保留 14 天
|
||||
auditRetention: 30 # 审计日志保留 30 天
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查流程图
|
||||
|
||||
### 通用排查流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[收到故障报告] --> B[确定故障类型]
|
||||
B --> C{故障类型?}
|
||||
C -->|功能错误 | D[查找相关 error 日志]
|
||||
C -->|性能问题 | E[查找慢操作日志]
|
||||
C -->|数据问题 | F[查找数据操作日志]
|
||||
D --> G[定位 requestId]
|
||||
E --> G
|
||||
F --> G
|
||||
G --> H[追踪完整请求链路]
|
||||
H --> I[分析错误根因]
|
||||
I --> J[制定修复方案]
|
||||
J --> K[执行修复]
|
||||
K --> L[验证修复效果]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
### 快速参考
|
||||
|
||||
| 需求 | 查询字段 |
|
||||
| ---------- | ------------------------ |
|
||||
| 完整追踪 | `requestId` |
|
||||
| 性能排查 | `duration`, `slow` |
|
||||
| 用户审计 | `userId` |
|
||||
| 错误分析 | `error`, `operationType` |
|
||||
| 数据库问题 | `tableName`, `records` |
|
||||
| 文件问题 | `fileSize`, `filePath` |
|
||||
|
||||
### 联系支持
|
||||
|
||||
如遇日志相关问题,请联系技术支持团队并提供:
|
||||
|
||||
1. 故障时间段
|
||||
2. 相关 `requestId`
|
||||
3. 错误日志内容
|
||||
|
||||
---
|
||||
|
||||
_文档版本:P0 Enhanced Logging_
|
||||
_更新日期:2026-04-04_
|
||||
752
docs/developer/guides/LOGGING_IMPLEMENTATION.md
Normal file
752
docs/developer/guides/LOGGING_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,752 @@
|
||||
# ERPAuto 日志系统实现文档
|
||||
|
||||
## 概述
|
||||
|
||||
ERPAuto 使用 **Winston** 作为核心日志库,实现了统一的主进程 - 渲染进程日志系统。系统支持日志级别管理、文件轮转、审计日志、错误全链路追踪等功能。
|
||||
|
||||
---
|
||||
|
||||
## 架构总览
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Renderer Process
|
||||
RC[React Components]
|
||||
UH[useLogger Hook]
|
||||
LA[Logger API]
|
||||
end
|
||||
|
||||
subgraph Preload Layer
|
||||
PL[Preload Bridge]
|
||||
LC[Level Cache]
|
||||
end
|
||||
|
||||
subgraph Main Process
|
||||
LH[Logger Handler]
|
||||
IL[IPC Router]
|
||||
WL[Winston Logger]
|
||||
FT[File Transports]
|
||||
CT[Console Transport]
|
||||
AL[Audit Logger]
|
||||
end
|
||||
|
||||
subgraph Storage
|
||||
ALF[app-YYYY-MM-DD.log]
|
||||
ELF[error-YYYY-MM-DD.log]
|
||||
AUF[audit-YYYY-MM-DD.jsonl]
|
||||
end
|
||||
|
||||
RC --> UH
|
||||
UH --> LA
|
||||
LA --> LC
|
||||
LC -->|IPC Send| PL
|
||||
PL -->|logger:forward| IL
|
||||
IL --> LH
|
||||
LH --> WL
|
||||
WL --> CT
|
||||
WL --> FT
|
||||
FT --> ALF
|
||||
FT --> ELF
|
||||
AL --> AUF
|
||||
|
||||
style WL fill:#f9f,stroke:#333
|
||||
style LH fill:#bbf,stroke:#333
|
||||
style AL fill:#bfb,stroke:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 1. 主进程日志服务 (`src/main/services/logger/`)
|
||||
|
||||
#### 1.1 核心日志器 (`index.ts`)
|
||||
|
||||
```typescript
|
||||
// 日志器创建与配置
|
||||
import winston from 'winston'
|
||||
import DailyRotateFile from 'winston-daily-rotate-file'
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: 'info',
|
||||
defaultMeta: { service: 'erpauto' },
|
||||
transports: [new winston.transports.Console({ format: consoleFormat })]
|
||||
})
|
||||
```
|
||||
|
||||
**关键特性:**
|
||||
|
||||
- **双格式输出**:控制台(彩色文本)+ 文件(JSON)
|
||||
- **每日轮转**:日志文件按日期拆分,自动压缩归档
|
||||
- **错误序列化**:完整捕获 stack trace 和自定义属性
|
||||
- **环境感知**:生产环境自动脱敏敏感信息
|
||||
|
||||
#### 1.2 日志级别与优先级
|
||||
|
||||
```typescript
|
||||
export type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose'
|
||||
|
||||
export const LOG_LEVEL_PRIORITY: Record<string, number> = {
|
||||
verbose: 0,
|
||||
debug: 1,
|
||||
info: 2,
|
||||
warn: 3,
|
||||
error: 4
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 错误工具类 (`error-utils.ts`)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Error Occurs] --> B{Error Type?}
|
||||
B -->|Error Instance| C[serializeError]
|
||||
B -->|Error-like| C
|
||||
B -->|Other| D[Wrap as UnknownError]
|
||||
C --> E{Production?}
|
||||
D --> E
|
||||
E -->|Yes| F[sanitizeError]
|
||||
E -->|No| G[Keep Full Details]
|
||||
F --> H[Redact Sensitive Keys]
|
||||
G --> I[Preserve Stack Trace]
|
||||
H --> J[Log Output]
|
||||
I --> J
|
||||
```
|
||||
|
||||
**序列化流程:**
|
||||
|
||||
1. 捕获所有 enumerable 和 non-enumerable 属性
|
||||
2. 递归处理 error cause 链
|
||||
3. 生产环境脱敏 password/token/secret 等敏感字段
|
||||
4. 提取堆栈中的文件/行号/列号信息
|
||||
|
||||
---
|
||||
|
||||
### 2. 审计日志服务 (`audit-logger.ts`)
|
||||
|
||||
**用途**:记录用户操作审计日志,满足合规要求
|
||||
|
||||
```typescript
|
||||
interface AuditEntry {
|
||||
timestamp: string // ISO 8601 时间戳
|
||||
action: string // 操作类型:LOGIN, EXTRACT, DELETE
|
||||
userId: string // 用户 ID
|
||||
username: string // 用户名
|
||||
computerName: string // 计算机名
|
||||
resource: string // 受影响的资源
|
||||
status: 'success' | 'failure' | 'partial'
|
||||
metadata: Record<string, unknown>
|
||||
}
|
||||
```
|
||||
|
||||
**格式特点:**
|
||||
|
||||
- **JSONL 格式**:每行一个 JSON 对象,便于流式解析
|
||||
- **30 天轮转**:默认保留 30 天审计日志
|
||||
- **独立文件**:`audit-YYYY-MM-DD.jsonl`
|
||||
|
||||
---
|
||||
|
||||
### 3. IPC 日志处理器 (`src/main/ipc/logger-handler.ts`)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R as Renderer
|
||||
participant B as Buffer State
|
||||
participant W as Winston
|
||||
participant F as File
|
||||
|
||||
R->>B: Send Log Entry
|
||||
Note over B: Circuit Breaker Check
|
||||
alt Error Level
|
||||
B->>B: Always Buffer
|
||||
else Non-Error & Buffer < 500
|
||||
B->>B: Buffer Entry
|
||||
else Buffer >= 500
|
||||
B->>B: Discard + Count
|
||||
end
|
||||
|
||||
Note over B: Batch Processing
|
||||
B->>B: 100ms Debounce OR 50 entries
|
||||
B->>W: Flush Batch
|
||||
W->>F: Write to File
|
||||
```
|
||||
|
||||
**批处理策略:**
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| `DEBOUNCE_MS` | 100ms | 防抖等待时间 |
|
||||
| `MAX_BATCH_SIZE` | 50 | 最大批次大小 |
|
||||
| `CIRCUIT_BREAKER_THRESHOLD` | 500 | 熔断阈值 |
|
||||
|
||||
**熔断机制:**
|
||||
|
||||
- 当缓冲区 > 500 条时,丢弃非错误日志
|
||||
- 错误日志始终绕过熔断器
|
||||
- 每丢弃 100 条记录一次警告
|
||||
|
||||
---
|
||||
|
||||
### 4. 渲染进程日志 Hook (`src/renderer/src/hooks/useLogger.ts`)
|
||||
|
||||
```typescript
|
||||
// 使用示例
|
||||
function MyComponent() {
|
||||
const logger = useLogger('MyComponent')
|
||||
|
||||
const handleClick = () => {
|
||||
logger.info('User clicked button', { buttonId: 'submit' })
|
||||
}
|
||||
|
||||
const handleError = (err: Error) => {
|
||||
logger.error('Operation failed', { error: err.message })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**客户端级别过滤:**
|
||||
|
||||
```typescript
|
||||
// 在发送 IPC 前检查日志级别,避免无效 IPC 调用
|
||||
if (!shouldLog(level)) return
|
||||
ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, { ... })
|
||||
```
|
||||
|
||||
**FPS 监控:**
|
||||
|
||||
- 检测因过度日志导致的 UI 卡顿
|
||||
- 当 FPS < 30 时发出警告
|
||||
- 5 秒冷却期避免重复警告
|
||||
|
||||
---
|
||||
|
||||
### 5. 预加载层 API (`src/preload/api/logger.ts`)
|
||||
|
||||
```typescript
|
||||
// 级别缓存机制
|
||||
let cachedLevel: LogLevel = 'info'
|
||||
|
||||
// 监听主进程级别变更广播
|
||||
ipcRenderer.on(IPC_CHANNELS.LOGGER_LEVEL_CHANGED, (level) => {
|
||||
cachedLevel = level
|
||||
})
|
||||
|
||||
// 客户端过滤
|
||||
function shouldLog(level: LogLevel): boolean {
|
||||
return priorities[level] >= priorities[cachedLevel]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 配置管理 (`src/main/services/config/config-manager.ts`)
|
||||
|
||||
```yaml
|
||||
# config.yaml 配置示例
|
||||
logging:
|
||||
level: info # 日志级别
|
||||
auditRetention: 30 # 审计日志保留天数
|
||||
appRetention: 14 # 应用日志保留天数
|
||||
```
|
||||
|
||||
**配置加载时机:**
|
||||
|
||||
1. 应用启动时加载 `config.yaml`
|
||||
2. 调用 `applyLoggingConfig()` 配置 Winston
|
||||
3. 调用 `applyAuditConfig()` 配置审计日志
|
||||
|
||||
---
|
||||
|
||||
## 日志数据流
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 渲染进程
|
||||
A[Component] --> B[useLogger Hook]
|
||||
B --> C{Level Check}
|
||||
C -->|Pass| D[loggerApi.log]
|
||||
C -->|Skip| E[Drop]
|
||||
end
|
||||
|
||||
subgraph IPC 传输
|
||||
D --> F[logger:forward]
|
||||
F --> G[Context Bridge]
|
||||
end
|
||||
|
||||
subgraph 主进程
|
||||
G --> H[Logger Handler]
|
||||
H --> I{Circuit Breaker}
|
||||
I -->|Pass| J[Batch Buffer]
|
||||
I -->|Block| K[Discard Counter]
|
||||
J --> L{Debounce Timer}
|
||||
L -->|100ms| M[Flush to Winston]
|
||||
J -->|50 entries| M
|
||||
end
|
||||
|
||||
subgraph Winston
|
||||
M --> N[Console Transport]
|
||||
M --> O[File Transport]
|
||||
O --> P{Error Level?}
|
||||
P -->|Yes| Q[error-DATE.log]
|
||||
P -->|All| R[app-DATE.log]
|
||||
end
|
||||
|
||||
subgraph 审计日志
|
||||
S[logAudit] --> T[Audit Logger]
|
||||
T --> U[audit-DATE.jsonl]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 日志文件组织
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
AppData/Roaming/erpauto/logs/
|
||||
├── app-2024-04-01.log
|
||||
├── app-2024-04-01.log.gz # 压缩归档
|
||||
├── app-2024-04-02.log
|
||||
├── error-2024-04-01.log # 仅错误级别
|
||||
├── error-2024-04-01.log.gz
|
||||
├── audit-2024-04-01.jsonl # 审计日志
|
||||
└── audit-2024-04-01.jsonl.gz
|
||||
```
|
||||
|
||||
### 文件格式
|
||||
|
||||
**应用日志 (JSON 格式):**
|
||||
|
||||
```json
|
||||
{
|
||||
"level": "info",
|
||||
"message": "Extractor started",
|
||||
"timestamp": "2024-04-01 10:30:00",
|
||||
"service": "erpauto",
|
||||
"context": "Extractor",
|
||||
"orders": ["SO001", "SO002"]
|
||||
}
|
||||
```
|
||||
|
||||
**错误日志 (含堆栈):**
|
||||
|
||||
```json
|
||||
{
|
||||
"level": "error",
|
||||
"message": "Database connection failed",
|
||||
"timestamp": "2024-04-01 10:31:00",
|
||||
"error": {
|
||||
"name": "ConnectionError",
|
||||
"message": "ECONNREFUSED",
|
||||
"stack": "ConnectionError: ECONNREFUSED\n at TCP.connectWrap (...)",
|
||||
"code": "ECONNREFUSED"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**审计日志 (JSONL 格式):**
|
||||
|
||||
```jsonl
|
||||
{"timestamp":"2024-04-01T10:30:00Z","action":"LOGIN","userId":"1","username":"admin","computerName":"DESKTOP-001","resource":"/auth","status":"success","metadata":{}}
|
||||
{"timestamp":"2024-04-01T10:35:00Z","action":"EXTRACT","userId":"1","username":"admin","computerName":"DESKTOP-001","resource":"orders","status":"success","metadata":{"orderCount":50}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## IPC 通道定义
|
||||
|
||||
```typescript
|
||||
// src/shared/ipc-channels.ts
|
||||
export const IPC_CHANNELS = {
|
||||
// 日志转发(renderer → main)
|
||||
LOGGER_FORWARD: 'logger:forward',
|
||||
|
||||
// 获取当前日志级别
|
||||
LOGGER_GET_LEVEL: 'logger:getLevel',
|
||||
|
||||
// 级别变更广播(main → renderer)
|
||||
LOGGER_LEVEL_CHANGED: 'logger:levelChanged'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 在主进程中记录日志
|
||||
|
||||
```typescript
|
||||
import { createLogger } from '@/main/services/logger'
|
||||
|
||||
const log = createLogger('MyService')
|
||||
|
||||
// 基础用法
|
||||
log.info('Operation started')
|
||||
log.warn('Disk space low')
|
||||
log.error('Failed to connect', { error: err })
|
||||
|
||||
// 带上下文的日志
|
||||
log.info('Processing batch', {
|
||||
batchId: 'B001',
|
||||
itemCount: 100,
|
||||
estimatedTime: '5min'
|
||||
})
|
||||
|
||||
// 错误日志(自动序列化堆栈)
|
||||
try {
|
||||
await riskyOperation()
|
||||
} catch (error) {
|
||||
log.error('Operation failed', { error })
|
||||
}
|
||||
```
|
||||
|
||||
### 在渲染进程中记录日志
|
||||
|
||||
```typescript
|
||||
import { useLogger } from '@/renderer/src/hooks/useLogger'
|
||||
|
||||
function MyComponent() {
|
||||
const logger = useLogger('MyComponent')
|
||||
|
||||
useEffect(() => {
|
||||
logger.info('Component mounted')
|
||||
return () => logger.debug('Component unmounted')
|
||||
}, [])
|
||||
|
||||
const handleAction = async () => {
|
||||
try {
|
||||
await api.doSomething()
|
||||
logger.info('Action succeeded')
|
||||
} catch (err) {
|
||||
logger.error('Action failed', { error: err.message })
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 记录审计日志
|
||||
|
||||
```typescript
|
||||
import { logAudit } from '@/main/services/logger/audit-logger'
|
||||
|
||||
// 用户登录审计
|
||||
logAudit('LOGIN', userId, {
|
||||
username: 'admin',
|
||||
computerName: 'DESKTOP-001',
|
||||
resource: '/auth',
|
||||
status: 'success',
|
||||
metadata: { loginMethod: 'password' }
|
||||
})
|
||||
|
||||
// 数据提取审计
|
||||
logAudit('EXTRACT', userId, {
|
||||
username: 'user1',
|
||||
computerName: 'DESKTOP-002',
|
||||
resource: 'materials',
|
||||
status: 'success',
|
||||
metadata: { orderCount: 50, materialCount: 1200 }
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 高级功能
|
||||
|
||||
### 1. 日志级别动态切换
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User (UI)
|
||||
participant C as ConfigManager
|
||||
participant M as Main Logger
|
||||
participant R as Renderer
|
||||
participant L as Level Cache
|
||||
|
||||
U->>C: Update logging.level
|
||||
C->>M: applyLoggingConfig(newLevel)
|
||||
M->>M: logger.level = newLevel
|
||||
M->>R: Broadcast levelChanged
|
||||
R->>L: cachedLevel = newLevel
|
||||
Note over L: Future logs filtered at client
|
||||
```
|
||||
|
||||
**代码示例:**
|
||||
|
||||
```typescript
|
||||
// 主进程设置级别
|
||||
import { setLogLevel } from '@/main/services/logger'
|
||||
setLogLevel('debug')
|
||||
|
||||
// 渲染进程自动同步
|
||||
// useLogger Hook 会自动接收级别变更广播
|
||||
// 客户端过滤自动生效
|
||||
```
|
||||
|
||||
### 2. 生产环境错误脱敏
|
||||
|
||||
```typescript
|
||||
// 自动脱敏以下关键字段
|
||||
const sensitiveKeys = [
|
||||
'password', 'secret', 'token', 'apiKey',
|
||||
'credentials', 'authorization', 'privateKey'
|
||||
]
|
||||
|
||||
// 生产环境错误消息
|
||||
{
|
||||
"name": "AuthError",
|
||||
"message": "An error occurred due to invalid credentials or configuration"
|
||||
// 原始错误消息被脱敏
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 错误上下文提取
|
||||
|
||||
```typescript
|
||||
// 从堆栈跟踪提取位置信息
|
||||
const errorContext = extractErrorContext(serializedError)
|
||||
// 输出:
|
||||
{
|
||||
fileName: 'extractor.ts',
|
||||
lineNumber: 142,
|
||||
columnName: 15,
|
||||
functionName: 'runExtraction'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
```typescript
|
||||
// 1. 使用 createLogger 创建带上下文的子日志器
|
||||
const log = createLogger('DatabaseService')
|
||||
|
||||
// 2. 记录错误时传递完整 Error 对象
|
||||
log.error('Query failed', { error })
|
||||
|
||||
// 3. 使用结构化元数据
|
||||
log.info('Batch processed', {
|
||||
batchId: 'B001',
|
||||
duration: 1250,
|
||||
itemCount: 100
|
||||
})
|
||||
|
||||
// 4. 渲染进程使用 useLogger Hook
|
||||
const logger = useLogger('LoginForm')
|
||||
|
||||
// 5. 敏感信息使用审计日志
|
||||
logAudit('DELETE', userId, { ... })
|
||||
```
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
```typescript
|
||||
// 1. 避免直接 console.log
|
||||
console.log('debug') // ❌ 不会被 Winston 捕获
|
||||
|
||||
// 2. 避免只记录错误消息
|
||||
log.error(err.message) // ❌ 丢失堆栈和类型
|
||||
|
||||
// 3. 避免循环引用元数据
|
||||
const obj: any = {}
|
||||
obj.self = obj
|
||||
log.info('test', { obj }) // ❌ 序列化失败
|
||||
|
||||
// 4. 避免过度日志
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
logger.info(`Item ${i}`) // ❌ 触发熔断
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题:日志文件不生成
|
||||
|
||||
**检查清单:**
|
||||
|
||||
1. 确认 `config.yaml` 中 logging 配置正确
|
||||
2. 检查日志目录权限
|
||||
3. 查看控制台输出是否有 Winston 错误
|
||||
4. 验证 `applyLoggingConfig()` 是否被调用
|
||||
|
||||
### 问题:渲染进程日志未到达主进程
|
||||
|
||||
**调试步骤:**
|
||||
|
||||
```typescript
|
||||
// 1. 检查 IPC 通道是否注册
|
||||
// src/main/ipc/index.ts 应包含:
|
||||
registerLoggerHandlers()
|
||||
|
||||
// 2. 检查 preload 暴露
|
||||
// src/preload/index.ts 应暴露:
|
||||
contextBridge.exposeInMainWorld('electron', api)
|
||||
|
||||
// 3. 检查级别过滤
|
||||
console.log(window.electron.logger) // 应存在
|
||||
```
|
||||
|
||||
### 问题:生产环境错误信息不完整
|
||||
|
||||
**原因**:生产环境自动脱敏
|
||||
**解决方案**:
|
||||
|
||||
- 查看 `error-DATE.log` 获取完整错误
|
||||
- 开发环境禁用脱敏:设置开发模式构建
|
||||
|
||||
---
|
||||
|
||||
## 测试支持
|
||||
|
||||
### 单元测试示例
|
||||
|
||||
```typescript
|
||||
import { createLogger } from '@/main/services/logger'
|
||||
|
||||
describe('Logger', () => {
|
||||
it('should log with context', () => {
|
||||
const log = createLogger('TestService')
|
||||
// 测试逻辑...
|
||||
expect(log).toBeDefined()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 集成测试
|
||||
|
||||
```typescript
|
||||
// tests/integration/ipc-logging.test.ts
|
||||
import { loggerApi } from '@/preload/api/logger'
|
||||
|
||||
test('Renderer logs should reach Winston', async () => {
|
||||
// Mock Winston transport
|
||||
// Send log via IPC
|
||||
// Assert log appears in main process
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置参考
|
||||
|
||||
### config.yaml 完整配置
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
# 日志级别:error | warn | info | debug | verbose
|
||||
level: info
|
||||
|
||||
# 审计日志保留天数
|
||||
auditRetention: 30
|
||||
|
||||
# 应用日志保留天数
|
||||
appRetention: 14
|
||||
```
|
||||
|
||||
### 日志级别说明
|
||||
|
||||
| 级别 | 使用场景 | 示例 |
|
||||
| --------- | -------------- | ---------------------------- |
|
||||
| `error` | 系统错误、异常 | 数据库连接失败、文件写入错误 |
|
||||
| `warn` | 可恢复的警告 | 磁盘空间不足、重试操作 |
|
||||
| `info` | 业务操作记录 | 用户登录、提取开始/结束 |
|
||||
| `debug` | 技术调试信息 | API 请求参数、SQL 语句 |
|
||||
| `verbose` | 详细跟踪 | 循环迭代、中间状态 |
|
||||
|
||||
---
|
||||
|
||||
## 相关文件索引
|
||||
|
||||
| 文件路径 | 职责 |
|
||||
| -------------------------------------------- | ------------------ |
|
||||
| `src/main/services/logger/index.ts` | Winston 日志器核心 |
|
||||
| `src/main/services/logger/shared.ts` | 共享工具函数 |
|
||||
| `src/main/services/logger/error-utils.ts` | 错误序列化/脱敏 |
|
||||
| `src/main/services/logger/audit-logger.ts` | 审计日志服务 |
|
||||
| `src/main/ipc/logger-handler.ts` | IPC 批处理与熔断 |
|
||||
| `src/renderer/src/hooks/useLogger.ts` | React Hook |
|
||||
| `src/preload/api/logger.ts` | Preload API |
|
||||
| `src/shared/ipc-channels.ts` | IPC 通道定义 |
|
||||
| `src/main/services/config/config-manager.ts` | 配置管理 |
|
||||
|
||||
---
|
||||
|
||||
## 架构图附录
|
||||
|
||||
### 完整日志系统架构
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph 渲染进程 Renderer
|
||||
UI[UI Components]
|
||||
HL[useLogger Hook]
|
||||
CF[Client Filter]
|
||||
LC[Level Cache]
|
||||
end
|
||||
|
||||
subgraph 预加载层 Preload
|
||||
CB[Context Bridge]
|
||||
IR[IPC Renderer]
|
||||
LA[Logger API]
|
||||
end
|
||||
|
||||
subgraph 主进程 Main
|
||||
IH[IPC Handler]
|
||||
BB[Batch Buffer]
|
||||
CB2[Circuit Breaker]
|
||||
WL[Winston Logger]
|
||||
AC[Audit Logger]
|
||||
CM[Config Manager]
|
||||
end
|
||||
|
||||
subgraph 传输层 Transports
|
||||
CT[Console]
|
||||
AFT[App File]
|
||||
EFT[Error File]
|
||||
ATF[Audit File]
|
||||
end
|
||||
|
||||
subgraph 文件系统 File System
|
||||
ALF[app-DATE.log]
|
||||
ELF[error-DATE.log]
|
||||
AUF[audit-DATE.jsonl]
|
||||
GZ[.gz Archive]
|
||||
end
|
||||
|
||||
UI --> HL
|
||||
HL --> CF
|
||||
CF --> LC
|
||||
LC --> LA
|
||||
LA --> IR
|
||||
IR --> CB
|
||||
CB --> IH
|
||||
IH --> CB2
|
||||
CB2 --> BB
|
||||
BB --> WL
|
||||
WL --> CT
|
||||
WL --> AFT
|
||||
WL --> EFT
|
||||
AC --> ATF
|
||||
CM --> WL
|
||||
AFT --> ALF
|
||||
EFT --> ELF
|
||||
ATF --> AUF
|
||||
ALF --> GZ
|
||||
ELF --> GZ
|
||||
AUF --> GZ
|
||||
|
||||
style WL fill:#f9f,stroke:#333
|
||||
style BB fill:#bbf,stroke:#333
|
||||
style CB2 fill:#fbb,stroke:#333
|
||||
style AC fill:#bfb,stroke:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
_文档生成日期:2026-04-04_
|
||||
_项目版本:ERPAuto v1.x_
|
||||
Reference in New Issue
Block a user