fix: logs error coding
This commit is contained in:
@@ -9,12 +9,18 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/engigu/baihu-panel/internal/constant"
|
||||
"github.com/engigu/baihu-panel/internal/logger"
|
||||
"github.com/engigu/baihu-panel/internal/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxLogBufferLen 定义了没有换行符时的最大缓冲长度 (4KB)
|
||||
maxLogBufferLen = 4096
|
||||
)
|
||||
|
||||
var (
|
||||
// globalTinyLogManager 跟踪所有活跃的 TinyLog 实例
|
||||
globalTinyLogManager = &TinyLogManager{
|
||||
@@ -98,12 +104,30 @@ func (l *TinyLog) Write(p []byte) (n int, err error) {
|
||||
l.remainder = nil
|
||||
}
|
||||
|
||||
// 1. 寻找最后一个换行符
|
||||
lastNewline := bytes.LastIndexByte(payload, '\n')
|
||||
if lastNewline == -1 {
|
||||
// 没有换行符,且如果长度超过 4KB,强制截断并输出,防止内存无限制增长
|
||||
if len(payload) > 4096 {
|
||||
lastNewline = len(payload) - 1
|
||||
// 1. 寻找最后一个换行符 (\n 或 \r)
|
||||
lastLineBreak := bytes.LastIndexAny(payload, "\n\r")
|
||||
var completeBytes []byte
|
||||
var remainder []byte
|
||||
|
||||
if lastLineBreak != -1 {
|
||||
// 2. 提取出完整的行
|
||||
completeBytes = payload[:lastLineBreak+1]
|
||||
remainder = payload[lastLineBreak+1:]
|
||||
} else {
|
||||
// 3. 没有换行符,且如果长度超过最大缓冲,强制截断并输出,防止内存无限制增长
|
||||
if len(payload) > maxLogBufferLen {
|
||||
// 寻找最后一个完整的 UTF-8 字符边界,避免乱码
|
||||
lastSafe := maxLogBufferLen
|
||||
for i := maxLogBufferLen; i > 0 && i > maxLogBufferLen-4; i-- {
|
||||
if utf8.RuneStart(payload[i-1]) {
|
||||
if !utf8.FullRune(payload[i-1 : maxLogBufferLen]) {
|
||||
lastSafe = i - 1
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
completeBytes = payload[:lastSafe]
|
||||
remainder = payload[lastSafe:]
|
||||
} else {
|
||||
// 保留当前所有内容到下一轮
|
||||
l.remainder = payload
|
||||
@@ -111,20 +135,14 @@ func (l *TinyLog) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 提取出完整的行
|
||||
completeBytes := payload[:lastNewline+1]
|
||||
// 4. 将剩余部分保存
|
||||
l.remainder = remainder
|
||||
|
||||
// 3. 剥离并保存剩余的部分
|
||||
if lastNewline+1 < len(payload) {
|
||||
l.remainder = make([]byte, len(payload)-(lastNewline+1))
|
||||
copy(l.remainder, payload[lastNewline+1:])
|
||||
}
|
||||
|
||||
// 4. 将完整行转换为 UTF-8 并脱敏
|
||||
// 5. 将完整行转换为 UTF-8 并脱敏
|
||||
text := utils.MaskSecrets(utils.ToUTF8(completeBytes), l.masks)
|
||||
outData := []byte(text)
|
||||
|
||||
// 5. 输出安全部分
|
||||
// 6. 输出安全部分
|
||||
_, err = l.writer.Write(outData)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTinyLog_UTF8Splitting(t *testing.T) {
|
||||
tl, err := NewTinyLog("test-utf8", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create TinyLog: %v", err)
|
||||
}
|
||||
defer tl.Close()
|
||||
|
||||
// "你好" in UTF-8: E4 BD A0, E5 a5 bd
|
||||
part1 := []byte{0xE4, 0xBD} // Partial "你"
|
||||
part2 := []byte{0xA0, 0xE5, 0xA5} // Rest of "你", partial "好"
|
||||
part3 := []byte{0xBD, '\n'} // Rest of "好", newline
|
||||
|
||||
_, _ = tl.Write(part1)
|
||||
if len(tl.remainder) != 2 {
|
||||
t.Errorf("Expected remainder len 2, got %d", len(tl.remainder))
|
||||
}
|
||||
|
||||
_, _ = tl.Write(part2)
|
||||
// Currently it should collect both parts but still no newline,
|
||||
// so remainder should be 5 bytes.
|
||||
if len(tl.remainder) != 5 {
|
||||
t.Errorf("Expected remainder len 5, got %d", len(tl.remainder))
|
||||
}
|
||||
|
||||
_, _ = tl.Write(part3)
|
||||
if len(tl.remainder) != 0 {
|
||||
t.Errorf("Expected remainder len 0 after newline, got %d", len(tl.remainder))
|
||||
}
|
||||
|
||||
// Read and verify
|
||||
data, err := tl.ReadLastLines(1)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadLastLines failed: %v", err)
|
||||
}
|
||||
if !bytes.Contains(data, []byte("你好")) {
|
||||
t.Errorf("Expected output to contain '你好', got %q", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTinyLog_CarriageReturn(t *testing.T) {
|
||||
tl, err := NewTinyLog("test-cr", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create TinyLog: %v", err)
|
||||
}
|
||||
defer tl.Close()
|
||||
|
||||
input := []byte("progress: 50%\rprogress: 100%\r\n")
|
||||
_, _ = tl.Write(input)
|
||||
|
||||
data, err := tl.ReadLastLines(10)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadLastLines failed: %v", err)
|
||||
}
|
||||
|
||||
// Should contain both progress lines (or at least be split correctly)
|
||||
if !bytes.Contains(data, []byte("progress: 50%")) {
|
||||
t.Errorf("Expected output to contain 'progress: 50%%', got %q", data)
|
||||
}
|
||||
if !bytes.Contains(data, []byte("progress: 100%")) {
|
||||
t.Errorf("Expected output to contain 'progress: 100%%', got %q", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTinyLog_LongLineCut(t *testing.T) {
|
||||
tl, err := NewTinyLog("test-long", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create TinyLog: %v", err)
|
||||
}
|
||||
defer tl.Close()
|
||||
|
||||
// Create a buffer of maxLogBufferLen-1 bytes with a multi-byte character at the maxLogBufferLen boundary
|
||||
// We want to ensure it doesn't cut in the middle of a 3-byte char.
|
||||
longData := make([]byte, maxLogBufferLen-1)
|
||||
for i := range longData {
|
||||
longData[i] = 'A'
|
||||
}
|
||||
// "你" is E4 BD A0
|
||||
longData = append(longData, 0xE4, 0xBD, 0xA0) // This starts at index maxLogBufferLen-1.
|
||||
// Index maxLogBufferLen-1: E4
|
||||
// Index maxLogBufferLen: BD
|
||||
// Index maxLogBufferLen+1: A0
|
||||
// If we cut at maxLogBufferLen, we split E4 and BD.
|
||||
|
||||
_, _ = tl.Write(longData)
|
||||
|
||||
// Since it's > maxLogBufferLen and no newline, it should trigger the cut.
|
||||
// Our logic finds the last safe boundary before maxLogBufferLen.
|
||||
// RuneStart(E4) at maxLogBufferLen-1 is true. FullRune(E4 at maxLogBufferLen-1 in payload[:maxLogBufferLen]) is false.
|
||||
// So lastSafe should be maxLogBufferLen-1.
|
||||
|
||||
// The first maxLogBufferLen-1 bytes (all 'A') should be processed.
|
||||
// The "你" should be in remainder.
|
||||
|
||||
if len(tl.remainder) != 3 {
|
||||
t.Errorf("Expected remainder len 3 (the char '你'), got %d", len(tl.remainder))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user