10 KiB
10 KiB
微服务日志架构设计
一、设计原则
1.1 核心原则
- 服务独立性:每个微服务应该有自己独立的日志实例
- 统一配置:所有服务使用相同的日志配置和格式
- 共享工具:在共享包中提供统一的日志初始化逻辑
- 分散部署:每个服务可以独立配置日志级别和输出方式
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格式,包含服务名)
实现方式:
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
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
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
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
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格式)
{
"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 实现方式
- 本地文件存储:每个服务将日志写入本地文件
- 日志收集Agent:使用Filebeat或Fluentd收集日志
- 日志聚合:发送到Elasticsearch或Loki
- 可视化查询:通过Kibana或Grafana查询
优势:
- 各服务独立,不依赖外部系统
- 即使日志收集系统故障,本地仍有日志
- 统一的查询界面,可以跨服务查询
六、环境变量配置
6.1 各服务独立配置
每个服务可以通过环境变量独立配置:
# 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示例
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 日志记录原则
- 每个服务独立初始化:确保服务独立性
- 统一的日志格式:便于统一管理和查询
- 包含服务名:每条日志都标识所属服务
- 结构化日志:使用字段而不是字符串拼接
- 敏感信息不记录:密码、完整Token等
7.3 性能考虑
- 生产环境使用结构化日志(
Logger.Info)而不是Sugar API - 合理设置日志级别,避免过多日志影响性能
- 使用日志轮转,避免日志文件过大
八、总结
推荐方案
✅ 每个服务独立,共享logger包
优势:
- 服务独立性:每个服务可以独立部署和配置
- 统一格式:所有服务使用相同的日志格式
- 易于管理:共享的logger包便于维护和升级
- 灵活配置:每个服务可以独立配置日志级别
- 便于扩展:后续可以轻松添加日志收集功能
不推荐:统一管理所有服务的日志实例
原因:
- 破坏了服务的独立性
- 难以在不同服务中使用不同的日志配置
- 服务间耦合度增加
九、实施步骤
- 第一步:创建共享的
pkg/logger/logger.go - 第二步:在每个服务的
main.go中初始化logger - 第三步:在Service层中使用日志记录
- 第四步:(可选)配置日志收集系统
十、相关文档
services/userService/zap日志框架集成指南.md- 详细的zap集成指南docs/微服务架构设计.md- 微服务架构总体设计