fix(log): 保留 Log Viewer 业务元数据(by AI)
This commit is contained in:
parent
59dbf323bf
commit
256b05a420
@ -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`,允许应用提供内置默认配置,并继续由配置文件和环境变量覆盖。
|
||||
|
||||
@ -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 合并,不会覆盖业务日志类型。
|
||||
|
||||
### 自定义日志扩展 (规范)
|
||||
|
||||
|
||||
12
TEST.md
12
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 验证
|
||||
|
||||
2
go.mod
2
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
|
||||
|
||||
4
go.sum
4
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=
|
||||
|
||||
2
meta.go
2
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
|
||||
|
||||
46
meta_test.go
46
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")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user