目标是实现一个可扩展的日志系统接口。日志系统应具备以下基本功能:
- 支持多种日志级别:如 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()
}
总结
通过以上分阶段的实现方案,我们设计了一个可扩展的日志系统接口,涵盖了基础日志记录、多目标输出、结构化日志、动态日志级别以及异步日志处理等功能。根据实际项目需求,你还可以进一步扩展,如:
- 添加日志文件的滚动与归档功能
- 实现更多的输出目标(例如远程日志服务器)
- 定制日志格式和增强错误处理机制
这个方案既保证了基础的日志记录能力,又留足了扩展的空间,能够满足大部分业务系统对日志的需求。