golang高效O(1)多输出源日志记录插件库log的使用

golang高效O(1)多输出源日志记录插件库log的使用

简介

这是一个O(1)恒定时间日志系统,允许你将一个日志连接到多个写入器(例如标准输出、文件、TCP连接等)。

安装

go get -u github.com/aerogo/log/...

使用示例

package main

import (
	"github.com/aerogo/log"
)

func main() {
	// 创建一个新的日志实例
	hello := log.New()
	
	// 添加一个文件写入器
	hello.AddWriter(log.File("hello.log"))
	
	// 写入非关键数据(缓冲)
	hello.Info("Hello World %d %d %d", 1, 2, 3)
	
	// 错误消息会强制立即I/O刷新
	hello.Error("Something went wrong")
}

工作原理

所有Write调用都被排队到一个通道中,该通道在一个单独的goroutine中读取。一旦通道接收到新数据,它就会将数据写入所有已注册的输出。

多输出源示例

package main

import (
	"github.com/aerogo/log"
	"os"
)

func main() {
	// 创建日志实例
	logger := log.New()
	
	// 添加多个输出源
	logger.AddWriter(os.Stdout)              // 标准输出
	logger.AddWriter(log.File("app.log"))    // 文件输出
	
	// 使用不同日志级别
	logger.Info("Application started")       // 信息级别
	logger.Warn("Low disk space")            // 警告级别
	logger.Error("Failed to open file")      // 错误级别
}

高级用法

package main

import (
	"github.com/aerogo/log"
	"net"
)

func main() {
	// 创建TCP连接作为日志输出
	conn, err := net.Dial("tcp", "localhost:8080")
	if err != nil {
		panic(err)
	}
	
	// 创建日志实例
	logger := log.New()
	
	// 添加多个输出源
	logger.AddWriter(os.Stdout)              // 控制台
	logger.AddWriter(log.File("debug.log"))  // 文件
	logger.AddWriter(conn)                   // TCP连接
	
	// 格式化日志输出
	logger.Info("User %s logged in", "john_doe")
	logger.Error("Database connection failed: %v", err)
	
	// 关闭连接
	conn.Close()
}

这个日志库提供了高效的O(1)性能,并且支持多种输出源,非常适合需要将日志同时输出到多个目的地的应用场景。


更多关于golang高效O(1)多输出源日志记录插件库log的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高效O(1)多输出源日志记录插件库log的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang高效O(1)多输出源日志记录插件库log的使用

在Go语言中,标准库log提供了基本的日志功能,但对于需要高性能、多输出源(如同时输出到控制台、文件、网络等)的场景,我们需要更强大的解决方案。下面我将介绍如何实现一个高效O(1)的多输出源日志系统。

核心设计思路

  1. O(1)时间复杂度:通过channel实现异步日志处理
  2. 多输出源:使用io.MultiWriter支持多个输出目标
  3. 日志级别:支持不同级别的日志过滤
  4. 插件化:可以轻松添加新的输出源

实现代码示例

package multilog

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

// 日志级别
type LogLevel int

const (
	LevelDebug LogLevel = iota
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
)

var levelNames = []string{
	"DEBUG",
	"INFO",
	"WARN",
	"ERROR",
	"FATAL",
}

// Logger 多输出源日志记录器
type Logger struct {
	level     LogLevel
	logChan   chan *logEntry
	writers   []io.Writer
	closeChan chan struct{}
	wg        sync.WaitGroup
}

type logEntry struct {
	level   LogLevel
	message string
}

// NewLogger 创建新的日志记录器
func NewLogger(level LogLevel, bufferSize int) *Logger {
	l := &Logger{
		level:     level,
		logChan:   make(chan *logEntry, bufferSize),
		closeChan: make(chan struct{}),
	}

	l.wg.Add(1)
	go l.processLogs()

	return l
}

// AddWriter 添加输出源
func (l *Logger) AddWriter(w io.Writer) {
	l.writers = append(l.writers, w)
}

// processLogs 处理日志的核心goroutine
func (l *Logger) processLogs() {
	defer l.wg.Done()

	// 创建组合writer
	multiWriter := io.MultiWriter(l.writers...)
	logger := log.New(multiWriter, "", log.LstdFlags|log.Lmicroseconds)

	for {
		select {
		case entry := <-l.logChan:
			if entry.level >= l.level {
				logger.Printf("[%s] %s", levelNames[entry.level], entry.message)
			}
		case <-l.closeChan:
			// 处理剩余日志
			for {
				select {
				case entry := <-l.logChan:
					if entry.level >= l.level {
						logger.Printf("[%s] %s", levelNames[entry.level], entry.message)
					}
				default:
					return
				}
			}
		}
	}
}

// 通用日志记录方法
func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
	select {
	case l.logChan <- &logEntry{
		level:   level,
		message: fmt.Sprintf(format, args...),
	}:
	default:
		// 通道满时丢弃日志(可根据需求改为阻塞或其他处理方式)
	}
}

// 各级别日志方法
func (l *Logger) Debug(format string, args ...interface{}) {
	l.log(LevelDebug, format, args...)
}

func (l *Logger) Info(format string, args ...interface{}) {
	l.log(LevelInfo, format, args...)
}

func (l *Logger) Warn(format string, args ...interface{}) {
	l.log(LevelWarn, format, args...)
}

func (l *Logger) Error(format string, args ...interface{}) {
	l.log(LevelError, format, args...)
}

func (l *Logger) Fatal(format string, args ...interface{}) {
	l.log(LevelFatal, format, args...)
	os.Exit(1)
}

// Close 关闭日志记录器
func (l *Logger) Close() {
	close(l.closeChan)
	l.wg.Wait()
}

使用示例

package main

import (
	"os"
	"path/filepath"
	"time"

	"./multilog" // 替换为你的实际模块路径
)

func main() {
	// 创建日志记录器
	logger := multilog.NewLogger(multilog.LevelDebug, 1000)

	// 添加控制台输出
	logger.AddWriter(os.Stdout)

	// 添加文件输出
	file, err := os.OpenFile(
		filepath.Join("logs", "app.log"),
		os.O_CREATE|os.O_WRONLY|os.O_APPEND,
		0666,
	)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	logger.AddWriter(file)

	// 使用日志记录器
	logger.Debug("这是一条调试信息")
	logger.Info("程序启动,时间: %s", time.Now().Format("2006-01-02 15:04:05"))
	logger.Warn("警告: 磁盘空间不足")
	logger.Error("发生错误: %v", "文件读取失败")

	// 模拟并发日志写入
	for i := 0; i < 100; i++ {
		go func(n int) {
			logger.Info("并发日志 %d", n)
		}(i)
	}

	// 关闭日志记录器(在实际应用中通常在程序退出时调用)
	logger.Close()
}

性能优化说明

  1. 异步处理:日志写入通过channel异步处理,调用日志方法的goroutine不会被阻塞
  2. 缓冲channel:通过合理设置bufferSize可以减少goroutine切换
  3. 无锁设计:主要操作都在单个goroutine中完成,避免了锁竞争
  4. 快速失败:当channel满时丢弃日志(可根据需求修改)

扩展建议

  1. 日志轮转:可以集成lumberjack等库实现日志文件轮转
  2. 网络输出:实现io.Writer接口支持远程日志服务器
  3. 结构化日志:支持JSON等结构化日志格式
  4. 上下文信息:添加请求ID等上下文信息

这个实现提供了高性能、多输出源的日志记录功能,核心日志记录操作时间复杂度为O(1),适合高并发场景使用。

回到顶部