mirror of
https://github.com/uptrace/go-clickhouse.git
synced 2025-06-08 23:26:11 +02:00
107 lines
2.2 KiB
Go
107 lines
2.2 KiB
Go
|
package chotel
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"database/sql"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
|
||
|
"go.opentelemetry.io/otel"
|
||
|
"go.opentelemetry.io/otel/attribute"
|
||
|
"go.opentelemetry.io/otel/codes"
|
||
|
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
|
||
|
"go.opentelemetry.io/otel/trace"
|
||
|
|
||
|
"github.com/uptrace/go-clickhouse/ch"
|
||
|
)
|
||
|
|
||
|
var tracer = otel.Tracer("go-clickhouse")
|
||
|
|
||
|
type QueryHook struct{}
|
||
|
|
||
|
var _ ch.QueryHook = (*QueryHook)(nil)
|
||
|
|
||
|
func NewQueryHook() *QueryHook {
|
||
|
return &QueryHook{}
|
||
|
}
|
||
|
|
||
|
func (h *QueryHook) BeforeQuery(
|
||
|
ctx context.Context, evt *ch.QueryEvent,
|
||
|
) context.Context {
|
||
|
if !trace.SpanFromContext(ctx).IsRecording() {
|
||
|
return ctx
|
||
|
}
|
||
|
|
||
|
ctx, _ = tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||
|
return ctx
|
||
|
}
|
||
|
|
||
|
func (h *QueryHook) AfterQuery(ctx context.Context, event *ch.QueryEvent) {
|
||
|
span := trace.SpanFromContext(ctx)
|
||
|
if !span.IsRecording() {
|
||
|
return
|
||
|
}
|
||
|
defer span.End()
|
||
|
|
||
|
operation := event.Operation()
|
||
|
fn, file, line := funcFileLine("go-clickhouse")
|
||
|
span.SetName(operation)
|
||
|
|
||
|
attrs := []attribute.KeyValue{
|
||
|
semconv.CodeFunctionKey.String(fn),
|
||
|
semconv.CodeFilepathKey.String(file),
|
||
|
semconv.CodeLineNumberKey.Int(line),
|
||
|
semconv.DBSystemKey.String("clickhouse"),
|
||
|
semconv.DBOperationKey.String(operation),
|
||
|
semconv.DBStatementKey.String(event.Query),
|
||
|
}
|
||
|
|
||
|
if event.IQuery != nil {
|
||
|
if tableName := event.IQuery.GetTableName(); tableName != "" {
|
||
|
attrs = append(attrs, semconv.DBSQLTableKey.String(tableName))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
span.SetAttributes(attrs...)
|
||
|
|
||
|
switch event.Err {
|
||
|
case nil, sql.ErrNoRows:
|
||
|
default:
|
||
|
span.SetStatus(codes.Error, "")
|
||
|
span.RecordError(event.Err)
|
||
|
}
|
||
|
|
||
|
if event.Result != nil {
|
||
|
numRow, err := event.Result.RowsAffected()
|
||
|
if err == nil {
|
||
|
span.SetAttributes(attribute.Int64("db.rows_affected", numRow))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func funcFileLine(pkg string) (string, string, int) {
|
||
|
const depth = 16
|
||
|
var pcs [depth]uintptr
|
||
|
n := runtime.Callers(3, pcs[:])
|
||
|
ff := runtime.CallersFrames(pcs[:n])
|
||
|
|
||
|
var fn, file string
|
||
|
var line int
|
||
|
for {
|
||
|
f, ok := ff.Next()
|
||
|
if !ok {
|
||
|
break
|
||
|
}
|
||
|
fn, file, line = f.Function, f.File, f.Line
|
||
|
if !strings.Contains(fn, pkg) {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ind := strings.LastIndexByte(fn, '/'); ind != -1 {
|
||
|
fn = fn[ind+1:]
|
||
|
}
|
||
|
|
||
|
return fn, file, line
|
||
|
}
|