golang实现PHP Monolog风格日志记录功能插件库glo的使用

golang实现PHP Monolog风格日志记录功能插件库glo的使用

GLO是一个受PHP Monolog启发的Golang日志库,其严重级别与Monolog完全相同。

安装

go get github.com/lajosbencz/glo

严重级别

Debug     = 100
Info      = 200
Notice    = 250
Warning   = 300
Error     = 400
Critical  = 500
Alert     = 550
Emergency = 600

简单示例

package main

import "github.com/lajosbencz/glo"

func main() {
    // Info - Warning 会输出到 os.Stdout
    // Error - Emergency 会输出到 os.Stderr
    log := glo.NewStdFacility()

    // 输出到 os.Stdout
    log.Debug("Detailed debug line: %#v", map[string]string{"x": "foo", "y": "bar"})

    // 输出到 os.Stderr
    log.Error("Oooof!")
}

输出:

2019-01-22T15:16:08+01:00 [DEBUG] Detailed debug line [map[x:foo y:bar]]
2019-01-22T15:16:08+01:00 [ERROR] Oooof! []

自定义示例

package main

import (
    "bytes"
    "fmt"
    "os"
    "strings"

    "github.com/lajosbencz/glo"
)

func main() {
    log := glo.NewFacility()

    // 将所有日志写入缓冲区
    bfr := bytes.NewBufferString("")
    handlerBfr := glo.NewHandler(bfr)
    log.PushHandler(handlerBfr)

    // 只将错误及以上级别的日志以简短格式写入stdout
    handlerStd := glo.NewHandler(os.Stdout)
    formatter := glo.NewFormatter("{L}: {M}")
    filter := glo.NewFilterLevel(glo.Error)
    handlerStd.SetFormatter(formatter)
    handlerStd.PushFilter(filter)
    log.PushHandler(handlerStd)

    fmt.Println("Log output:")
    fmt.Println(strings.Repeat("=", 70))
    log.Info("Only written to the buffer")
    log.Alert("Written to both buffer and stdout")

    fmt.Println("")
    fmt.Println("Buffer contents:")
    fmt.Println(strings.Repeat("=", 70))
    fmt.Println(bfr.String())
}

输出:

Log output:
======================================================================
ALERT: Written to both buffer and stdout []

Buffer contents:
======================================================================
2019-01-22T15:14:16+01:00 [INFO] Only written to the buffer []
2019-01-22T15:14:16+01:00 [ALERT] Written to both buffer and stdout []

自定义过滤器

package main

import (
    "os"
    "regexp"

    "github.com/lajosbencz/glo"
)

func main() {
    handler := glo.NewHandler(os.Stdout)
    filterEmptyLines := &filterRgx{regexp.MustCompile(`^.+$`)}
    handler.PushFilter(filterEmptyLines)

    log := glo.NewFacility()
    log.PushHandler(handler)

    log.Debug("", "format is empty, should be ignored")
    log.Debug("only this should appear at the output")
}

type filterRgx struct {
    rgx *regexp.Regexp
}

func (f *filterRgx) Check(level glo.Level, line string, params ...interface{}) bool {
    return f.rgx.MatchString(line)
}

输出:

2019-01-22T15:30:23+01:00 [DEBUG] only this should appear at the output

更多关于golang实现PHP Monolog风格日志记录功能插件库glo的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现PHP Monolog风格日志记录功能插件库glo的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用Golang实现PHP Monolog风格的日志记录库glo

PHP Monolog是一个流行的日志记录库,提供了灵活的日志处理功能。在Golang中,我们可以创建一个类似的库"glo"来实现相似的功能。下面我将介绍如何实现这样一个库及其使用方法。

基本设计思路

glo库将包含以下Monolog的核心特性:

  • 多级别日志记录 (Debug, Info, Warning, Error等)
  • 多种日志处理器 (文件、控制台、网络等)
  • 日志格式化
  • 日志通道概念
  • 上下文信息记录

实现代码

package glo

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"
)

// 日志级别
type Level int

const (
	Debug Level = iota
	Info
	Warning
	Error
	Critical
)

var levelNames = []string{
	"DEBUG",
	"INFO",
	"WARNING",
	"ERROR",
	"CRITICAL",
}

// 日志记录结构
type Record struct {
	Message   string
	Level     Level
	Timestamp time.Time
	Context   map[string]interface{}
}

// 日志处理器接口
type Handler interface {
	Handle(record Record) error
	Close() error
}

// 格式化接口
type Formatter interface {
	Format(record Record) string
}

// 默认格式化器
type LineFormatter struct{}

func (f *LineFormatter) Format(record Record) string {
	return fmt.Sprintf("[%s] %s: %s %v\n",
		record.Timestamp.Format("2006-01-02 15:04:05"),
		levelNames[record.Level],
		record.Message,
		record.Context)
}

// 基础处理器
type BaseHandler struct {
	formatter Formatter
	writer    io.Writer
}

func NewBaseHandler(writer io.Writer, formatter Formatter) *BaseHandler {
	return &BaseHandler{
		writer:    writer,
		formatter: formatter,
	}
}

