自定义go日志接口的实现

目标是实现一个可扩展的日志系统接口。日志系统应具备以下基本功能:

  • 支持多种日志级别:如 Debug、Info、Warn、Error、Fatal 等。
  • 可配置的日志输出目标:例如控制台输出、文件输出、甚至网络输出。
  • 结构化日志:支持附带键值对信息(方便后续日志聚合、查询等)。
  • 动态配置日志级别:允许在运行时调整日志级别,过滤不需要的日志。
  • 异步日志记录:通过异步写入和缓冲来提升性能。
  • 可扩展性:通过插件或接口机制支持未来扩展,例如日志旋转、格式化定制等。

下面按阶段说明各阶段的实现步骤及示例代码。


第一阶段:基础日志接口和控制台日志实现

目标 :设计一个基础的 Logger 接口,并提供一个简单的控制台日志实现。

步骤

  • 定义 Logger 接口,包含 Debug、Info、Warn、Error、Fatal 等方法。
  • 实现一个简单的 ConsoleLogger,将日志打印到控制台。

代码示例

go 复制代码
go
複製編輯
package main

import (
	"fmt"
	"time"
)

// Logger 定义了日志接口
type Logger interface {
	Debug(msg string)
	Info(msg string)
	Warn(msg string)
	Error(msg string)
	Fatal(msg string)
}

// ConsoleLogger 实现了 Logger 接口,将日志输出到控制台
type ConsoleLogger struct{}

func (c *ConsoleLogger) log(level, msg string) {
	fmt.Printf("%s [%s] %s\n", time.Now().Format("2006-01-02 15:04:05"), level, msg)
}

func (c *ConsoleLogger) Debug(msg string) {
	c.log("DEBUG", msg)
}

func (c *ConsoleLogger) Info(msg string) {
	c.log("INFO", msg)
}

func (c *ConsoleLogger) Warn(msg string) {
	c.log("WARN", msg)
}

func (c *ConsoleLogger) Error(msg string) {
	c.log("ERROR", msg)
}

func (c *ConsoleLogger) Fatal(msg string) {
	c.log("FATAL", msg)
	// 根据需要可以调用 os.Exit(1) 退出程序
	// os.Exit(1)
}

func main() {
	var logger Logger = &ConsoleLogger{}
	logger.Info("系统启动")
	logger.Error("发生错误")
}

第二阶段:支持多输出目标(扩展 LogWriter 接口)

目标:实现日志系统的多输出能力,允许注册多个日志输出器(例如文件、控制台等)。

步骤

  • 定义 LogWriter 接口,用于扩展输出目标。
  • 修改 Logger 实现,添加 AddWriter() 方法,让日志能够发送到多个输出器。

代码示例

scss 复制代码
go
複製編輯
package main

import (
	"fmt"
	"time"
)

// LogWriter 定义了日志写入器接口
type LogWriter interface {
	WriteLog(level, formattedMsg string)
}

// Logger 定义日志接口,包含添加输出器的方法
type Logger interface {
	Debug(msg string)
	Info(msg string)
	Warn(msg string)
	Error(msg string)
	Fatal(msg string)
	AddWriter(writer LogWriter)
}

// BaseLogger 实现了 Logger 接口,并支持多个 LogWriter
type BaseLogger struct {
	writers []LogWriter
}

func (b *BaseLogger) log(level, msg string) {
	formatted := fmt.Sprintf("%s [%s] %s", time.Now().Format("2006-01-02 15:04:05"), level, msg)
	for _, w := range b.writers {
		w.WriteLog(level, formatted)
	}
}

func (b *BaseLogger) Debug(msg string) {
	b.log("DEBUG", msg)
}
func (b *BaseLogger) Info(msg string) {
	b.log("INFO", msg)
}
func (b *BaseLogger) Warn(msg string) {
	b.log("WARN", msg)
}
func (b *BaseLogger) Error(msg string) {
	b.log("ERROR", msg)
}
func (b *BaseLogger) Fatal(msg string) {
	b.log("FATAL", msg)
	// os.Exit(1)
}

func (b *BaseLogger) AddWriter(writer LogWriter) {
	b.writers = append(b.writers, writer)
}

