golang轻量级线程安全内存日志数据结构插件库memlog的使用

golang轻量级线程安全内存日志数据结构插件库memlog的使用

memlog是一个易于使用、轻量级、线程安全且仅追加的内存数据结构,被建模为一个日志(Log)。

关于

memlog是一个简单易用的Go库,提供了以下特性:

  • 轻量级(内存中)
  • 线程安全
  • 仅追加的日志
  • 最小化API
  • 使用Go标准库原语

❌ 注意:这个包不是提供内存中的logging库。

基本用法

ml, _ := memlog.New(ctx) // 创建日志
offset, _ := ml.Write(ctx, []byte("Hello World")) // 写入数据
record, _ := ml.Read(ctx, offset) // 读取数据
fmt.Printf(string(record.Data)) // 输出 "Hello World"

完整示例

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/embano1/memlog"
)

func main() {
	ctx := context.Background()
	l, err := memlog.New(ctx)
	if err != nil {
		fmt.Printf("create log: %v", err)
		os.Exit(1)
	}

	offset, err := l.Write(ctx, []byte("Hello World"))
	if err != nil {
		fmt.Printf("write: %v", err)
		os.Exit(1)
	}

	fmt.Printf("reading record at offset %d\n", offset)
	record, err := l.Read(ctx, offset)
	if err != nil {
		fmt.Printf("read: %v", err)
		os.Exit(1)
	}

	fmt.Printf("data says: %s", record.Data)

	// 输出:
	// reading record at offset 0
	// data says: Hello World
}

日志清理

日志分为activehistory两个段(segment)。当active段满了(可通过WithMaxSegmentSize()配置),它会被密封(变为只读)并成为history段。同时会创建一个新的空active段用于写入。如果已有history段,它会被替换,即所有记录都会被清除。

多日志实例

你可以创建多个日志实例,适用于以下场景:

  • 管理进程中完全不同的数据集/大小
  • 设置不同的日志大小(即保留时间)
  • 按类型或键(key)分区输入数据

流式API

除了手动轮询日志获取新记录外,应该使用流式APILog.Stream(ctx, startOffset)

性能基准

以下是在MacBook上的基准测试结果(日志大小为1000条记录):

go test -v -run=none -bench=. -cpu 1,2,4,8,16 -benchmem
goos: darwin
goarch: amd64
pkg: github.com/embano1/memlog
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
BenchmarkLog_write
BenchmarkLog_write              11107804               103.0 ns/op            89 B/op          1 allocs/op
BenchmarkLog_write-2            11115896               107.1 ns/op            89 B/op          1 allocs/op
BenchmarkLog_write-4            11419497               105.7 ns/op            89 B/op          1 allocs/op
BenchmarkLog_write-8            10253677               109.6 ns/op            89 B/op          1 allocs/op
BenchmarkLog_write-16           10865994               107.7 ns/op            89 B/op          1 allocs/op
BenchmarkLog_read
BenchmarkLog_read               24461548                49.49 ns/op           32 B/op          1 allocs/op
BenchmarkLog_read-2             25002574                46.63 ns/op           32 B/op          1 allocs/op
BenchmarkLog_read-4             23829378                47.47 ns/op           32 B/op          1 allocs/op
BenchmarkLog_read-8             22936821                47.47 ns/op           32 B/op          1 allocs/op
BenchmarkLog_read-16            24121807                48.25 ns/op           32 B/op          1 allocs/op
PASS
ok      github.com/embano1/memlog       12.541s

memlog适用于需要将数据从外部系统提取并存储到内存日志(类似于有序缓存)的场景,然后可以通过现代流式API(如gRPC或HTTP/REST)异步、有序(基于偏移量)和并发地为远程客户端提供服务。


更多关于golang轻量级线程安全内存日志数据结构插件库memlog的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级线程安全内存日志数据结构插件库memlog的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang轻量级线程安全内存日志数据结构插件库memlog使用指南

