mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-02 08:52:26 -06:00
feat: initial tracing support (#1623)
This commit is contained in:
parent
878ed48de3
commit
6392e00653
472 changed files with 102600 additions and 12 deletions
24
vendor/github.com/uptrace/bun/extra/bunotel/LICENSE
generated
vendored
Normal file
24
vendor/github.com/uptrace/bun/extra/bunotel/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2021 Vladimir Mihailenco. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
3
vendor/github.com/uptrace/bun/extra/bunotel/README.md
generated
vendored
Normal file
3
vendor/github.com/uptrace/bun/extra/bunotel/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# OpenTelemetry instrumentation for Bun
|
||||
|
||||
See [example](../example/opentelemetry) for details.
|
||||
32
vendor/github.com/uptrace/bun/extra/bunotel/option.go
generated
vendored
Normal file
32
vendor/github.com/uptrace/bun/extra/bunotel/option.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package bunotel
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
|
||||
)
|
||||
|
||||
type Option func(h *QueryHook)
|
||||
|
||||
// WithAttributes configures attributes that are used to create a span.
|
||||
func WithAttributes(attrs ...attribute.KeyValue) Option {
|
||||
return func(h *QueryHook) {
|
||||
h.attrs = append(h.attrs, attrs...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDBName configures a db.name attribute.
|
||||
func WithDBName(name string) Option {
|
||||
return func(h *QueryHook) {
|
||||
h.attrs = append(h.attrs, semconv.DBNameKey.String(name))
|
||||
}
|
||||
}
|
||||
|
||||
// WithFormattedQueries enables formatting of the query that is added
|
||||
// as the statement attribute to the trace.
|
||||
// This means that all placeholders and arguments will be filled first
|
||||
// and the query will contain all information as sent to the database.
|
||||
func WithFormattedQueries(format bool) Option {
|
||||
return func(h *QueryHook) {
|
||||
h.formatQueries = format
|
||||
}
|
||||
}
|
||||
188
vendor/github.com/uptrace/bun/extra/bunotel/otel.go
generated
vendored
Normal file
188
vendor/github.com/uptrace/bun/extra/bunotel/otel.go
generated
vendored
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package bunotel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric/global"
|
||||
"go.opentelemetry.io/otel/metric/instrument"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
"github.com/uptrace/bun/schema"
|
||||
"github.com/uptrace/opentelemetry-go-extra/otelsql"
|
||||
)
|
||||
|
||||
var (
|
||||
tracer = otel.Tracer("github.com/uptrace/bun")
|
||||
meter = global.Meter("github.com/uptrace/bun")
|
||||
|
||||
queryHistogram, _ = meter.Int64Histogram(
|
||||
"go.sql.query_timing",
|
||||
instrument.WithDescription("Timing of processed queries"),
|
||||
instrument.WithUnit("milliseconds"),
|
||||
)
|
||||
)
|
||||
|
||||
type QueryHook struct {
|
||||
attrs []attribute.KeyValue
|
||||
formatQueries bool
|
||||
}
|
||||
|
||||
var _ bun.QueryHook = (*QueryHook)(nil)
|
||||
|
||||
func NewQueryHook(opts ...Option) *QueryHook {
|
||||
h := new(QueryHook)
|
||||
for _, opt := range opts {
|
||||
opt(h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *QueryHook) Init(db *bun.DB) {
|
||||
labels := make([]attribute.KeyValue, 0, len(h.attrs)+1)
|
||||
labels = append(labels, h.attrs...)
|
||||
if sys := dbSystem(db); sys.Valid() {
|
||||
labels = append(labels, sys)
|
||||
}
|
||||
|
||||
otelsql.ReportDBStatsMetrics(db.DB, otelsql.WithAttributes(labels...))
|
||||
}
|
||||
|
||||
func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context {
|
||||
ctx, _ = tracer.Start(ctx, "", trace.WithSpanKind(trace.SpanKindClient))
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
|
||||
operation := event.Operation()
|
||||
dbOperation := semconv.DBOperationKey.String(operation)
|
||||
|
||||
labels := make([]attribute.KeyValue, 0, len(h.attrs)+2)
|
||||
labels = append(labels, h.attrs...)
|
||||
labels = append(labels, dbOperation)
|
||||
if event.IQuery != nil {
|
||||
if tableName := event.IQuery.GetTableName(); tableName != "" {
|
||||
labels = append(labels, semconv.DBSQLTableKey.String(tableName))
|
||||
}
|
||||
}
|
||||
|
||||
queryHistogram.Record(ctx, time.Since(event.StartTime).Milliseconds(), labels...)
|
||||
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if !span.IsRecording() {
|
||||
return
|
||||
}
|
||||
|
||||
span.SetName(operation)
|
||||
defer span.End()
|
||||
|
||||
query := h.eventQuery(event)
|
||||
fn, file, line := funcFileLine("github.com/uptrace/bun")
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, 10)
|
||||
attrs = append(attrs, h.attrs...)
|
||||
attrs = append(attrs,
|
||||
dbOperation,
|
||||
semconv.DBStatementKey.String(query),
|
||||
semconv.CodeFunctionKey.String(fn),
|
||||
semconv.CodeFilepathKey.String(file),
|
||||
semconv.CodeLineNumberKey.Int(line),
|
||||
)
|
||||
|
||||
if sys := dbSystem(event.DB); sys.Valid() {
|
||||
attrs = append(attrs, sys)
|
||||
}
|
||||
if event.Result != nil {
|
||||
if n, _ := event.Result.RowsAffected(); n > 0 {
|
||||
attrs = append(attrs, attribute.Int64("db.rows_affected", n))
|
||||
}
|
||||
}
|
||||
|
||||
switch event.Err {
|
||||
case nil, sql.ErrNoRows, sql.ErrTxDone:
|
||||
// ignore
|
||||
default:
|
||||
span.RecordError(event.Err)
|
||||
span.SetStatus(codes.Error, event.Err.Error())
|
||||
}
|
||||
|
||||
span.SetAttributes(attrs...)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (h *QueryHook) eventQuery(event *bun.QueryEvent) string {
|
||||
const softQueryLimit = 8000
|
||||
const hardQueryLimit = 16000
|
||||
|
||||
var query string
|
||||
|
||||
if h.formatQueries && len(event.Query) <= softQueryLimit {
|
||||
query = event.Query
|
||||
} else {
|
||||
query = unformattedQuery(event)
|
||||
}
|
||||
|
||||
if len(query) > hardQueryLimit {
|
||||
query = query[:hardQueryLimit]
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func unformattedQuery(event *bun.QueryEvent) string {
|
||||
if event.IQuery != nil {
|
||||
if b, err := event.IQuery.AppendQuery(schema.NewNopFormatter(), nil); err == nil {
|
||||
return bytesToString(b)
|
||||
}
|
||||
}
|
||||
return string(event.QueryTemplate)
|
||||
}
|
||||
|
||||
func dbSystem(db *bun.DB) attribute.KeyValue {
|
||||
switch db.Dialect().Name() {
|
||||
case dialect.PG:
|
||||
return semconv.DBSystemPostgreSQL
|
||||
case dialect.MySQL:
|
||||
return semconv.DBSystemMySQL
|
||||
case dialect.SQLite:
|
||||
return semconv.DBSystemSqlite
|
||||
case dialect.MSSQL:
|
||||
return semconv.DBSystemMSSQL
|
||||
default:
|
||||
return attribute.KeyValue{}
|
||||
}
|
||||
}
|
||||
11
vendor/github.com/uptrace/bun/extra/bunotel/safe.go
generated
vendored
Normal file
11
vendor/github.com/uptrace/bun/extra/bunotel/safe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// +build appengine
|
||||
|
||||
package internal
|
||||
|
||||
func bytesToString(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func stringToBytes(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
18
vendor/github.com/uptrace/bun/extra/bunotel/unsafe.go
generated
vendored
Normal file
18
vendor/github.com/uptrace/bun/extra/bunotel/unsafe.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// +build !appengine
|
||||
|
||||
package bunotel
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func bytesToString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
func stringToBytes(s string) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(
|
||||
&struct {
|
||||
string
|
||||
Cap int
|
||||
}{s, len(s)},
|
||||
))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue