refactor: 重构trustlog-sdk目录结构到trustlog/go-trustlog

- 将所有trustlog-sdk文件移动到trustlog/go-trustlog/目录
- 更新README中所有import路径从trustlog-sdk改为go-trustlog
- 更新cookiecutter配置文件中的项目名称
- 更新根目录.lefthook.yml以引用新位置的配置
- 添加go.sum文件到版本控制
- 删除过时的示例文件

这次重构与trustlog-server保持一致的目录结构,
为未来支持多语言SDK(Python、Java等)预留空间。
This commit is contained in:
ryan
2025-12-22 13:37:57 +08:00
commit d313449c5c
87 changed files with 20622 additions and 0 deletions

183
api/logger/adapter.go Normal file
View File

@@ -0,0 +1,183 @@
package logger
import (
"context"
"sync"
"github.com/go-logr/logr"
)
// Logger 定义项目的日志接口基于logr但提供更清晰的抽象.
// 支持结构化日志和上下文感知的日志记录.
type Logger interface {
// Context-aware structured logging methods (推荐使用)
DebugContext(ctx context.Context, msg string, args ...any)
InfoContext(ctx context.Context, msg string, args ...any)
WarnContext(ctx context.Context, msg string, args ...any)
ErrorContext(ctx context.Context, msg string, args ...any)
// Non-context structured logging methods (用于适配器和内部组件)
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
}
// NewLogger 创建一个基于logr的Logger实现.
func NewLogger(logger logr.Logger) Logger {
return &logrAdapter{
logger: logger,
}
}
// NewDefaultLogger 创建一个默认的Logger实现.
// 注意logr需要显式提供一个LogSink实现这里返回一个discard logger.
func NewDefaultLogger() Logger {
return &logrAdapter{
logger: logr.Discard(),
}
}
// NopLogger 空操作日志器实现,所有日志方法都不执行任何操作.
// 适用于不需要日志输出的场景,如测试或性能敏感的场景.
type NopLogger struct{}
// NewNopLogger 创建新的空操作日志器.
func NewNopLogger() *NopLogger {
return &NopLogger{}
}
func (n *NopLogger) DebugContext(_ context.Context, _ string, _ ...any) {}
func (n *NopLogger) InfoContext(_ context.Context, _ string, _ ...any) {}
func (n *NopLogger) WarnContext(_ context.Context, _ string, _ ...any) {}
func (n *NopLogger) ErrorContext(_ context.Context, _ string, _ ...any) {}
func (n *NopLogger) Debug(_ string, _ ...any) {}
func (n *NopLogger) Info(_ string, _ ...any) {}
func (n *NopLogger) Warn(_ string, _ ...any) {}
func (n *NopLogger) Error(_ string, _ ...any) {}
// 全局日志器相关变量.
//
//nolint:gochecknoglobals // 全局日志器是必要的,用于提供便捷的日志访问接口
var (
globalLogger Logger = NewNopLogger() // 默认使用 NopLogger
globalLoggerLock sync.RWMutex
)
// SetGlobalLogger 设置全局日志器.
// 线程安全,可以在程序启动时调用以设置全局日志器.
func SetGlobalLogger(logger Logger) {
if logger == nil {
logger = NewNopLogger()
}
globalLoggerLock.Lock()
defer globalLoggerLock.Unlock()
globalLogger = logger
}
// GetGlobalLogger 获取全局日志器.
// 线程安全,返回当前设置的全局日志器,如果未设置则返回 NopLogger.
func GetGlobalLogger() Logger {
globalLoggerLock.RLock()
defer globalLoggerLock.RUnlock()
return globalLogger
}
// logrAdapter 是Logger接口的logr实现.
type logrAdapter struct {
logger logr.Logger
}
// convertArgs 将args转换为logr的key-value对格式.
// logr要求key-value成对出现如果args是奇数个最后一个会作为单独的value.
func convertArgs(args ...any) []any {
// 如果args已经是成对的key-value格式直接返回
if len(args)%2 == 0 {
return args
}
// 如果不是成对的,可能需要特殊处理
// 这里我们假设调用者传入的是key-value对
return args
}
func (l *logrAdapter) DebugContext(ctx context.Context, msg string, args ...any) {
_ = ctx // 保持接口兼容性logr目前不支持context
l.logger.V(1).Info(msg, convertArgs(args...)...)
}
func (l *logrAdapter) InfoContext(ctx context.Context, msg string, args ...any) {
_ = ctx // 保持接口兼容性logr目前不支持context
l.logger.Info(msg, convertArgs(args...)...)
}
func (l *logrAdapter) WarnContext(ctx context.Context, msg string, args ...any) {
_ = ctx // 保持接口兼容性logr目前不支持context
// logr没有Warn级别使用Info但标记为warning
kv := convertArgs(args...)
kv = append(kv, "level", "warning")
l.logger.Info(msg, kv...)
}
func (l *logrAdapter) ErrorContext(ctx context.Context, msg string, args ...any) {
_ = ctx // 保持接口兼容性logr目前不支持context
// 尝试从args中提取error
var err error
kvArgs := make([]any, 0, len(args))
for i := 0; i < len(args); i++ {
if i+1 < len(args) && args[i] == "error" {
if e, ok := args[i+1].(error); ok {
err = e
i++ // 跳过error值
continue
}
}
kvArgs = append(kvArgs, args[i])
}
if err != nil {
l.logger.Error(err, msg, convertArgs(kvArgs...)...)
} else {
// 如果没有error使用Info但标记为error级别
kvArgs = append(kvArgs, "level", "error")
l.logger.Info(msg, convertArgs(kvArgs...)...)
}
}
func (l *logrAdapter) Debug(msg string, args ...any) {
l.logger.V(1).Info(msg, convertArgs(args...)...)
}
func (l *logrAdapter) Info(msg string, args ...any) {
l.logger.Info(msg, convertArgs(args...)...)
}
func (l *logrAdapter) Warn(msg string, args ...any) {
// logr没有Warn级别使用Info但标记为warning
kv := convertArgs(args...)
kv = append(kv, "level", "warning")
l.logger.Info(msg, kv...)
}
func (l *logrAdapter) Error(msg string, args ...any) {
// 尝试从args中提取error
var err error
kvArgs := make([]any, 0, len(args))
for i := 0; i < len(args); i++ {
if i+1 < len(args) && args[i] == "error" {
if e, ok := args[i+1].(error); ok {
err = e
i++ // 跳过error值
continue
}
}
kvArgs = append(kvArgs, args[i])
}
if err != nil {
l.logger.Error(err, msg, convertArgs(kvArgs...)...)
} else {
// 如果没有error使用Info但标记为error级别
kvArgs = append(kvArgs, "level", "error")
l.logger.Info(msg, convertArgs(kvArgs...)...)
}
}

