golang统一日志接口与最佳实践集成插件库logur的使用
Golang 统一日志接口与最佳实践:集成插件库 Logur 的使用
警告
这个项目已经归档且不再维护。建议考虑使用 log/slog
替代。
Logur 简介
Logur 是一个遵循日志最佳实践的日志库,其主要特点包括:
核心特性
- 统一的日志接口
- 用于测试日志记录的测试日志器
- 用于丢弃日志事件的无操作日志器
io.Writer
支持- 标准库日志器支持
- 与知名库的集成(如 gRPC、MySQL 驱动、Watermill 等)
- 知名日志库的适配器(如 hclog、logrus、zap 等)
安装
使用 Go Modules 安装:
go get logur.dev/logur
最佳实践示例
创建自定义接口
建议定义自己的日志接口而不是直接使用 Logur 的接口:
type MyLogger interface {
Trace(msg string, fields ...map[string]interface{})
Debug(msg string, fields ...map[string]interface{})
Info(msg string, fields ...map[string]interface{})
Warn(msg string, fields ...map[string]interface{})
Error(msg string, fields ...map[string]interface{})
}
func main() {
logger := logur.NewNoopLogger()
myFunc(logger)
}
func myFunc(logger MyLogger) {
logger.Debug("myFunc ran")
// 或
logger.Debug("myFunc ran", map[string]interface{}{"key": "value"})
}
带字段的日志接口
如果需要添加公共上下文,可以这样扩展接口:
type MyLogger interface {
Trace(msg string, fields ...map[string]interface{})
Debug(msg string, fields ...map[string]interface{})
// ...
WithFields(fields map[string]interface{}) MyLogger
}
type myLogger struct {
logger logur.Logger
}
func (l *myLogger) Debug(msg string, fields ...map[string]interface{}) { l.logger.Debug(msg, fields...) }
func (l *myLogger) WithFields(fields map[string]interface{}) MyLogger {
return &myLogger{logur.WithFields(l.logger, fields)}
}
func main() {
logger := &myLogger{logur.NewNoopLogger()}
myFunc(logger)
}
func myFunc(logger MyLogger) {
logger.WithFields(map[string]interface{}{"key": "value"}).Debug("myFunc ran", nil)
}
标准库日志器集成
创建标准库日志器用于 HTTP 服务器错误日志:
func newStandardErrorLogger() *log.Logger {
return logur.NewStandardLogger(logur.NewNoopLogger(), logur.ErrorLevel, "", 0)
}
func main() {
server := &http.Server{
Handler: nil,
ErrorLog: newStandardErrorLogger(),
}
}
常见问题
为什么不直接使用某个日志库?
使用自定义接口可以避免将应用程序与特定日志库耦合。
为什么不使用 Go kit 日志器?
虽然 Go kit 的日志接口简单强大,但它需要导入额外的包来实现结构化或分级日志。
为什么不使用 logger.With(keyvals ...interface{})
?
虽然这种接口更简洁,但使用 map[string]interface{}
也有其优势:
- 更清晰的键值对关系
- 避免奇数个参数的问题
- 性能差异在大多数情况下可以忽略
为什么没有格式化函数(*f
)?
现代日志实践更推荐结构化日志而不是格式化消息。
为什么没有行日志函数(*ln
)?
单独的日志事件通常已经占据一行,添加这些函数价值不大。
完整示例
package main
import (
"logur.dev/logur"
)
// 自定义日志接口
type AppLogger interface {
Debug(msg string, fields ...map[string]interface{})
Info(msg string, fields ...map[string]interface{})
Error(msg string, fields ...map[string]interface{})
WithFields(fields map[string]interface{}) AppLogger
}
// 适配器实现
type logurAdapter struct {
logger logur.Logger
}
func (l *logurAdapter) Debug(msg string, fields ...map[string]interface{}) {
l.logger.Debug(msg, fields...)
}
func (l *logurAdapter) Info(msg string, fields ...map[string]interface{}) {
l.logger.Info(msg, fields...)
}
func (l *logurAdapter) Error(msg string, fields ...map[string]interface{}) {
l.logger.Error(msg, fields...)
}
func (l *logurAdapter) WithFields(fields map[string]interface{}) AppLogger {
return &logurAdapter{logur.WithFields(l.logger, fields)}
}
func main() {
// 使用无操作日志器
logger := &logurAdapter{logur.NewNoopLogger()}
// 带上下文的日志
serviceLogger := logger.WithFields(map[string]interface{}{
"service": "user",
"version": "1.0",
})
serviceLogger.Info("Service started")
serviceLogger.Debug("Processing request", map[string]interface{}{
"user_id": 123,
"action": "create",
})
// 错误日志
serviceLogger.Error("Failed to process request", map[string]interface{}{
"error": "connection timeout",
})
}
Logur 虽然不再维护,但它展示的日志接口设计和最佳实践仍然值得学习。对于新项目,建议考虑 Go 1.21 引入的 log/slog
作为替代方案。
更多关于golang统一日志接口与最佳实践集成插件库logur的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang统一日志接口与最佳实践集成插件库logur的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 统一日志接口与 logur 使用指南
统一日志接口的重要性
在 Go 项目中,统一的日志接口可以带来以下好处:
- 解耦业务代码与具体日志实现
- 方便切换不同的日志库
- 统一日志格式和输出方式
- 便于集中管理日志级别和过滤
logur 简介
logur 是一个轻量级的日志接口抽象层,它提供:
- 标准化的日志接口
- 适配多种流行日志库的适配器
- 丰富的中间件功能
- 简洁易用的 API 设计
基本使用
安装 logur
go get github.com/logur/logur
基础示例
package main
import (
"github.com/logur/logur"
"github.com/logur/logur/adapter/logrus"
"github.com/sirupsen/logrus"
)
func main() {
// 创建 logrus 实例
logrusLogger := logrus.New()
// 通过适配器转换为 logur 接口
logger := logrus.New(logrusLogger)
// 使用 logur 接口记录日志
logger.Debug("This is a debug message")
logger.Info("This is an info message", logur.Fields{"key": "value"})
logger.Warn("This is a warning message")
logger.Error("This is an error message", logur.Fields{"err": "something went wrong"})
}
最佳实践
1. 依赖注入日志接口
type Service struct {
logger logur.Logger
}
func NewService(logger logur.Logger) *Service {
return &Service{
logger: logger,
}
}
func (s *Service) Process() {
s.logger.Info("Processing started")
// 业务逻辑
}
2. 使用中间件
import "github.com/logur/logur/adapter/zaplog"
func main() {
// 创建 zap logger
zapLogger, _ := zap.NewProduction()
// 转换为 logur 接口
logger := zaplog.New(zapLogger)
// 添加前缀
prefixedLogger := logur.WithFields(logger, logur.Fields{
"service": "my-service",
"version": "1.0.0",
})
prefixedLogger.Info("Service started")
}
3. 日志级别控制
import (
"github.com/logur/logur"
"github.com/logur/logur/adapter/stdlog"
)
func main() {
// 创建标准库 logger
stdLogger := log.New(os.Stdout, "", log.LstdFlags)
// 转换为 logur 接口
logger := stdlog.New(stdLogger)
// 过滤日志级别
filteredLogger := logur.WithFilter(
logger,
logur.AllowInfo(), // 只允许 Info 及以上级别
)
filteredLogger.Debug("This won't be logged") // 被过滤
filteredLogger.Info("This will be logged") // 正常记录
}
4. 上下文日志
func handleRequest(logger logur.Logger, req *http.Request) {
// 添加请求上下文
requestLogger := logur.WithFields(logger, logur.Fields{
"request_id": req.Header.Get("X-Request-ID"),
"method": req.Method,
"path": req.URL.Path,
})
requestLogger.Info("Request received")
// 处理请求...
}
支持的适配器
logur 支持多种流行的日志库:
- 标准库 -
stdlog
- Logrus -
logrus
- Zap -
zaplog
- Zerolog -
zerolog
- Klog -
klog
- Testing -
testlog
(用于测试)
高级功能
日志采样
import "github.com/logur/logur/conformance"
func main() {
baseLogger := // 初始化基础 logger
// 每10条日志采样1条
sampledLogger := conformance.NewSampler(baseLogger, 10)
for i := 0; i < 100; i++ {
sampledLogger.Info("Log message", logur.Fields{"count": i})
}
}
错误处理增强
import "github.com/logur/logur/conformance"
func main() {
baseLogger := // 初始化基础 logger
// 增强错误处理
enhancedLogger := conformance.NewErrorEnhancer(baseLogger)
err := someOperation()
if err != nil {
// 自动添加堆栈信息
enhancedLogger.Error("Operation failed", logur.Fields{"err": err})
}
}
总结
logur 为 Go 项目提供了:
- 统一的日志接口,解耦业务代码与日志实现
- 灵活的适配器系统,支持主流日志库
- 丰富的中间件功能,增强日志处理能力
- 简洁一致的 API 设计
通过遵循这些最佳实践,您可以构建更灵活、更易维护的日志系统,同时保持随时切换底层日志实现的能力。