feat(db): 添加数据库配置自动查找和缓存功能
- 实现配置文件自动查找功能,支持yaml、yml、toml、ini、json格式 - 添加查询缓存机制,提高重复查询性能 - 新增构建脚本build.sh和build.bat用于跨平台编译 - 添加完整的数据库连接配置和时间字段配置功能 - 实现DAO基类提供通用CRUD操作方法 - 添加配置文件示例和相关测试用例
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// Tracer 数据库操作追踪器
|
||||
type Tracer struct {
|
||||
tracer trace.Tracer
|
||||
config *TracerConfig
|
||||
}
|
||||
|
||||
// TracerConfig 追踪器配置
|
||||
type TracerConfig struct {
|
||||
ServiceName string // 服务名称
|
||||
DBName string // 数据库名称
|
||||
DBSystem string // 数据库类型(mysql/postgresql/sqlite)
|
||||
}
|
||||
|
||||
// NewTracer 创建数据库追踪器
|
||||
func NewTracer(config *TracerConfig) *Tracer {
|
||||
return &Tracer{
|
||||
tracer: otel.Tracer(config.ServiceName),
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// TraceQuery 追踪查询操作
|
||||
func (t *Tracer) TraceQuery(ctx context.Context, query string, args []interface{}) (context.Context, error) {
|
||||
// 创建 Span
|
||||
spanName := fmt.Sprintf("DB Query: %s", t.getOperationName(query))
|
||||
ctx, span := t.tracer.Start(ctx, spanName,
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// 设置属性
|
||||
span.SetAttributes(
|
||||
attribute.String("db.system", t.config.DBSystem),
|
||||
attribute.String("db.name", t.config.DBName),
|
||||
attribute.String("db.statement", query),
|
||||
attribute.StringSlice("db.args", t.argsToString(args)),
|
||||
)
|
||||
|
||||
// 返回包含 Span 的 context
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// RecordError 记录错误
|
||||
func (t *Tracer) RecordError(ctx context.Context, err error) {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span.IsRecording() {
|
||||
span.RecordError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// RecordAffectedRows 记录影响的行数
|
||||
func (t *Tracer) RecordAffectedRows(ctx context.Context, rows int64) {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attribute.Int64("db.rows_affected", rows))
|
||||
}
|
||||
}
|
||||
|
||||
// getOperationName 从 SQL 获取操作名称
|
||||
func (t *Tracer) getOperationName(sql string) string {
|
||||
if len(sql) < 6 {
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
prefix := sql[:6]
|
||||
switch prefix {
|
||||
case "SELECT":
|
||||
return "SELECT"
|
||||
case "INSERT":
|
||||
return "INSERT"
|
||||
case "UPDATE":
|
||||
return "UPDATE"
|
||||
case "DELETE":
|
||||
return "DELETE"
|
||||
default:
|
||||
return "OTHER"
|
||||
}
|
||||
}
|
||||
|
||||
// argsToString 将参数转换为字符串切片
|
||||
func (t *Tracer) argsToString(args []interface{}) []string {
|
||||
result := make([]string, len(args))
|
||||
for i, arg := range args {
|
||||
result[i] = fmt.Sprintf("%v", arg)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// WithTrace 在查询中启用追踪
|
||||
func WithTrace(ctx context.Context, db *sql.DB, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
// 获取追踪器(从全局或上下文中)
|
||||
tracer := getTracerFromContext(ctx)
|
||||
|
||||
if tracer != nil {
|
||||
var err error
|
||||
ctx, err = tracer.TraceQuery(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(start time.Time) {
|
||||
duration := time.Since(start)
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attribute.Int64("db.duration_ms", duration.Milliseconds()))
|
||||
}
|
||||
}(time.Now())
|
||||
}
|
||||
|
||||
// 执行实际查询
|
||||
return db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// ExecWithTrace 在执行中启用追踪
|
||||
func ExecWithTrace(ctx context.Context, db *sql.DB, query string, args ...interface{}) (sql.Result, error) {
|
||||
tracer := getTracerFromContext(ctx)
|
||||
|
||||
if tracer != nil {
|
||||
var err error
|
||||
ctx, err = tracer.TraceQuery(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(start time.Time) {
|
||||
duration := time.Since(start)
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span.IsRecording() {
|
||||
span.SetAttributes(attribute.Int64("db.duration_ms", duration.Milliseconds()))
|
||||
}
|
||||
}(time.Now())
|
||||
}
|
||||
|
||||
// 执行实际操作
|
||||
result, err := db.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
if tracer != nil {
|
||||
tracer.RecordError(ctx, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录影响的行数
|
||||
if tracer != nil {
|
||||
rows, _ := result.RowsAffected()
|
||||
tracer.RecordAffectedRows(ctx, rows)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// contextKey 上下文键类型
|
||||
type contextKey string
|
||||
|
||||
const tracerKey contextKey = "db_tracer"
|
||||
|
||||
// ContextWithTracer 将追踪器存入上下文
|
||||
func ContextWithTracer(ctx context.Context, tracer *Tracer) context.Context {
|
||||
return context.WithValue(ctx, tracerKey, tracer)
|
||||
}
|
||||
|
||||
// getTracerFromContext 从上下文获取追踪器
|
||||
func getTracerFromContext(ctx context.Context) *Tracer {
|
||||
if tracer, ok := ctx.Value(tracerKey).(*Tracer); ok {
|
||||
return tracer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user