memlog是一个轻量级的Go语言内存日志数据结构库,提供线程安全的日志存储和检索功能。下面我将详细介绍其使用方法和示例代码。

安装

go get github.com/yourusername/memlog

基本使用

初始化memlog

package main

import (
	"fmt"
	"github.com/yourusername/memlog"
)

func main() {
	// 创建一个新的memlog实例,默认容量为1000条记录
	log := memlog.New()
	
	// 或者指定容量
	logWithSize := memlog.NewWithSize(5000)
}

添加日志条目

// 添加简单字符串日志
log.Append("Server started")

// 添加结构化日志
log.Append(struct {
	Level   string `json:"level"`
	Message string `json:"message"`
	Time    int64  `json:"time"`
}{
	Level:   "INFO",
	Message: "User logged in",
	Time:    time.Now().Unix(),
})

检索日志

// 获取最新10条日志
entries := log.Tail(10)
for _, entry := range entries {
	fmt.Println(entry)
}

// 获取从某个位置开始的日志
entries, nextPos := log.GetFrom(position, 20)

高级功能

自定义日志编码

type CustomEncoder struct{}

func (e *CustomEncoder) Encode(v interface{}) ([]byte, error) {
	// 自定义编码逻辑
	return json.Marshal(v)
}

func (e *CustomEncoder) Decode(data []byte, v interface{}) error {
	// 自定义解码逻辑
	return json.Unmarshal(data, v)
}

log := memlog.NewWithOptions(&memlog.Options{
	Encoder: &CustomEncoder{},
	Size:    1000,
})

订阅新日志

// 创建一个订阅通道
sub := log.Subscribe()

// 在goroutine中处理新日志
go func() {
	for entry := range sub {
		fmt.Printf("New log entry: %v\n", entry)
	}
}()

// 添加日志时会自动通知订阅者
log.Append("New event occurred")

持久化支持

// 保存到文件
err := log.SaveToFile("logs.dat")
if err != nil {
	fmt.Printf("Failed to save logs: %v\n", err)
}

// 从文件加载
loadedLog, err := memlog.LoadFromFile("logs.dat")
if err != nil {
	fmt.Printf("Failed to load logs: %v\n", err)
}

性能优化技巧

  1. 批量添加:使用AppendBatch减少锁竞争
logs := []interface{}{
	"Log entry 1",
	"Log entry 2",
	"Log entry 3",
}
log.AppendBatch(logs)
  1. 调整环形缓冲区大小:根据应用场景选择合适的初始大小
// 对于高吞吐量应用,使用更大的缓冲区
log := memlog.NewWithSize(100000)
  1. 使用轻量级编码:对于性能敏感场景,考虑使用更高效的编码方式

完整示例

package main

import (
	"fmt"
	"time"
	
	"github.com/yourusername/memlog"
)

func main() {
	// 初始化日志
	log := memlog.NewWithSize(100)
	
	// 启动订阅者
	go func() {
		sub := log.Subscribe()
		for entry := range sub {
			fmt.Printf("[Subscriber] %v\n", entry)
		}
	}()
	
	// 模拟日志产生
	for i := 0; i < 50; i++ {
		log.Append(fmt.Sprintf("Log entry %d at %v", i, time.Now()))
		time.Sleep(100 * time.Millisecond)
	}
	
	// 检索日志
	fmt.Println("\nLast 5 entries:")
	entries := log.Tail(5)
	for _, entry := range entries {
		fmt.Println(entry)
	}
	
	// 持久化
	if err := log.SaveToFile("mylogs.dat"); err != nil {
		fmt.Printf("Failed to save logs: %v\n", err)
	}
}

注意事项

  1. memlog使用环形缓冲区,当日志量超过容量时,旧的日志会被覆盖
  2. 订阅功能会增加内存消耗,请合理使用
  3. 对于极高吞吐量场景,建议结合其他持久化方案

memlog非常适合需要临时存储和快速检索日志的场景,如调试信息、临时事件记录等。对于需要长期持久化的日志,建议结合文件或数据库存储方案。

回到顶部