自定义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()
}

总结

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

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

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

相关推荐
zhuyasen6 小时前
Go语言开发实战:app库实现多服务启动与关闭的优雅方案
后端·go
DemonAvenger11 小时前
深入Go并发编程:Goroutine性能调优与实战技巧全解析
设计模式·架构·go
考虑考虑15 小时前
Golang 使用定时任务(robfig/cron/v3)
后端·程序员·go
一个热爱生活的普通人17 小时前
深入解析Go语言container/list:双向链表的实现与应用
后端·面试·go
十字路口的火丁18 小时前
Golang 中的 Restful API 请求客户端 resty 简介(类似 Java 中的 Jersey)
后端·go
寻月隐君1 天前
gogen:一键生成 Go 项目,开发者的效率利器
后端·go·github
HappyChan1 天前
kakfa生产者消费者实践
云原生·kafka·go
小白白白_1 天前
开工一个月,我自己造了一个编程语言!
go·编程语言
forever232 天前
go实现带超时控制的API调用
go