topfans/backend/docs/日志架构设计.md
2026-04-07 22:29:48 +08:00

458 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 微服务日志架构设计
## 一、设计原则
### 1.1 核心原则
1. **服务独立性**:每个微服务应该有自己独立的日志实例
2. **统一配置**:所有服务使用相同的日志配置和格式
3. **共享工具**:在共享包中提供统一的日志初始化逻辑
4. **分散部署**:每个服务可以独立配置日志级别和输出方式
### 1.2 架构选择
**推荐方案****每个服务独立共享logger包**
- ✅ 每个服务有独立的logger实例
- ✅ 所有服务共享 `pkg/logger` 包的初始化逻辑
- ✅ 统一的日志格式和配置方式
- ✅ 服务可以独立配置日志级别
---
## 二、架构设计
### 2.1 目录结构
```
backend/
├── pkg/
│ └── logger/
│ └── logger.go # 共享的日志工具包(统一配置)
├── services/
│ ├── userService/
│ │ ├── main.go # 初始化自己的logger实例
│ │ └── service/
│ │ └── auth_service.go # 使用logger记录日志
│ ├── assetService/
│ │ ├── main.go # 初始化自己的logger实例
│ │ └── service/
│ │ └── asset_service.go
│ ├── socialService/
│ │ ├── main.go # 初始化自己的logger实例
│ │ └── service/
│ │ └── social_service.go
│ └── ...
└── gateway/ # API Gateway
└── main.go # 初始化自己的logger实例
```
### 2.2 日志包设计
**文件**`pkg/logger/logger.go`
**设计要点**
- 提供统一的初始化函数 `Init()`
- 每个服务调用 `Init()` 创建自己的logger实例
- 支持服务级别的配置(服务名、环境等)
- 统一的日志格式JSON格式包含服务名
**实现方式**
```go
package logger
import (
"os"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
// Logger 全局logger实例每个服务独立
Logger *zap.Logger
// Sugar 全局Sugar实例
Sugar *zap.SugaredLogger
)
// Config 日志配置
type Config struct {
ServiceName string // 服务名称user-service, asset-service
Environment string // 环境development 或 production
LogLevel string // 日志级别debug, info, warn, error
LogPath string // 日志文件路径可选默认为当前目录下的logs/
}
// Init 初始化日志(每个服务独立调用)
func Init(config Config) error {
var zapConfig zap.Config
// 设置服务名作为默认字段
serviceNameField := zap.String("service", config.ServiceName)
// 环境判断
if config.Environment == "development" {
// 开发环境:彩色控制台输出
zapConfig = zap.NewDevelopmentConfig()
zapConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
} else {
// 生产环境JSON格式文件输出
zapConfig = zap.NewProductionConfig()
// 设置日志文件路径
logPath := config.LogPath
if logPath == "" {
logPath = "logs"
}
// 创建日志目录
if err := os.MkdirAll(logPath, 0755); err != nil {
return err
}
zapConfig.OutputPaths = []string{
"stdout",
logPath + "/" + config.ServiceName + ".log",
}
zapConfig.ErrorOutputPaths = []string{
"stderr",
logPath + "/" + config.ServiceName + "-error.log",
}
}
// 设置日志级别
logLevel := config.LogLevel
if logLevel == "" {
logLevel = os.Getenv("LOG_LEVEL")
if logLevel == "" {
logLevel = "info"
}
}
var level zapcore.Level
if err := level.UnmarshalText([]byte(logLevel)); err == nil {
zapConfig.Level = zap.NewAtomicLevelAt(level)
}
// 构建logger
var err error
Logger, err = zapConfig.Build(
zap.AddCaller(),
zap.AddStacktrace(zapcore.ErrorLevel),
zap.Fields(serviceNameField), // 添加服务名字段
)
if err != nil {
return err
}
// 创建Sugar实例
Sugar = Logger.Sugar()
return nil
}
// Sync 刷新日志缓冲区
func Sync() {
if Logger != nil {
_ = Logger.Sync()
}
}
// GetLogger 获取logger实例
func GetLogger() *zap.Logger {
return Logger
}
// GetSugar 获取Sugar实例
func GetSugar() *zap.SugaredLogger {
return Sugar
}
```
---
## 三、各服务使用方式
### 3.1 User Service用户与认证服务
**文件**`services/userService/main.go`
```go
package main
import (
"flag"
"os"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/logger"
)
func main() {
// 初始化日志(每个服务独立初始化)
env := os.Getenv("ENV")
if env == "" {
env = "development"
}
if err := logger.Init(logger.Config{
ServiceName: "user-service",
Environment: env,
LogLevel: os.Getenv("LOG_LEVEL"),
}); err != nil {
panic(err)
}
defer logger.Sync()
logger.Sugar.Info("Starting User Service...")
// 其他初始化代码...
}
```
**文件**`services/userService/service/auth_service.go`
```go
package service
import (
"github.com/topfans/backend/pkg/logger"
"go.uber.org/zap"
)
func (s *AuthService) Register(req *pb.RegisterRequest) (*pb.RegisterResponse, error) {
logger.Logger.Info("Register request received",
zap.String("mobile", req.Mobile),
zap.Int64("star_id", req.StarId),
)
// 业务逻辑...
}
```
### 3.2 Asset Service资产服务
**文件**`services/assetService/main.go`
```go
package main
import (
"github.com/topfans/backend/pkg/logger"
)
func main() {
// 初始化日志独立的logger实例
if err := logger.Init(logger.Config{
ServiceName: "asset-service",
Environment: os.Getenv("ENV"),
LogLevel: os.Getenv("LOG_LEVEL"),
}); err != nil {
panic(err)
}
defer logger.Sync()
logger.Sugar.Info("Starting Asset Service...")
// ...
}
```
### 3.3 API Gateway
**文件**`gateway/main.go`
```go
package main
import (
"github.com/topfans/backend/pkg/logger"
)
func main() {
// 初始化日志独立的logger实例
if err := logger.Init(logger.Config{
ServiceName: "api-gateway",
Environment: os.Getenv("ENV"),
LogLevel: os.Getenv("LOG_LEVEL"),
}); err != nil {
panic(err)
}
defer logger.Sync()
logger.Sugar.Info("Starting API Gateway...")
// ...
}
```
---
## 四、日志格式示例
### 4.1 开发环境(控制台)
```
2024-01-01T10:00:00.000+0800 INFO service=user-service Register request received {"mobile": "13800138000", "star_id": 123}
```
### 4.2 生产环境JSON格式
```json
{
"level": "info",
"ts": 1704067200.000,
"caller": "auth_service.go:45",
"msg": "Register request received",
"service": "user-service",
"mobile": "13800138000",
"star_id": 123
}
```
**优势**
- 每个日志都包含 `service` 字段,便于区分是哪个服务的日志
- JSON格式便于日志收集系统如ELK、Loki解析
- 统一的格式便于统一管理和查询
---
## 五、日志收集(可选,生产环境)
### 5.1 集中式日志收集架构
```
各个服务独立logger
日志文件(本地存储)
日志收集AgentFilebeat/Fluentd
日志聚合系统ELK Stack / Grafana Loki
可视化查询Kibana / Grafana
```
### 5.2 实现方式
1. **本地文件存储**:每个服务将日志写入本地文件
2. **日志收集Agent**使用Filebeat或Fluentd收集日志
3. **日志聚合**发送到Elasticsearch或Loki
4. **可视化查询**通过Kibana或Grafana查询
**优势**
- 各服务独立,不依赖外部系统
- 即使日志收集系统故障,本地仍有日志
- 统一的查询界面,可以跨服务查询
---
## 六、环境变量配置
### 6.1 各服务独立配置
每个服务可以通过环境变量独立配置:
```bash
# User Service
ENV=production
LOG_LEVEL=info
LOG_PATH=/var/log/user-service
# Asset Service
ENV=production
LOG_LEVEL=debug
LOG_PATH=/var/log/asset-service
# API Gateway
ENV=production
LOG_LEVEL=warn
LOG_PATH=/var/log/gateway
```
### 6.2 Docker Compose示例
```yaml
services:
user-service:
environment:
- ENV=production
- LOG_LEVEL=info
- LOG_PATH=/var/log
volumes:
- ./logs/user-service:/var/log
asset-service:
environment:
- ENV=production
- LOG_LEVEL=info
- LOG_PATH=/var/log
volumes:
- ./logs/asset-service:/var/log
```
---
## 七、最佳实践
### 7.1 日志级别建议
| 服务类型 | 推荐级别 | 说明 |
|---------|---------|------|
| API Gateway | `warn` | 只记录警告和错误 |
| User Service | `info` | 记录关键操作 |
| Asset Service | `info` | 记录关键操作 |
| Task Service | `debug` | 任务调度需要详细日志 |
### 7.2 日志记录原则
1. **每个服务独立初始化**:确保服务独立性
2. **统一的日志格式**:便于统一管理和查询
3. **包含服务名**:每条日志都标识所属服务
4. **结构化日志**:使用字段而不是字符串拼接
5. **敏感信息不记录**密码、完整Token等
### 7.3 性能考虑
- 生产环境使用结构化日志(`Logger.Info`而不是Sugar API
- 合理设置日志级别,避免过多日志影响性能
- 使用日志轮转,避免日志文件过大
---
## 八、总结
### 推荐方案
**每个服务独立共享logger包**
**优势**
1. **服务独立性**:每个服务可以独立部署和配置
2. **统一格式**:所有服务使用相同的日志格式
3. **易于管理**共享的logger包便于维护和升级
4. **灵活配置**:每个服务可以独立配置日志级别
5. **便于扩展**:后续可以轻松添加日志收集功能
**不推荐**:统一管理所有服务的日志实例
**原因**
- 破坏了服务的独立性
- 难以在不同服务中使用不同的日志配置
- 服务间耦合度增加
---
## 九、实施步骤
1. **第一步**:创建共享的 `pkg/logger/logger.go`
2. **第二步**:在每个服务的 `main.go` 中初始化logger
3. **第三步**在Service层中使用日志记录
4. **第四步**:(可选)配置日志收集系统
---
## 十、相关文档
- `services/userService/zap日志框架集成指南.md` - 详细的zap集成指南
- `docs/微服务架构设计.md` - 微服务架构总体设计