// ConsoleWriter 将日志写入控制台
type ConsoleWriter struct{}

func (c *ConsoleWriter) WriteLog(level, formattedMsg string) {
	fmt.Println(formattedMsg)
}

func main() {
	logger := &BaseLogger{}
	consoleWriter := &ConsoleWriter{}
	logger.AddWriter(consoleWriter)

	logger.Info("多输出日志系统启动")
}

第三阶段:结构化日志支持

目标:增强日志记录的结构化能力,使日志中可以附带键值对信息,便于日志聚合和后续处理。

步骤

  • 定义 Field 结构体,用于封装键值对。
  • 修改日志方法,接受可变参数 fields ...Field,并将日志记录转换为 JSON 格式输出。

代码示例

go 复制代码
go
複製編輯
package main

import (
	"encoding/json"
	"fmt"
	"time"
)

// Field 定义了结构化日志的键值对
type Field struct {
	Key   string
	Value interface{}
}

// Logger 定义结构化日志接口
type Logger interface {
	Debug(msg string, fields ...Field)
	Info(msg string, fields ...Field)
	Warn(msg string, fields ...Field)
	Error(msg string, fields ...Field)
	Fatal(msg string, fields ...Field)
	AddWriter(writer LogWriter)
}

// BaseLogger 实现了结构化日志功能
type BaseLogger struct {
	writers []LogWriter
}

func (b *BaseLogger) log(level, msg string, fields ...Field) {
	logEntry := make(map[string]interface{})
	logEntry["time"] = time.Now().Format("2006-01-02 15:04:05")
	logEntry["level"] = level
	logEntry["message"] = msg
	for _, field := range fields {
		logEntry[field.Key] = field.Value
	}
	data, _ := json.Marshal(logEntry)
	for _, w := range b.writers {
		w.WriteLog(level, string(data))
	}
}

func (b *BaseLogger) Debug(msg string, fields ...Field) {
	b.log("DEBUG", msg, fields...)
}
func (b *BaseLogger) Info(msg string, fields ...Field) {
	b.log("INFO", msg, fields...)
}
func (b *BaseLogger) Warn(msg string, fields ...Field) {
	b.log("WARN", msg, fields...)
}
func (b *BaseLogger) Error(msg string, fields ...Field) {
	b.log("ERROR", msg, fields...)
}
func (b *BaseLogger) Fatal(msg string, fields ...Field) {
	b.log("FATAL", msg, fields...)
	// os.Exit(1)
}

func (b *BaseLogger) AddWriter(writer LogWriter) {
	b.writers = append(b.writers, writer)
}

// LogWriter 接口和 ConsoleWriter 与前阶段相同
type LogWriter interface {
	WriteLog(level, formattedMsg string)
}

type ConsoleWriter struct{}

func (c *ConsoleWriter) WriteLog(level, formattedMsg string) {
	fmt.Println(formattedMsg)
}

func main() {
	logger := &BaseLogger{}
	logger.AddWriter(&ConsoleWriter{})

	logger.Info("用户登录成功", Field{"user", "john_doe"}, Field{"ip", "192.168.1.1"})
}

第四阶段:动态日志级别和配置

目标:增加日志级别控制,允许动态设置日志级别,只有达到设定级别的日志才会被输出。

步骤

  • 定义 LogLevel 枚举类型,并实现转换方法。
  • 在 Logger 实现中增加对日志级别的判断逻辑以及设置当前日志级别的方法。

代码示例

scss 复制代码
go
複製編輯
package main

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"
)

// LogLevel 定义日志级别枚举
type LogLevel int

const (
	DEBUG LogLevel = iota
	INFO
	WARN
	ERROR
	FATAL
)

func (l LogLevel) String() string {
	levels := []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
	if int(l) < len(levels) {
		return levels[l]
	}
	return "UNKNOWN"
}

// Field 定义结构化日志的键值对
type Field struct {
	Key   string
	Value interface{}
}

// Logger 定义动态日志级别的接口
type Logger interface {
	Debug(msg string, fields ...Field)
	Info(msg string, fields ...Field)
	Warn(msg string, fields ...Field)
	Error(msg string, fields ...Field)
	Fatal(msg string, fields ...Field)
	AddWriter(writer LogWriter)
	SetLevel(level LogLevel)
}