func (h *BaseHandler) Handle(record Record) error {
	_, err := fmt.Fprint(h.writer, h.formatter.Format(record))
	return err
}

func (h *BaseHandler) Close() error {
	if closer, ok := h.writer.(io.Closer); ok {
		return closer.Close()
	}
	return nil
}

// 文件处理器
type FileHandler struct {
	BaseHandler
	file *os.File
}

func NewFileHandler(filename string, formatter Formatter) (*FileHandler, error) {
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil, err
	}
	return &FileHandler{
		BaseHandler: *NewBaseHandler(file, formatter),
		file:        file,
	}, nil
}

// 控制台处理器
type ConsoleHandler struct {
	BaseHandler
}

func NewConsoleHandler(formatter Formatter) *ConsoleHandler {
	return &ConsoleHandler{
		BaseHandler: *NewBaseHandler(os.Stdout, formatter),
	}
}

// 日志记录器
type Logger struct {
	name      string
	handlers  []Handler
	minLevel  Level
	context   map[string]interface{}
	contextMu sync.RWMutex
}

func NewLogger(name string) *Logger {
	return &Logger{
		name:     name,
		handlers: make([]Handler, 0),
		minLevel: Debug,
		context:  make(map[string]interface{}),
	}
}

func (l *Logger) AddHandler(handler Handler) {
	l.handlers = append(l.handlers, handler)
}

func (l *Logger) SetLevel(level Level) {
	l.minLevel = level
}

func (l *Logger) WithContext(key string, value interface{}) *Logger {
	l.contextMu.Lock()
	defer l.contextMu.Unlock()
	
	newLogger := NewLogger(l.name)
	newLogger.handlers = l.handlers
	newLogger.minLevel = l.minLevel
	
	// 复制上下文
	for k, v := range l.context {
		newLogger.context[k] = v
	}
	newLogger.context[key] = value
	
	return newLogger
}

func (l *Logger) log(level Level, message string, context map[string]interface{}) {
	if level < l.minLevel {
		return
	}
	
	// 合并上下文
	mergedContext := make(map[string]interface{})
	l.contextMu.RLock()
	for k, v := range l.context {
		mergedContext[k] = v
	}
	l.contextMu.RUnlock()
	
	for k, v := range context {
		mergedContext[k] = v
	}
	
	record := Record{
		Message:   message,
		Level:     level,
		Timestamp: time.Now(),
		Context:   mergedContext,
	}
	
	for _, handler := range l.handlers {
		_ = handler.Handle(record)
	}
}

// 各级别日志方法
func (l *Logger) Debug(message string, context map[string]interface{}) {
	l.log(Debug, message, context)
}

func (l *Logger) Info(message string, context map[string]interface{}) {
	l.log(Info, message, context)
}

func (l *Logger) Warning(message string, context map[string]interface{}) {
	l.log(Warning, message, context)
}

func (l *Logger) Error(message string, context map[string]interface{}) {
	l.log(Error, message, context)
}

func (l *Logger) Critical(message string, context map[string]interface{}) {
	l.log(Critical, message, context)
}

// 关闭所有处理器
func (l *Logger) Close() error {
	var err error
	for _, handler := range l.handlers {
		if e := handler.Close(); e != nil {
			err = e
		}
	}
	return err
}

使用示例

package main

import (
	"log"
	"path/to/glo"
)

func main() {
	// 创建日志记录器
	logger := glo.NewLogger("myapp")
	
	// 添加控制台处理器
	consoleHandler := glo.NewConsoleHandler(&glo.LineFormatter{})
	logger.AddHandler(consoleHandler)
	
	// 添加文件处理器
	fileHandler, err := glo.NewFileHandler("app.log", &glo.LineFormatter{})
	if err != nil {
		log.Fatal(err)
	}
	logger.AddHandler(fileHandler)
	
	// 设置日志级别
	logger.SetLevel(glo.Info)
	
	// 记录日志
	logger.Info("Application started", nil)
	
	// 带上下文的日志
	ctxLogger := logger.WithContext("request_id", "12345")
	ctxLogger.Warning("Something might be wrong", map[string]interface{}{
		"user": "john_doe",
	})
	
	// 错误日志
	logger.Error("Failed to connect to database", map[string]interface{}{
		"error": "connection timeout",
	})
	
	// 关闭日志处理器
	if err := logger.Close(); err != nil {
		log.Println("Failed to close logger:", err)
	}
}

功能扩展建议

  1. 更多处理器

    • 实现SyslogHandler、SocketHandler等
    • 添加RotatingFileHandler支持日志轮转
  2. 更丰富的格式化

    • 实现JSONFormatter
    • 支持自定义格式化模板
  3. 高级功能

    • 添加日志缓冲
    • 实现日志处理器组
    • 添加日志过滤功能
  4. 性能优化

    • 使用缓冲写入
    • 异步日志记录

这个glo库实现了Monolog的核心功能,包括多级别日志记录、多种处理器、上下文信息和格式化功能。您可以根据需要进一步扩展它,添加更多高级特性。

回到顶部