255
api/logger/adapter_test.go Normal file
View File

@@ -0,0 +1,255 @@
package logger_test
import (
"context"
"errors"
"testing"
"github.com/go-logr/logr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.yandata.net/iod/iod/trustlog-sdk/api/logger"
)
func TestNewLogger(t *testing.T) {
t.Parallel()
l := logr.Discard()
result := logger.NewLogger(l)
assert.NotNil(t, result)
}
func TestNewDefaultLogger(t *testing.T) {
t.Parallel()
result := logger.NewDefaultLogger()
assert.NotNil(t, result)
}
func TestNewNopLogger(t *testing.T) {
t.Parallel()
result := logger.NewNopLogger()
assert.NotNil(t, result)
}
func TestNopLogger_Methods(t *testing.T) {
t.Parallel()
nop := logger.NewNopLogger()
ctx := context.Background()
// All methods should not panic
assert.NotPanics(t, func() {
nop.DebugContext(ctx, "test")
nop.InfoContext(ctx, "test")
nop.WarnContext(ctx, "test")
nop.ErrorContext(ctx, "test")
nop.Debug("test")
nop.Info("test")
nop.Warn("test")
nop.Error("test")
})
}
func TestLogrAdapter_DebugContext(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
ctx := context.Background()
assert.NotPanics(t, func() {
adapter.DebugContext(ctx, "debug message", "key", "value")
})
}
func TestLogrAdapter_InfoContext(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
ctx := context.Background()
assert.NotPanics(t, func() {
adapter.InfoContext(ctx, "info message", "key", "value")
})
}
func TestLogrAdapter_WarnContext(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
ctx := context.Background()
assert.NotPanics(t, func() {
adapter.WarnContext(ctx, "warn message", "key", "value")
})
}
func TestLogrAdapter_ErrorContext(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
ctx := context.Background()
tests := []struct {
name string
args []any
}{
{
name: "with error",
args: []any{"error", errors.New("test error"), "key", "value"},
},
{
name: "without error",
args: []any{"key", "value"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
assert.NotPanics(t, func() {
adapter.ErrorContext(ctx, "error message", tt.args...)
})
})
}
}
func TestLogrAdapter_Debug(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
assert.NotPanics(t, func() {
adapter.Debug("debug message", "key", "value")
})
}
func TestLogrAdapter_Info(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
assert.NotPanics(t, func() {
adapter.Info("info message", "key", "value")
})
}
func TestLogrAdapter_Warn(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
assert.NotPanics(t, func() {
adapter.Warn("warn message", "key", "value")
})
}
func TestLogrAdapter_Error(t *testing.T) {
t.Parallel()
l := logr.Discard()
adapter := logger.NewLogger(l)
tests := []struct {
name string
args []any
}{
{
name: "with error",
args: []any{"error", errors.New("test error"), "key", "value"},
},
{
name: "without error",
args: []any{"key", "value"},
},
{
name: "odd number of args",
args: []any{"key", "value", "extra"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
assert.NotPanics(t, func() {
adapter.Error("error message", tt.args...)
})
})
}
}
func TestSetGlobalLogger(t *testing.T) {
t.Parallel()
original := logger.GetGlobalLogger()
defer logger.SetGlobalLogger(original)
newLogger := logger.NewNopLogger()
logger.SetGlobalLogger(newLogger)
result := logger.GetGlobalLogger()
assert.Equal(t, newLogger, result)
}
func TestSetGlobalLogger_Nil(t *testing.T) {
t.Parallel()
original := logger.GetGlobalLogger()
defer logger.SetGlobalLogger(original)
logger.SetGlobalLogger(nil)
result := logger.GetGlobalLogger()
assert.NotNil(t, result) // Should be NopLogger
}
func TestGetGlobalLogger(t *testing.T) {
t.Parallel()
result := logger.GetGlobalLogger()
assert.NotNil(t, result)
}
func TestGlobalLogger_ConcurrentAccess(t *testing.T) {
t.Parallel()
original := logger.GetGlobalLogger()
defer logger.SetGlobalLogger(original)
// Test concurrent reads
done := make(chan bool, 10)
for range 10 {
go func() {
_ = logger.GetGlobalLogger()
done <- true
}()
}
for range 10 {
<-done
}
// Test concurrent writes
newLogger := logger.NewNopLogger()
for range 5 {
go func() {
logger.SetGlobalLogger(newLogger)
done <- true
}()
}
for range 5 {
<-done
}
result := logger.GetGlobalLogger()
require.NotNil(t, result)
}