pkg/rules/goredis/goredis_otel_instrumenter.go (134 lines of code) (raw):
// Copyright (c) 2024 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package goredis
import (
"fmt"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/db"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/instrumenter"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/utils"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/version"
redis "github.com/redis/go-redis/v9"
"go.opentelemetry.io/otel/sdk/instrumentation"
"strconv"
"time"
"unicode/utf8"
"unsafe"
)
type goRedisAttrsGetter struct {
}
func (d goRedisAttrsGetter) GetSystem(request goRedisRequest) string {
return "redis"
}
func (d goRedisAttrsGetter) GetServerAddress(request goRedisRequest) string {
return request.endpoint
}
func (d goRedisAttrsGetter) GetStatement(request goRedisRequest) string {
b := make([]byte, 0, 64)
for i, arg := range request.cmd.Args() {
if i > 0 {
b = append(b, ' ')
}
b = redisV9AppendArg(b, arg)
}
if err := request.cmd.Err(); err != nil {
b = append(b, ": "...)
b = append(b, err.Error()...)
}
if cmd, ok := request.cmd.(*redis.Cmd); ok {
b = append(b, ": "...)
b = redisV9AppendArg(b, cmd)
}
return redisV9String(b)
}
func (d goRedisAttrsGetter) GetOperation(request goRedisRequest) string {
return request.cmd.FullName()
}
func (d goRedisAttrsGetter) GetCollection(request goRedisRequest) string {
// TBD: We need to implement retrieving the collection later.
return ""
}
func (d goRedisAttrsGetter) GetParameters(request goRedisRequest) []any {
return nil
}
func (d goRedisAttrsGetter) GetDbNamespace(request goRedisRequest) string {
return ""
}
func (d goRedisAttrsGetter) GetBatchSize(request goRedisRequest) int {
return 0
}
func BuildGoRedisOtelInstrumenter() instrumenter.Instrumenter[goRedisRequest, any] {
builder := instrumenter.Builder[goRedisRequest, any]{}
getter := goRedisAttrsGetter{}
return builder.Init().SetSpanNameExtractor(&db.DBSpanNameExtractor[goRedisRequest]{Getter: getter}).SetSpanKindExtractor(&instrumenter.AlwaysClientExtractor[goRedisRequest]{}).
AddAttributesExtractor(&db.DbClientAttrsExtractor[goRedisRequest, any, db.DbClientAttrsGetter[goRedisRequest]]{Base: db.DbClientCommonAttrsExtractor[goRedisRequest, any, db.DbClientAttrsGetter[goRedisRequest]]{Getter: getter}}).
AddOperationListeners(db.DbClientMetrics("nosql.goredisv9")).
SetInstrumentationScope(instrumentation.Scope{
Name: utils.GO_REDIS_V9_SCOPE_NAME,
Version: version.Tag,
}).
BuildInstrumenter()
}
func redisV9String(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
func redisV9AppendUTF8String(dst []byte, src []byte) []byte {
dst = append(dst, src...)
return dst
}
func redisV9Bytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(
&struct {
string
Cap int
}{s, len(s)},
))
}
func redisV9AppendArg(b []byte, v interface{}) []byte {
switch v := v.(type) {
case nil:
return append(b, "<nil>"...)
case string:
bts := redisV9Bytes(v)
if utf8.Valid(bts) {
return redisV9AppendUTF8String(b, bts)
} else {
return redisV9AppendUTF8String(b, redisV9Bytes("<string>"))
}
case []byte:
if utf8.Valid(v) {
return redisV9AppendUTF8String(b, v)
} else {
return redisV9AppendUTF8String(b, redisV9Bytes("<byte>"))
}
case int:
return strconv.AppendInt(b, int64(v), 10)
case int8:
return strconv.AppendInt(b, int64(v), 10)
case int16:
return strconv.AppendInt(b, int64(v), 10)
case int32:
return strconv.AppendInt(b, int64(v), 10)
case int64:
return strconv.AppendInt(b, v, 10)
case uint:
return strconv.AppendUint(b, uint64(v), 10)
case uint8:
return strconv.AppendUint(b, uint64(v), 10)
case uint16:
return strconv.AppendUint(b, uint64(v), 10)
case uint32:
return strconv.AppendUint(b, uint64(v), 10)
case uint64:
return strconv.AppendUint(b, v, 10)
case float32:
return strconv.AppendFloat(b, float64(v), 'f', -1, 64)
case float64:
return strconv.AppendFloat(b, v, 'f', -1, 64)
case bool:
if v {
return append(b, "true"...)
}
return append(b, "false"...)
case time.Time:
return v.AppendFormat(b, time.RFC3339Nano)
default:
return append(b, fmt.Sprint(v)...)
}
}