Skip to content

Commit 0e6d07e

Browse files
authored
Use pre-Explain SQL for WithoutQueryVariables (#15)
The current implementation of `WithoutQueryVariables` substitutes `?` for any query variables and calls `tx.Dialector.Explain`, which can result in a lot of string allocations. This implementation avoids the substitution and uses whatever the default representation of the query parameters is for the SQL dialect.
1 parent 2a6e1e2 commit 0e6d07e

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

tracing/tracing.go

+5-8
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,14 @@ func (p *otelPlugin) after() gormHookFunc {
116116
}
117117

118118
vars := tx.Statement.Vars
119-
if p.excludeQueryVars {
120-
// Replace query variables with '?' to mask them
121-
vars = make([]interface{}, len(tx.Statement.Vars))
122119

123-
for i := 0; i < len(vars); i++ {
124-
vars[i] = "?"
125-
}
120+
var query string
121+
if p.excludeQueryVars {
122+
query = tx.Statement.SQL.String()
123+
} else {
124+
query = tx.Dialector.Explain(tx.Statement.SQL.String(), vars...)
126125
}
127126

128-
query := tx.Dialector.Explain(tx.Statement.SQL.String(), vars...)
129-
130127
attrs = append(attrs, semconv.DBStatementKey.String(p.formatQuery(query)))
131128
if tx.Statement.Table != "" {
132129
attrs = append(attrs, semconv.DBSQLTableKey.String(tx.Statement.Table))

tracing/tracing_test.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
)
1818

1919
type Test struct {
20+
opts []Option
2021
do func(ctx context.Context, db *gorm.DB)
2122
require func(t *testing.T, spans []sdktrace.ReadOnlySpan)
2223
}
@@ -70,6 +71,35 @@ func TestOtel(t *testing.T) {
7071
require.Equal(t, "SELECT foo_bar", stmt.AsString())
7172
},
7273
},
74+
{
75+
opts: []Option{WithoutQueryVariables()},
76+
do: func(ctx context.Context, db *gorm.DB) {
77+
err := db.Exec("CREATE TABLE foo (id int)").Error
78+
require.NoError(t, err)
79+
var num int
80+
param := 42
81+
err = db.WithContext(ctx).Table("foo").Select("id", param).Where("id = ?", param).Scan(&num).Error
82+
require.NoError(t, err)
83+
},
84+
require: func(t *testing.T, spans []sdktrace.ReadOnlySpan) {
85+
for _, s := range spans {
86+
fmt.Printf("span=%#v\n", s)
87+
}
88+
require.Equal(t, 2, len(spans))
89+
require.Equal(t, "gorm.Row", spans[1].Name())
90+
require.Equal(t, trace.SpanKindClient, spans[1].SpanKind())
91+
92+
m := attrMap(spans[1].Attributes())
93+
94+
sys, ok := m[semconv.DBSystemKey]
95+
require.True(t, ok)
96+
require.Equal(t, "sqlite", sys.AsString())
97+
98+
stmt, ok := m[semconv.DBStatementKey]
99+
require.True(t, ok)
100+
require.Equal(t, "SELECT id FROM `foo` WHERE id = ?", stmt.AsString())
101+
},
102+
},
73103
}
74104

75105
for i, test := range tests {
@@ -80,7 +110,7 @@ func TestOtel(t *testing.T) {
80110
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
81111
require.NoError(t, err)
82112

83-
err = db.Use(NewPlugin(WithTracerProvider(provider)))
113+
err = db.Use(NewPlugin(append(test.opts, WithTracerProvider(provider))...))
84114
require.NoError(t, err)
85115

86116
test.do(context.TODO(), db)

0 commit comments

Comments
 (0)