// BaseLogger 实现动态日志级别控制
type BaseLogger struct {
	writers []LogWriter
	level   LogLevel
	mu      sync.RWMutex
}

func NewBaseLogger(level LogLevel) *BaseLogger {
	return &BaseLogger{
		writers: []LogWriter{},
		level:   level,
	}
}

func (b *BaseLogger) log(level LogLevel, msg string, fields ...Field) {
	b.mu.RLock()
	currentLevel := b.level
	b.mu.RUnlock()

	if level < currentLevel {
		return
	}

	logEntry := make(map[string]interface{})
	logEntry["time"] = time.Now().Format("2006-01-02 15:04:05")
	logEntry["level"] = level.String()
	logEntry["message"] = msg
	for _, field := range fields {
		logEntry[field.Key] = field.Value
	}
	data, _ := json.Marshal(logEntry)
	for _, w := range b.writers {
		w.WriteLog(level.String(), string(data))
	}
}

func (b *BaseLogger) Debug(msg string, fields ...Field) {
	b.log(DEBUG, msg, fields...)
}
func (b *BaseLogger) Info(msg string, fields ...Field) {
	b.log(INFO, msg, fields...)
}
func (b *BaseLogger) Warn(msg string, fields ...Field) {
	b.log(WARN, msg, fields...)
}
func (b *BaseLogger) Error(msg string, fields ...Field) {
	b.log(ERROR, msg, fields...)
}
func (b *BaseLogger) Fatal(msg string, fields ...Field) {
	b.log(FATAL, msg, fields...)
	// os.Exit(1)
}

func (b *BaseLogger) AddWriter(writer LogWriter) {
	b.writers = append(b.writers, writer)
}

func (b *BaseLogger) SetLevel(level LogLevel) {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.level = level
}

// LogWriter 接口和 ConsoleWriter 与前阶段相同
type LogWriter interface {
	WriteLog(level, formattedMsg string)
}

type ConsoleWriter struct{}

func (c *ConsoleWriter) WriteLog(level, formattedMsg string) {
	fmt.Println(formattedMsg)
}

func main() {
	logger := NewBaseLogger(INFO)
	logger.AddWriter(&ConsoleWriter{})

	logger.Debug("这是一条调试信息") // 当前级别为 INFO,不会打印
	logger.Info("系统正常启动")

	// 动态设置日志级别
	logger.SetLevel(DEBUG)
	logger.Debug("现在可以打印调试信息了")
}

第五阶段:异步日志及高级功能扩展

目标:通过异步日志记录来提升性能,同时为后续扩展(如日志旋转、网络传输)打下基础。

步骤

  • 使用 Goroutine 和 Channel 实现异步日志处理,将日志写入任务异步发送出去。
  • 定义一个停止方法,确保在程序退出前可以优雅地关闭日志处理 Goroutine。

代码示例

scss 复制代码
go
複製編輯
package main

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"
)

// LogLevel 定义日志级别枚举
type LogLevel int

const (
	DEBUG LogLevel = iota
	INFO
	WARN
	ERROR
	FATAL
)

func (l LogLevel) String() string {
	levels := []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
	if int(l) < len(levels) {
		return levels[l]
	}
	return "UNKNOWN"
}

// Field 定义结构化日志的键值对
type Field struct {
	Key   string
	Value interface{}
}

// LogMessage 封装异步日志任务
type LogMessage struct {
	Level  LogLevel
	Msg    string
	Fields []Field
}

// Logger 接口增加 Stop() 方法用于关闭异步处理
type Logger interface {
	Debug(msg string, fields ...Field)
	Info(msg string, fields ...Field)
	Warn(msg string, fields ...Field)
	Error(msg string, fields ...Field)
	Fatal(msg string, fields ...Field)
	AddWriter(writer LogWriter)
	SetLevel(level LogLevel)
	Stop() // 停止异步日志处理
}

// AsyncLogger 实现异步日志记录
type AsyncLogger struct {
	writers  []LogWriter
	level    LogLevel
	msgChan  chan LogMessage
	wg       sync.WaitGroup
	quitChan chan struct{}
	mu       sync.RWMutex
}

