golang统一日志接口与最佳实践集成插件库logur的使用

Golang 统一日志接口与最佳实践:集成插件库 Logur 的使用

警告

这个项目已经归档且不再维护。建议考虑使用 log/slog 替代。

Logur 简介

Logur 是一个遵循日志最佳实践的日志库,其主要特点包括:

Logur Logo

核心特性

  • 统一的日志接口
  • 用于测试日志记录的测试日志器
  • 用于丢弃日志事件的无操作日志器
  • 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

1 回复

更多关于golang统一日志接口与最佳实践集成插件库logur的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 统一日志接口与 logur 使用指南

统一日志接口的重要性

在 Go 项目中,统一的日志接口可以带来以下好处:

  • 解耦业务代码与具体日志实现
  • 方便切换不同的日志库
  • 统一日志格式和输出方式
  • 便于集中管理日志级别和过滤

logur 简介

logur 是一个轻量级的日志接口抽象层,它提供:

  1. 标准化的日志接口
  2. 适配多种流行日志库的适配器
  3. 丰富的中间件功能
  4. 简洁易用的 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 支持多种流行的日志库:

  1. 标准库 - stdlog
  2. Logrus - logrus
  3. Zap - zaplog
  4. Zerolog - zerolog
  5. Klog - klog
  6. 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 项目提供了:

  1. 统一的日志接口,解耦业务代码与日志实现
  2. 灵活的适配器系统,支持主流日志库
  3. 丰富的中间件功能,增强日志处理能力
  4. 简洁一致的 API 设计

通过遵循这些最佳实践,您可以构建更灵活、更易维护的日志系统,同时保持随时切换底层日志实现的能力。

回到顶部