Compare commits

..

No commits in common. "main" and "v1.5.10" have entirely different histories.

7 changed files with 12 additions and 61 deletions

View File

@ -1,11 +1,5 @@
# Changelog # 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 ## [1.5.10] - 2026-08-24
- **默认配置扩展**: - **默认配置扩展**:
- 新增 `SetDefaultConfig`,允许应用提供内置默认配置,并继续由配置文件和环境变量覆盖。 - 新增 `SetDefaultConfig`,允许应用提供内置默认配置,并继续由配置文件和环境变量覆盖。

View File

@ -110,7 +110,6 @@ export LOG_FILE=console
5. **独立可视化工具 (`logv`)** 5. **独立可视化工具 (`logv`)**
* **安装**: `go install apigo.cc/go/log/logv@latest` * **安装**: `go install apigo.cc/go/log/logv@latest`
* **使用**: `tail -f app.log | logv``tail -f app.log | logv -json` * **使用**: `tail -f app.log | logv``tail -f app.log | logv -json`
* `logv` 从当前目录加载 `.log.meta.json`;基础 Meta 会与应用已有业务 Meta 合并,不会覆盖业务日志类型。
### 自定义日志扩展 (规范) ### 自定义日志扩展 (规范)

12
TEST.md
View File

@ -1,11 +1,5 @@
# Test Results # Test Results
## v1.5.11 验证
- 验证 Meta 同步保留磁盘已有业务类型,并更新当前进程注册的同名类型。
- 验证本地 `logv` 格式化 `request` 日志时恢复时间、字段映射、隐藏规则和 ANSI 颜色。
- 验证 `logv` 运行后 `.log.meta.json` 仍保留 `api_call``request`
- 全量单元测试、竞态测试和基准测试均通过。
## v1.5.10 验证 ## v1.5.10 验证
- 验证 split 文件 logger 在包初始化阶段安全创建,不依赖文件级 `init` 顺序。 - 验证 split 文件 logger 在包初始化阶段安全创建,不依赖文件级 `init` 顺序。
- 验证文件 logger 自动创建父目录。 - 验证文件 logger 自动创建父目录。
@ -17,9 +11,9 @@
### BenchmarkApple M3 Max / arm64 ### BenchmarkApple M3 Max / arm64
```text ```text
BenchmarkLoggerInfo-16 889536 1361 ns/op BenchmarkLoggerInfo-16 877782 1392 ns/op
BenchmarkLoggerAsyncConcurrent-16 4128828 286.5 ns/op BenchmarkLoggerAsyncConcurrent-16 4188966 284.4 ns/op
BenchmarkLogger_RequestLog_Realistic-16 1000000 1111 ns/op 975 B/op 27 allocs/op BenchmarkLogger_RequestLog_Realistic-16 973011 1124 ns/op 987 B/op 27 allocs/op
``` ```
## v1.5.9 验证 ## v1.5.9 验证

2
go.mod
View File

@ -7,7 +7,7 @@ require (
apigo.cc/go/config v1.5.4 apigo.cc/go/config v1.5.4
apigo.cc/go/file v1.5.6 apigo.cc/go/file v1.5.6
apigo.cc/go/id v1.5.7 apigo.cc/go/id v1.5.7
apigo.cc/go/shell v1.5.6 apigo.cc/go/shell v1.5.5
) )
require apigo.cc/go/jsmod v1.5.3 // indirect require apigo.cc/go/jsmod v1.5.3 // indirect

4
go.sum
View File

@ -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/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 h1:9p/BmdlVWLbekpKByZIFC09Qn8Wdhik2eINiwunBxPs=
apigo.cc/go/safe v1.5.3/go.mod h1:Ay8kEPL76DeXH4ifsVTc/3/sfGHlWLQjAp4vi7GA9AI= apigo.cc/go/safe v1.5.3/go.mod h1:Ay8kEPL76DeXH4ifsVTc/3/sfGHlWLQjAp4vi7GA9AI=
apigo.cc/go/shell v1.5.6 h1:2i93lNJ0oy/D5kOmlmOjwVjLPsW7vQBo0trLOF9AAAI= apigo.cc/go/shell v1.5.5 h1:sf1QZiL7IHkoOLdrYX1qT4JWIlYZIAAYGJJXorjscB4=
apigo.cc/go/shell v1.5.6/go.mod h1:Bp73DGKESOISWSIGUtL1dsFo5g4SI10GhgsnLJCTpsw= apigo.cc/go/shell v1.5.5/go.mod h1:FdZWUrcXHGJXo725oSyHqAeFoX0E9yY3PDhrz9hujgY=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=

View File

@ -228,7 +228,7 @@ func flattenStructFields(t reflect.Type, result *[]fieldInfo, parentIndex []int)
func syncMetaFile() { func syncMetaFile() {
metaLock.RLock() metaLock.RLock()
defer metaLock.RUnlock() defer metaLock.RUnlock()
_ = file.PatchFile(metaFilePath, metaRegistry) _ = file.MarshalFilePretty(metaFilePath, metaRegistry)
} }
// SetMetaFilePath allows changing the path for testing or configuration purposes // SetMetaFilePath allows changing the path for testing or configuration purposes

View File

@ -47,12 +47,8 @@ func (l *MockErrorLog) Reset() {
func TestMetaExtraction(t *testing.T) { func TestMetaExtraction(t *testing.T) {
// Setup custom meta file path for testing // Setup custom meta file path for testing
oldMetaFilePath := metaFilePath
SetMetaFilePath(".test.meta.json") SetMetaFilePath(".test.meta.json")
t.Cleanup(func() { defer os.Remove(".test.meta.json")
SetMetaFilePath(oldMetaFilePath)
_ = os.Remove(".test.meta.json")
})
RegisterType("mock_info", MockInfoLog{}) RegisterType("mock_info", MockInfoLog{})
RegisterType("mock_error", MockErrorLog{}) RegisterType("mock_error", MockErrorLog{})
@ -107,35 +103,3 @@ func TestMetaExtraction(t *testing.T) {
t.Errorf("expected at least 2 types in registry, got %d", len(registry)) 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, &registry); 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")
}
}