func NewAsyncLogger(level LogLevel, bufferSize int) *AsyncLogger {
	al := &AsyncLogger{
		writers:  []LogWriter{},
		level:    level,
		msgChan:  make(chan LogMessage, bufferSize),
		quitChan: make(chan struct{}),
	}
	al.wg.Add(1)
	go al.process()
	return al
}

func (a *AsyncLogger) process() {
	defer a.wg.Done()
	for {
		select {
		case msg := <-a.msgChan:
			a.mu.RLock()
			if msg.Level < a.level {
				a.mu.RUnlock()
				continue
			}
			logEntry := make(map[string]interface{})
			logEntry["time"] = time.Now().Format("2006-01-02 15:04:05")
			logEntry["level"] = msg.Level.String()
			logEntry["message"] = msg.Msg
			for _, field := range msg.Fields {
				logEntry[field.Key] = field.Value
			}
			data, _ := json.Marshal(logEntry)
			for _, w := range a.writers {
				w.WriteLog(msg.Level.String(), string(data))
			}
			a.mu.RUnlock()
		case <-a.quitChan:
			return
		}
	}
}

func (a *AsyncLogger) log(level LogLevel, msg string, fields ...Field) {
	a.msgChan <- LogMessage{Level: level, Msg: msg, Fields: fields}
}

func (a *AsyncLogger) Debug(msg string, fields ...Field) {
	a.log(DEBUG, msg, fields...)
}
func (a *AsyncLogger) Info(msg string, fields ...Field) {
	a.log(INFO, msg, fields...)
}
func (a *AsyncLogger) Warn(msg string, fields ...Field) {
	a.log(WARN, msg, fields...)
}
func (a *AsyncLogger) Error(msg string, fields ...Field) {
	a.log(ERROR, msg, fields...)
}
func (a *AsyncLogger) Fatal(msg string, fields ...Field) {
	a.log(FATAL, msg, fields...)
	// os.Exit(1)
}

func (a *AsyncLogger) AddWriter(writer LogWriter) {
	a.mu.Lock()
	a.writers = append(a.writers, writer)
	a.mu.Unlock()
}

func (a *AsyncLogger) SetLevel(level LogLevel) {
	a.mu.Lock()
	a.level = level
	a.mu.Unlock()
}

func (a *AsyncLogger) Stop() {
	close(a.quitChan)
	a.wg.Wait()
}

// LogWriter 接口和 ConsoleWriter 同前
type LogWriter interface {
	WriteLog(level, formattedMsg string)
}

type ConsoleWriter struct{}

func (c *ConsoleWriter) WriteLog(level, formattedMsg string) {
	fmt.Println(formattedMsg)
}

func main() {
	logger := NewAsyncLogger(INFO, 100)
	logger.AddWriter(&ConsoleWriter{})

	logger.Info("异步日志系统启动")
	logger.Debug("这条调试信息不会打印")

	logger.SetLevel(DEBUG)
	logger.Debug("现在可以打印调试信息了", Field{"module", "main"})

	// 程序退出前确保关闭异步日志处理
	logger.Stop()
}

总结

通过以上分阶段的实现方案,我们设计了一个可扩展的日志系统接口,涵盖了基础日志记录、多目标输出、结构化日志、动态日志级别以及异步日志处理等功能。根据实际项目需求,你还可以进一步扩展,如:

  • 添加日志文件的滚动与归档功能
  • 实现更多的输出目标(例如远程日志服务器)
  • 定制日志格式和增强错误处理机制

这个方案既保证了基础的日志记录能力,又留足了扩展的空间,能够满足大部分业务系统对日志的需求。

相关推荐
lekami_兰1 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘4 小时前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤5 小时前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt1118 小时前
AI DDD重构实践
go
Grassto2 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto4 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室5 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题5 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo
啊汉7 天前
古文观芷App搜索方案深度解析:打造极致性能的古文搜索引擎
go·软件随想
asaotomo7 天前
一款 AI 驱动的新一代安全运维代理 —— DeepSentry(深哨)
运维·人工智能·安全·ai·go