Files
seqlog/writer.go
bourdon 810664eb12 重构:优化记录格式并修复核心功能
- 修改记录存储格式为 [4B len][8B offset][4B CRC][16B UUID][data]
- 修复 TopicProcessor 中 WaitGroup 使用错误导致 handler 不执行的问题
- 修复写入保护逻辑,避免 dirtyOffset=-1 时误判为写入中
- 添加统计信息定期持久化功能
- 改进 UTF-8 字符截断处理,防止 CJK 字符乱码
- 优化 Web UI:显示人类可读的文件大小,支持点击外部关闭弹窗
- 重构示例代码,添加 webui 和 webui_integration 示例

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 17:54:49 +08:00

111 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package seqlog
import (
"encoding/binary"
"hash/crc32"
"os"
"sync"
"github.com/google/uuid"
)
// LogWriter 日志写入器
type LogWriter struct {
fd *os.File
off int64 // 当前写入偏移
dirtyOff int64 // 最后一次写入偏移
wbuf []byte // 8 MiB 复用
index *RecordIndex // 索引管理器(可选)
mu sync.RWMutex // 保护 off 字段
}
// NewLogWriter 创建一个新的日志写入器
// index: 外部提供的索引管理器,用于在多个组件间共享
func NewLogWriter(path string, index *RecordIndex) (*LogWriter, error) {
if index == nil {
return nil, os.ErrInvalid
}
fd, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return nil, err
}
off, _ := fd.Seek(0, 2) // 跳到尾部
w := &LogWriter{
fd: fd,
off: off,
dirtyOff: -1,
wbuf: make([]byte, 0, 8<<20),
index: index,
}
return w, nil
}
// Append 追加一条日志记录,返回该记录的偏移量
func (w *LogWriter) Append(data []byte) (int64, error) {
w.mu.Lock()
defer w.mu.Unlock()
// 记录当前偏移(返回给调用者,用于索引)
offset := w.off
w.dirtyOff = offset
defer func() {
w.dirtyOff = -1
}()
// 生成 UUID v4
id := uuid.New()
// 编码:[4B len][8B offset][4B CRC][16B UUID][data]
buf := w.wbuf[:0]
buf = binary.LittleEndian.AppendUint32(buf, uint32(len(data)))
buf = binary.LittleEndian.AppendUint64(buf, uint64(offset))
buf = binary.LittleEndian.AppendUint32(buf, crc32.ChecksumIEEE(data))
buf = append(buf, id[:]...)
buf = append(buf, data...)
// 落盘 + sync
if _, err := w.fd.Write(buf); err != nil {
return 0, err
}
if err := w.fd.Sync(); err != nil {
return 0, err
}
// 数据写入成功,立即更新偏移量(保证 w.off 和文件大小一致)
w.off += int64(len(buf))
// 更新索引(如果索引失败,数据已持久化,依赖启动时 rebuild 恢复)
if err := w.index.Append(offset); err != nil {
// 索引失败不影响 w.off因为数据已经写入
return 0, err
}
return offset, nil
}
// GetWriteOffset 获取当前写入偏移量(线程安全)
func (w *LogWriter) GetWriteOffset() int64 {
w.mu.RLock()
defer w.mu.RUnlock()
return w.off
}
func (w *LogWriter) GetDirtyOffset() int64 {
w.mu.RLock()
defer w.mu.RUnlock()
return w.dirtyOff
}
// Close 关闭写入器
// 注意:不关闭 index因为 index 是外部管理的共享资源
func (w *LogWriter) Close() error {
if w.fd == nil {
return nil
}
return w.fd.Close()
}