# 微服务日志架构设计 ## 一、设计原则 ### 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) ↓ 日志文件(本地存储) ↓ 日志收集Agent(Filebeat/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` - 微服务架构总体设计