From 256b05a42025f11ced4d3abf5b03052a9178f7ad Mon Sep 17 00:00:00 2001 From: Star Date: Mon, 24 Aug 2026 11:40:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(log):=20=E4=BF=9D=E7=95=99=20Log=20Viewer?= =?UTF-8?q?=20=E4=B8=9A=E5=8A=A1=E5=85=83=E6=95=B0=E6=8D=AE=EF=BC=88by=20A?= =?UTF-8?q?I=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ README.md | 1 + TEST.md | 12 +++++++++--- go.mod | 2 +- go.sum | 4 ++-- meta.go | 2 +- meta_test.go | 46 +++++++++++++++++++++++++++++++++++++++++----- 7 files changed, 61 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 053f639..f99f960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [1.5.11] - 2026-08-24 +- **Log Viewer 元数据修复**: + - Meta 持久化改为合并磁盘已有类型,避免独立 `logv` 启动时用基础日志类型覆盖应用的 `api_call`、`request` 等业务 Meta。 + - 修复业务日志在 `logv` 中退化为无颜色、无字段映射的数组输出。 +- **依赖更新**: 对齐 `shell v1.5.6`。 + ## [1.5.10] - 2026-08-24 - **默认配置扩展**: - 新增 `SetDefaultConfig`,允许应用提供内置默认配置,并继续由配置文件和环境变量覆盖。 diff --git a/README.md b/README.md index 628e183..b75004e 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ export LOG_FILE=console 5. **独立可视化工具 (`logv`)** * **安装**: `go install apigo.cc/go/log/logv@latest` * **使用**: `tail -f app.log | logv` 或 `tail -f app.log | logv -json`。 + * `logv` 从当前目录加载 `.log.meta.json`;基础 Meta 会与应用已有业务 Meta 合并,不会覆盖业务日志类型。 ### 自定义日志扩展 (规范) diff --git a/TEST.md b/TEST.md index 0473d51..f0ec4e8 100644 --- a/TEST.md +++ b/TEST.md @@ -1,5 +1,11 @@ # Test Results +## v1.5.11 验证 +- 验证 Meta 同步保留磁盘已有业务类型,并更新当前进程注册的同名类型。 +- 验证本地 `logv` 格式化 `request` 日志时恢复时间、字段映射、隐藏规则和 ANSI 颜色。 +- 验证 `logv` 运行后 `.log.meta.json` 仍保留 `api_call` 与 `request`。 +- 全量单元测试、竞态测试和基准测试均通过。 + ## v1.5.10 验证 - 验证 split 文件 logger 在包初始化阶段安全创建,不依赖文件级 `init` 顺序。 - 验证文件 logger 自动创建父目录。 @@ -11,9 +17,9 @@ ### Benchmark(Apple M3 Max / arm64) ```text -BenchmarkLoggerInfo-16 877782 1392 ns/op -BenchmarkLoggerAsyncConcurrent-16 4188966 284.4 ns/op -BenchmarkLogger_RequestLog_Realistic-16 973011 1124 ns/op 987 B/op 27 allocs/op +BenchmarkLoggerInfo-16 889536 1361 ns/op +BenchmarkLoggerAsyncConcurrent-16 4128828 286.5 ns/op +BenchmarkLogger_RequestLog_Realistic-16 1000000 1111 ns/op 975 B/op 27 allocs/op ``` ## v1.5.9 验证 diff --git a/go.mod b/go.mod index 96e9457..05337eb 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( apigo.cc/go/config v1.5.4 apigo.cc/go/file v1.5.6 apigo.cc/go/id v1.5.7 - apigo.cc/go/shell v1.5.5 + apigo.cc/go/shell v1.5.6 ) require apigo.cc/go/jsmod v1.5.3 // indirect diff --git a/go.sum b/go.sum index fbddea6..556c643 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ apigo.cc/go/rand v1.5.4 h1:eessFBsKQuoOYdzrStldOGw9f4HqbeO87X95bI4jIBQ= apigo.cc/go/rand v1.5.4/go.mod h1:q1BTFkY/cXE229dDD5Q22lF7T0DoKPV6xAu+6bCrDH4= apigo.cc/go/safe v1.5.3 h1:9p/BmdlVWLbekpKByZIFC09Qn8Wdhik2eINiwunBxPs= apigo.cc/go/safe v1.5.3/go.mod h1:Ay8kEPL76DeXH4ifsVTc/3/sfGHlWLQjAp4vi7GA9AI= -apigo.cc/go/shell v1.5.5 h1:sf1QZiL7IHkoOLdrYX1qT4JWIlYZIAAYGJJXorjscB4= -apigo.cc/go/shell v1.5.5/go.mod h1:FdZWUrcXHGJXo725oSyHqAeFoX0E9yY3PDhrz9hujgY= +apigo.cc/go/shell v1.5.6 h1:2i93lNJ0oy/D5kOmlmOjwVjLPsW7vQBo0trLOF9AAAI= +apigo.cc/go/shell v1.5.6/go.mod h1:Bp73DGKESOISWSIGUtL1dsFo5g4SI10GhgsnLJCTpsw= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/meta.go b/meta.go index 46df143..2eaee61 100644 --- a/meta.go +++ b/meta.go @@ -228,7 +228,7 @@ func flattenStructFields(t reflect.Type, result *[]fieldInfo, parentIndex []int) func syncMetaFile() { metaLock.RLock() defer metaLock.RUnlock() - _ = file.MarshalFilePretty(metaFilePath, metaRegistry) + _ = file.PatchFile(metaFilePath, metaRegistry) } // SetMetaFilePath allows changing the path for testing or configuration purposes diff --git a/meta_test.go b/meta_test.go index 28e1a0a..469e568 100644 --- a/meta_test.go +++ b/meta_test.go @@ -16,12 +16,12 @@ func (b *MockBaseLog) Reset() { b.BaseField2 = 0 } -func (b *MockBaseLog) IsLogEntry() bool { return true } +func (b *MockBaseLog) IsLogEntry() bool { return true } func (b *MockBaseLog) GetBaseLog() *BaseLog { return &BaseLog{} } type MockInfoLog struct { MockBaseLog - Message string `log:"pos:2"` + Message string `log:"pos:2"` Extra map[string]any `log:"pos:1000"` } @@ -33,8 +33,8 @@ func (l *MockInfoLog) Reset() { type MockErrorLog struct { MockBaseLog - Error string `log:"pos:2,color:red"` - CallStacks []string `log:"pos:1001"` + Error string `log:"pos:2,color:red"` + CallStacks []string `log:"pos:1001"` Extra map[string]any `log:"pos:1000"` } @@ -47,8 +47,12 @@ func (l *MockErrorLog) Reset() { func TestMetaExtraction(t *testing.T) { // Setup custom meta file path for testing + oldMetaFilePath := metaFilePath SetMetaFilePath(".test.meta.json") - defer os.Remove(".test.meta.json") + t.Cleanup(func() { + SetMetaFilePath(oldMetaFilePath) + _ = os.Remove(".test.meta.json") + }) RegisterType("mock_info", MockInfoLog{}) RegisterType("mock_error", MockErrorLog{}) @@ -103,3 +107,35 @@ func TestMetaExtraction(t *testing.T) { t.Errorf("expected at least 2 types in registry, got %d", len(registry)) } } + +func TestSyncMetaFilePreservesExistingTypes(t *testing.T) { + oldMetaFilePath := metaFilePath + metaPath := ".test.merge.meta.json" + SetMetaFilePath(metaPath) + t.Cleanup(func() { + SetMetaFilePath(oldMetaFilePath) + _ = os.Remove(metaPath) + }) + + existing := `{"api_call":[{"Index":0,"Name":"Action","Color":"cyan"}]}` + if err := os.WriteFile(metaPath, []byte(existing), 0644); err != nil { + t.Fatal(err) + } + + RegisterType("mock_merge", MockInfoLog{}) + + data, err := os.ReadFile(metaPath) + if err != nil { + t.Fatal(err) + } + var registry map[string][]MetaField + if err := json.Unmarshal(data, ®istry); err != nil { + t.Fatal(err) + } + if len(registry["api_call"]) != 1 || registry["api_call"][0].Name != "Action" { + t.Fatalf("existing business metadata was not preserved: %+v", registry["api_call"]) + } + if len(registry["mock_merge"]) == 0 { + t.Fatal("newly registered metadata was not written") + } +}