summaryrefslogtreecommitdiff
path: root/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql
diff options
context:
space:
mode:
authorLibravatar Dominik Süß <dominik@suess.wtf>2023-05-09 19:19:48 +0200
committerLibravatar GitHub <noreply@github.com>2023-05-09 18:19:48 +0100
commit6392e00653d3b81062ef60d8ae2fa2621873533f (patch)
tree761d0ff445c2c6a85020cecdc58f92ae1cf78513 /vendor/github.com/uptrace/opentelemetry-go-extra/otelsql
parent[bugfix] Don't try to get user when serializing local instance account (#1757) (diff)
downloadgotosocial-6392e00653d3b81062ef60d8ae2fa2621873533f.tar.xz
feat: initial tracing support (#1623)
Diffstat (limited to 'vendor/github.com/uptrace/opentelemetry-go-extra/otelsql')
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/.golangci.yml5
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/LICENSE24
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/README.md118
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/driver.go460
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/otel.go254
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/stmt.go120
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/tx.go38
-rw-r--r--vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/version.go6
8 files changed, 1025 insertions, 0 deletions
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/.golangci.yml b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/.golangci.yml
new file mode 100644
index 000000000..65b3c9e6e
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/.golangci.yml
@@ -0,0 +1,5 @@
+issues:
+ exclude-rules:
+ - text: 'Drivers should implement'
+ linters:
+ - staticcheck
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/LICENSE b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/LICENSE
new file mode 100644
index 000000000..83bbb00f4
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2020 github.com/uptrace/opentelemetry-go-extra Contributors. 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.
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/README.md b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/README.md
new file mode 100644
index 000000000..dbded166d
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/README.md
@@ -0,0 +1,118 @@
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/uptrace/opentelemetry-go-extra/otelsql)](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql)
+
+# database/sql instrumentation for OpenTelemetry Go
+
+[database/sql OpenTelemetry instrumentation](https://uptrace.dev/opentelemetry/instrumentations/go-database-sql.html)
+records database queries (including `Tx` and `Stmt` queries) and reports `DBStats` metrics.
+
+## Installation
+
+```shell
+go get github.com/uptrace/opentelemetry-go-extra/otelsql
+```
+
+## Usage
+
+To instrument database/sql, you need to connect to a database using the API provided by otelsql:
+
+| sql | otelsql |
+| --------------------------- | ------------------------------- |
+| `sql.Open(driverName, dsn)` | `otelsql.Open(driverName, dsn)` |
+| `sql.OpenDB(connector)` | `otelsql.OpenDB(connector)` |
+
+```go
+import (
+ "github.com/uptrace/opentelemetry-go-extra/otelsql"
+ semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
+)
+
+db, err := otelsql.Open("sqlite", "file::memory:?cache=shared",
+ otelsql.WithAttributes(semconv.DBSystemSqlite),
+ otelsql.WithDBName("mydb"))
+if err != nil {
+ panic(err)
+}
+
+// db is *sql.DB
+```
+
+And then use context-aware API to propagate the active span via
+[context](https://uptrace.dev/opentelemetry/go-tracing.html#context):
+
+```go
+var num int
+if err := db.QueryRowContext(ctx, "SELECT 42").Scan(&num); err != nil {
+ panic(err)
+}
+```
+
+See [example](/example/) for details.
+
+## Options
+
+Both [otelsql.Open](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#Open) and
+[otelsql.OpenDB](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#OpenDB) accept
+the same [options](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#Option):
+
+- [WithAttributes](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#WithAttributes)
+ configures attributes that are used to create a span.
+- [WithDBName](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#WithDBName)
+ configures a `db.name` attribute.
+- [WithDBSystem](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra/otelsql#WithDBSystem)
+ configures a `db.system` attribute. When possible, you should prefer using WithAttributes and
+ [semconv](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.10.0), for example,
+ `otelsql.WithAttributes(semconv.DBSystemSqlite)`.
+
+## sqlboiler
+
+You can use otelsql to instrument [sqlboiler](https://github.com/volatiletech/sqlboiler) ORM:
+
+```go
+import (
+ "github.com/uptrace/opentelemetry-go-extra/otelsql"
+ semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
+)
+
+db, err := otelsql.Open("postgres", "dbname=fun user=abc",
+ otelsql.WithAttributes(semconv.DBSystemPostgreSQL))
+if err != nil {
+ return err
+}
+
+boil.SetDB(db)
+```
+
+## GORM 1
+
+You can use otelsql to instrument [GORM 1](https://v1.gorm.io/):
+
+```go
+import (
+ "github.com/jinzhu/gorm"
+ "github.com/uptrace/opentelemetry-go-extra/otelsql"
+ semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
+)
+
+// gormOpen is like gorm.Open, but it uses otelsql to instrument the database.
+func gormOpen(driverName, dataSourceName string, opts ...otelsql.Option) (*gorm.DB, error) {
+ db, err := otelsql.Open(driverName, dataSourceName, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return gorm.Open(driverName, db)
+}
+
+db, err := gormOpen("mysql", "user:password@/dbname",
+ otelsql.WithAttributes(semconv.DBSystemMySQL))
+if err != nil {
+ panic(err)
+}
+```
+
+To instrument GORM 2, use
+[otelgorm](https://github.com/uptrace/opentelemetry-go-extra/tree/main/otelgorm).
+
+## Alternatives
+
+- https://github.com/XSAM/otelsql - different driver registration and no metrics.
+- https://github.com/j2gg0s/otsql - like XSAM/otelsql but with Prometheus metrics.
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/driver.go b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/driver.go
new file mode 100644
index 000000000..056af3c6c
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/driver.go
@@ -0,0 +1,460 @@
+package otelsql
+
+import (
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+
+ "go.opentelemetry.io/otel/trace"
+)
+
+// Open is a wrapper over sql.Open that instruments the sql.DB to record executed queries
+// using OpenTelemetry API.
+func Open(driverName, dsn string, opts ...Option) (*sql.DB, error) {
+ db, err := sql.Open(driverName, dsn)
+ if err != nil {
+ return nil, err
+ }
+ return patchDB(db, dsn, opts...)
+}
+
+func patchDB(db *sql.DB, dsn string, opts ...Option) (*sql.DB, error) {
+ dbDriver := db.Driver()
+ d := newDriver(dbDriver, opts)
+
+ if _, ok := dbDriver.(driver.DriverContext); ok {
+ connector, err := d.OpenConnector(dsn)
+ if err != nil {
+ return nil, err
+ }
+ return sqlOpenDB(connector, d.instrum), nil
+ }
+
+ return sqlOpenDB(&dsnConnector{
+ driver: d,
+ dsn: dsn,
+ }, d.instrum), nil
+}
+
+// OpenDB is a wrapper over sql.OpenDB that instruments the sql.DB to record executed queries
+// using OpenTelemetry API.
+func OpenDB(connector driver.Connector, opts ...Option) *sql.DB {
+ instrum := newDBInstrum(opts)
+ c := newConnector(connector.Driver(), connector, instrum)
+ return sqlOpenDB(c, instrum)
+}
+
+func sqlOpenDB(connector driver.Connector, instrum *dbInstrum) *sql.DB {
+ db := sql.OpenDB(connector)
+ ReportDBStatsMetrics(db, WithMeterProvider(instrum.meterProvider), WithAttributes(instrum.attrs...))
+ return db
+}
+
+type dsnConnector struct {
+ driver *otelDriver
+ dsn string
+}
+
+func (c *dsnConnector) Connect(ctx context.Context) (driver.Conn, error) {
+ var conn driver.Conn
+ err := c.driver.instrum.withSpan(ctx, "db.Connect", "",
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ conn, err = c.driver.Open(c.dsn)
+ return err
+ })
+ return conn, err
+}
+
+func (c *dsnConnector) Driver() driver.Driver {
+ return c.driver
+}
+
+//------------------------------------------------------------------------------
+
+type otelDriver struct {
+ driver driver.Driver
+ driverCtx driver.DriverContext
+ instrum *dbInstrum
+}
+
+var _ driver.DriverContext = (*otelDriver)(nil)
+
+func newDriver(dr driver.Driver, opts []Option) *otelDriver {
+ driverCtx, _ := dr.(driver.DriverContext)
+ d := &otelDriver{
+ driver: dr,
+ driverCtx: driverCtx,
+ instrum: newDBInstrum(opts),
+ }
+ return d
+}
+
+func (d *otelDriver) Open(name string) (driver.Conn, error) {
+ conn, err := d.driver.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ return newConn(conn, d.instrum), nil
+}
+
+func (d *otelDriver) OpenConnector(dsn string) (driver.Connector, error) {
+ connector, err := d.driverCtx.OpenConnector(dsn)
+ if err != nil {
+ return nil, err
+ }
+ return newConnector(d, connector, d.instrum), nil
+}
+
+//------------------------------------------------------------------------------
+
+type connector struct {
+ driver.Connector
+ driver driver.Driver
+ instrum *dbInstrum
+}
+
+var _ driver.Connector = (*connector)(nil)
+
+func newConnector(d driver.Driver, c driver.Connector, instrum *dbInstrum) *connector {
+ return &connector{
+ driver: d,
+ Connector: c,
+ instrum: instrum,
+ }
+}
+
+func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
+ var conn driver.Conn
+ if err := c.instrum.withSpan(ctx, "db.Connect", "",
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ conn, err = c.Connector.Connect(ctx)
+ return err
+ }); err != nil {
+ return nil, err
+ }
+ return newConn(conn, c.instrum), nil
+}
+
+func (c *connector) Driver() driver.Driver {
+ return c.driver
+}
+
+//------------------------------------------------------------------------------
+
+type otelConn struct {
+ driver.Conn
+
+ instrum *dbInstrum
+
+ ping pingFunc
+ exec execFunc
+ execCtx execCtxFunc
+ query queryFunc
+ queryCtx queryCtxFunc
+ prepareCtx prepareCtxFunc
+ beginTx beginTxFunc
+ resetSession resetSessionFunc
+ checkNamedValue checkNamedValueFunc
+}
+
+var _ driver.Conn = (*otelConn)(nil)
+
+func newConn(conn driver.Conn, instrum *dbInstrum) *otelConn {
+ cn := &otelConn{
+ Conn: conn,
+ instrum: instrum,
+ }
+
+ cn.ping = cn.createPingFunc(conn)
+ cn.exec = cn.createExecFunc(conn)
+ cn.execCtx = cn.createExecCtxFunc(conn)
+ cn.query = cn.createQueryFunc(conn)
+ cn.queryCtx = cn.createQueryCtxFunc(conn)
+ cn.prepareCtx = cn.createPrepareCtxFunc(conn)
+ cn.beginTx = cn.createBeginTxFunc(conn)
+ cn.resetSession = cn.createResetSessionFunc(conn)
+ cn.checkNamedValue = cn.createCheckNamedValueFunc(conn)
+
+ return cn
+}
+
+var _ driver.Pinger = (*otelConn)(nil)
+
+func (c *otelConn) Ping(ctx context.Context) error {
+ return c.ping(ctx)
+}
+
+type pingFunc func(ctx context.Context) error
+
+func (c *otelConn) createPingFunc(conn driver.Conn) pingFunc {
+ if pinger, ok := conn.(driver.Pinger); ok {
+ return func(ctx context.Context) error {
+ return c.instrum.withSpan(ctx, "db.Ping", "",
+ func(ctx context.Context, span trace.Span) error {
+ return pinger.Ping(ctx)
+ })
+ }
+ }
+ return func(ctx context.Context) error {
+ return driver.ErrSkip
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.Execer = (*otelConn)(nil)
+
+func (c *otelConn) Exec(query string, args []driver.Value) (driver.Result, error) {
+ return c.exec(query, args)
+}
+
+type execFunc func(query string, args []driver.Value) (driver.Result, error)
+
+func (c *otelConn) createExecFunc(conn driver.Conn) execFunc {
+ if execer, ok := conn.(driver.Execer); ok {
+ return func(query string, args []driver.Value) (driver.Result, error) {
+ return execer.Exec(query, args)
+ }
+ }
+ return func(query string, args []driver.Value) (driver.Result, error) {
+ return nil, driver.ErrSkip
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.ExecerContext = (*otelConn)(nil)
+
+func (c *otelConn) ExecContext(
+ ctx context.Context, query string, args []driver.NamedValue,
+) (driver.Result, error) {
+ return c.execCtx(ctx, query, args)
+}
+
+type execCtxFunc func(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error)
+
+func (c *otelConn) createExecCtxFunc(conn driver.Conn) execCtxFunc {
+ var fn execCtxFunc
+
+ if execer, ok := conn.(driver.ExecerContext); ok {
+ fn = execer.ExecContext
+ } else {
+ fn = func(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
+ vArgs, err := namedValueToValue(args)
+ if err != nil {
+ return nil, err
+ }
+ return c.exec(query, vArgs)
+ }
+ }
+
+ return func(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
+ var res driver.Result
+ if err := c.instrum.withSpan(ctx, "db.Exec", query,
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ res, err = fn(ctx, query, args)
+ if err != nil {
+ return err
+ }
+
+ if span.IsRecording() {
+ rows, err := res.RowsAffected()
+ if err == nil {
+ span.SetAttributes(dbRowsAffected.Int64(rows))
+ }
+ }
+
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+ return res, nil
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.Queryer = (*otelConn)(nil)
+
+func (c *otelConn) Query(query string, args []driver.Value) (driver.Rows, error) {
+ return c.query(query, args)
+}
+
+type queryFunc func(query string, args []driver.Value) (driver.Rows, error)
+
+func (c *otelConn) createQueryFunc(conn driver.Conn) queryFunc {
+ if queryer, ok := c.Conn.(driver.Queryer); ok {
+ return func(query string, args []driver.Value) (driver.Rows, error) {
+ return queryer.Query(query, args)
+ }
+ }
+ return func(query string, args []driver.Value) (driver.Rows, error) {
+ return nil, driver.ErrSkip
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.QueryerContext = (*otelConn)(nil)
+
+func (c *otelConn) QueryContext(
+ ctx context.Context, query string, args []driver.NamedValue,
+) (driver.Rows, error) {
+ return c.queryCtx(ctx, query, args)
+}
+
+type queryCtxFunc func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error)
+
+func (c *otelConn) createQueryCtxFunc(conn driver.Conn) queryCtxFunc {
+ var fn queryCtxFunc
+
+ if queryer, ok := c.Conn.(driver.QueryerContext); ok {
+ fn = queryer.QueryContext
+ } else {
+ fn = func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
+ vArgs, err := namedValueToValue(args)
+ if err != nil {
+ return nil, err
+ }
+ return c.query(query, vArgs)
+ }
+ }
+
+ return func(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
+ var rows driver.Rows
+ err := c.instrum.withSpan(ctx, "db.Query", query,
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ rows, err = fn(ctx, query, args)
+ return err
+ })
+ return rows, err
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.ConnPrepareContext = (*otelConn)(nil)
+
+func (c *otelConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
+ return c.prepareCtx(ctx, query)
+}
+
+type prepareCtxFunc func(ctx context.Context, query string) (driver.Stmt, error)
+
+func (c *otelConn) createPrepareCtxFunc(conn driver.Conn) prepareCtxFunc {
+ var fn prepareCtxFunc
+
+ if preparer, ok := c.Conn.(driver.ConnPrepareContext); ok {
+ fn = preparer.PrepareContext
+ } else {
+ fn = func(ctx context.Context, query string) (driver.Stmt, error) {
+ return c.Conn.Prepare(query)
+ }
+ }
+
+ return func(ctx context.Context, query string) (driver.Stmt, error) {
+ var stmt driver.Stmt
+ if err := c.instrum.withSpan(ctx, "db.Prepare", query,
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ stmt, err = fn(ctx, query)
+ return err
+ }); err != nil {
+ return nil, err
+ }
+ return newStmt(stmt, query, c.instrum), nil
+ }
+}
+
+var _ driver.ConnBeginTx = (*otelConn)(nil)
+
+func (c *otelConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
+ return c.beginTx(ctx, opts)
+}
+
+type beginTxFunc func(ctx context.Context, opts driver.TxOptions) (driver.Tx, error)
+
+func (c *otelConn) createBeginTxFunc(conn driver.Conn) beginTxFunc {
+ var fn beginTxFunc
+
+ if txor, ok := conn.(driver.ConnBeginTx); ok {
+ fn = txor.BeginTx
+ } else {
+ fn = func(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
+ return conn.Begin()
+ }
+ }
+
+ return func(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
+ var tx driver.Tx
+ if err := c.instrum.withSpan(ctx, "db.Begin", "",
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ tx, err = fn(ctx, opts)
+ return err
+ }); err != nil {
+ return nil, err
+ }
+ return newTx(ctx, tx, c.instrum), nil
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.SessionResetter = (*otelConn)(nil)
+
+func (c *otelConn) ResetSession(ctx context.Context) error {
+ return c.resetSession(ctx)
+}
+
+type resetSessionFunc func(ctx context.Context) error
+
+func (c *otelConn) createResetSessionFunc(conn driver.Conn) resetSessionFunc {
+ if resetter, ok := c.Conn.(driver.SessionResetter); ok {
+ return func(ctx context.Context) error {
+ return resetter.ResetSession(ctx)
+ }
+ }
+ return func(ctx context.Context) error {
+ return driver.ErrSkip
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.NamedValueChecker = (*otelConn)(nil)
+
+func (c *otelConn) CheckNamedValue(value *driver.NamedValue) error {
+ return c.checkNamedValue(value)
+}
+
+type checkNamedValueFunc func(*driver.NamedValue) error
+
+func (c *otelConn) createCheckNamedValueFunc(conn driver.Conn) checkNamedValueFunc {
+ if checker, ok := c.Conn.(driver.NamedValueChecker); ok {
+ return func(value *driver.NamedValue) error {
+ return checker.CheckNamedValue(value)
+ }
+ }
+ return func(value *driver.NamedValue) error {
+ return driver.ErrSkip
+ }
+}
+
+//------------------------------------------------------------------------------
+
+func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
+ args := make([]driver.Value, len(named))
+ for n, param := range named {
+ if len(param.Name) > 0 {
+ return nil, errors.New("otelsql: driver does not support named parameters")
+ }
+ args[n] = param.Value
+ }
+ return args, nil
+}
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/otel.go b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/otel.go
new file mode 100644
index 000000000..0932e2759
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/otel.go
@@ -0,0 +1,254 @@
+package otelsql
+
+import (
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "io"
+ "time"
+
+ "go.opentelemetry.io/otel"
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/codes"
+ "go.opentelemetry.io/otel/metric"
+ "go.opentelemetry.io/otel/metric/global"
+ "go.opentelemetry.io/otel/metric/instrument"
+ semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
+ "go.opentelemetry.io/otel/trace"
+)
+
+const instrumName = "github.com/uptrace/opentelemetry-go-extra/otelsql"
+
+var dbRowsAffected = attribute.Key("db.rows_affected")
+
+type config struct {
+ tracerProvider trace.TracerProvider
+ tracer trace.Tracer //nolint:structcheck
+
+ meterProvider metric.MeterProvider
+ meter metric.Meter
+
+ attrs []attribute.KeyValue
+
+ queryFormatter func(query string) string
+}
+
+func newConfig(opts []Option) *config {
+ c := &config{
+ tracerProvider: otel.GetTracerProvider(),
+ meterProvider: global.MeterProvider(),
+ }
+ for _, opt := range opts {
+ opt(c)
+ }
+ return c
+}
+
+func (c *config) formatQuery(query string) string {
+ if c.queryFormatter != nil {
+ return c.queryFormatter(query)
+ }
+ return query
+}
+
+type dbInstrum struct {
+ *config
+
+ queryHistogram instrument.Int64Histogram
+}
+
+func newDBInstrum(opts []Option) *dbInstrum {
+ t := &dbInstrum{
+ config: newConfig(opts),
+ }
+
+ if t.tracer == nil {
+ t.tracer = t.tracerProvider.Tracer(instrumName)
+ }
+ if t.meter == nil {
+ t.meter = t.meterProvider.Meter(instrumName)
+ }
+
+ var err error
+ t.queryHistogram, err = t.meter.Int64Histogram(
+ "go.sql.query_timing",
+ instrument.WithDescription("Timing of processed queries"),
+ instrument.WithUnit("milliseconds"),
+ )
+ if err != nil {
+ panic(err)
+ }
+
+ return t
+}
+
+func (t *dbInstrum) withSpan(
+ ctx context.Context,
+ spanName string,
+ query string,
+ fn func(ctx context.Context, span trace.Span) error,
+) error {
+ var startTime time.Time
+ if query != "" {
+ startTime = time.Now()
+ }
+
+ attrs := make([]attribute.KeyValue, 0, len(t.attrs)+1)
+ attrs = append(attrs, t.attrs...)
+ if query != "" {
+ attrs = append(attrs, semconv.DBStatementKey.String(t.formatQuery(query)))
+ }
+
+ ctx, span := t.tracer.Start(ctx, spanName,
+ trace.WithSpanKind(trace.SpanKindClient),
+ trace.WithAttributes(attrs...))
+ err := fn(ctx, span)
+ span.End()
+
+ if query != "" {
+ t.queryHistogram.Record(ctx, time.Since(startTime).Milliseconds(), t.attrs...)
+ }
+
+ if !span.IsRecording() {
+ return err
+ }
+
+ switch err {
+ case nil,
+ driver.ErrSkip,
+ io.EOF, // end of rows iterator
+ sql.ErrNoRows:
+ // ignore
+ default:
+ span.RecordError(err)
+ span.SetStatus(codes.Error, err.Error())
+ }
+
+ return err
+}
+
+type Option func(c *config)
+
+// WithTracerProvider configures a tracer provider that is used to create a tracer.
+func WithTracerProvider(tracerProvider trace.TracerProvider) Option {
+ return func(c *config) {
+ c.tracerProvider = tracerProvider
+ }
+}
+
+// WithAttributes configures attributes that are used to create a span.
+func WithAttributes(attrs ...attribute.KeyValue) Option {
+ return func(c *config) {
+ c.attrs = append(c.attrs, attrs...)
+ }
+}
+
+// WithDBSystem configures a db.system attribute. You should prefer using
+// WithAttributes and semconv, for example, `otelsql.WithAttributes(semconv.DBSystemSqlite)`.
+func WithDBSystem(system string) Option {
+ return func(c *config) {
+ c.attrs = append(c.attrs, semconv.DBSystemKey.String(system))
+ }
+}
+
+// WithDBName configures a db.name attribute.
+func WithDBName(name string) Option {
+ return func(c *config) {
+ c.attrs = append(c.attrs, semconv.DBNameKey.String(name))
+ }
+}
+
+// WithMeterProvider configures a metric.Meter used to create instruments.
+func WithMeterProvider(meterProvider metric.MeterProvider) Option {
+ return func(c *config) {
+ c.meterProvider = meterProvider
+ }
+}
+
+// WithQueryFormatter configures a query formatter
+func WithQueryFormatter(queryFormatter func(query string) string) Option {
+ return func(c *config) {
+ c.queryFormatter = queryFormatter
+ }
+}
+
+// ReportDBStatsMetrics reports DBStats metrics using OpenTelemetry Metrics API.
+func ReportDBStatsMetrics(db *sql.DB, opts ...Option) {
+ cfg := newConfig(opts)
+
+ if cfg.meter == nil {
+ cfg.meter = cfg.meterProvider.Meter(instrumName)
+ }
+
+ meter := cfg.meter
+ labels := cfg.attrs
+
+ maxOpenConns, _ := meter.Int64ObservableGauge(
+ "go.sql.connections_max_open",
+ instrument.WithDescription("Maximum number of open connections to the database"),
+ )
+ openConns, _ := meter.Int64ObservableGauge(
+ "go.sql.connections_open",
+ instrument.WithDescription("The number of established connections both in use and idle"),
+ )
+ inUseConns, _ := meter.Int64ObservableGauge(
+ "go.sql.connections_in_use",
+ instrument.WithDescription("The number of connections currently in use"),
+ )
+ idleConns, _ := meter.Int64ObservableGauge(
+ "go.sql.connections_idle",
+ instrument.WithDescription("The number of idle connections"),
+ )
+ connsWaitCount, _ := meter.Int64ObservableCounter(
+ "go.sql.connections_wait_count",
+ instrument.WithDescription("The total number of connections waited for"),
+ )
+ connsWaitDuration, _ := meter.Int64ObservableCounter(
+ "go.sql.connections_wait_duration",
+ instrument.WithDescription("The total time blocked waiting for a new connection"),
+ instrument.WithUnit("nanoseconds"),
+ )
+ connsClosedMaxIdle, _ := meter.Int64ObservableCounter(
+ "go.sql.connections_closed_max_idle",
+ instrument.WithDescription("The total number of connections closed due to SetMaxIdleConns"),
+ )
+ connsClosedMaxIdleTime, _ := meter.Int64ObservableCounter(
+ "go.sql.connections_closed_max_idle_time",
+ instrument.WithDescription("The total number of connections closed due to SetConnMaxIdleTime"),
+ )
+ connsClosedMaxLifetime, _ := meter.Int64ObservableCounter(
+ "go.sql.connections_closed_max_lifetime",
+ instrument.WithDescription("The total number of connections closed due to SetConnMaxLifetime"),
+ )
+
+ if _, err := meter.RegisterCallback(
+ func(ctx context.Context, o metric.Observer) error {
+ stats := db.Stats()
+
+ o.ObserveInt64(maxOpenConns, int64(stats.MaxOpenConnections), labels...)
+
+ o.ObserveInt64(openConns, int64(stats.OpenConnections), labels...)
+ o.ObserveInt64(inUseConns, int64(stats.InUse), labels...)
+ o.ObserveInt64(idleConns, int64(stats.Idle), labels...)
+
+ o.ObserveInt64(connsWaitCount, stats.WaitCount, labels...)
+ o.ObserveInt64(connsWaitDuration, int64(stats.WaitDuration), labels...)
+ o.ObserveInt64(connsClosedMaxIdle, stats.MaxIdleClosed, labels...)
+ o.ObserveInt64(connsClosedMaxIdleTime, stats.MaxIdleTimeClosed, labels...)
+ o.ObserveInt64(connsClosedMaxLifetime, stats.MaxLifetimeClosed, labels...)
+
+ return nil
+ },
+ maxOpenConns,
+ openConns,
+ inUseConns,
+ idleConns,
+ connsWaitCount,
+ connsWaitDuration,
+ connsClosedMaxIdle,
+ connsClosedMaxIdleTime,
+ connsClosedMaxLifetime,
+ ); err != nil {
+ panic(err)
+ }
+}
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/stmt.go b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/stmt.go
new file mode 100644
index 000000000..e87a1e73f
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/stmt.go
@@ -0,0 +1,120 @@
+package otelsql
+
+import (
+ "context"
+ "database/sql/driver"
+
+ "go.opentelemetry.io/otel/trace"
+)
+
+type otelStmt struct {
+ driver.Stmt
+
+ query string
+ instrum *dbInstrum
+
+ execCtx stmtExecCtxFunc
+ queryCtx stmtQueryCtxFunc
+}
+
+var _ driver.Stmt = (*otelStmt)(nil)
+
+func newStmt(stmt driver.Stmt, query string, instrum *dbInstrum) *otelStmt {
+ s := &otelStmt{
+ Stmt: stmt,
+ query: query,
+ instrum: instrum,
+ }
+ s.execCtx = s.createExecCtxFunc(stmt)
+ s.queryCtx = s.createQueryCtxFunc(stmt)
+ return s
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.StmtExecContext = (*otelStmt)(nil)
+
+func (stmt *otelStmt) ExecContext(
+ ctx context.Context, args []driver.NamedValue,
+) (driver.Result, error) {
+ return stmt.execCtx(ctx, args)
+}
+
+type stmtExecCtxFunc func(ctx context.Context, args []driver.NamedValue) (driver.Result, error)
+
+func (s *otelStmt) createExecCtxFunc(stmt driver.Stmt) stmtExecCtxFunc {
+ var fn stmtExecCtxFunc
+
+ if execer, ok := s.Stmt.(driver.StmtExecContext); ok {
+ fn = execer.ExecContext
+ } else {
+ fn = func(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
+ vArgs, err := namedValueToValue(args)
+ if err != nil {
+ return nil, err
+ }
+ return stmt.Exec(vArgs)
+ }
+ }
+
+ return func(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
+ var res driver.Result
+ err := s.instrum.withSpan(ctx, "stmt.Exec", s.query,
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ res, err = fn(ctx, args)
+ if err != nil {
+ return err
+ }
+
+ if span.IsRecording() {
+ rows, err := res.RowsAffected()
+ if err == nil {
+ span.SetAttributes(dbRowsAffected.Int64(rows))
+ }
+ }
+
+ return nil
+ })
+ return res, err
+ }
+}
+
+//------------------------------------------------------------------------------
+
+var _ driver.StmtQueryContext = (*otelStmt)(nil)
+
+func (stmt *otelStmt) QueryContext(
+ ctx context.Context, args []driver.NamedValue,
+) (driver.Rows, error) {
+ return stmt.queryCtx(ctx, args)
+}
+
+type stmtQueryCtxFunc func(ctx context.Context, args []driver.NamedValue) (driver.Rows, error)
+
+func (s *otelStmt) createQueryCtxFunc(stmt driver.Stmt) stmtQueryCtxFunc {
+ var fn stmtQueryCtxFunc
+
+ if queryer, ok := s.Stmt.(driver.StmtQueryContext); ok {
+ fn = queryer.QueryContext
+ } else {
+ fn = func(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
+ vArgs, err := namedValueToValue(args)
+ if err != nil {
+ return nil, err
+ }
+ return s.Query(vArgs)
+ }
+ }
+
+ return func(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
+ var rows driver.Rows
+ err := s.instrum.withSpan(ctx, "stmt.Query", s.query,
+ func(ctx context.Context, span trace.Span) error {
+ var err error
+ rows, err = fn(ctx, args)
+ return err
+ })
+ return rows, err
+ }
+}
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/tx.go b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/tx.go
new file mode 100644
index 000000000..c4bd55e13
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/tx.go
@@ -0,0 +1,38 @@
+package otelsql
+
+import (
+ "context"
+ "database/sql/driver"
+
+ "go.opentelemetry.io/otel/trace"
+)
+
+type otelTx struct {
+ ctx context.Context
+ tx driver.Tx
+ instrum *dbInstrum
+}
+
+var _ driver.Tx = (*otelTx)(nil)
+
+func newTx(ctx context.Context, tx driver.Tx, instrum *dbInstrum) *otelTx {
+ return &otelTx{
+ ctx: ctx,
+ tx: tx,
+ instrum: instrum,
+ }
+}
+
+func (tx *otelTx) Commit() error {
+ return tx.instrum.withSpan(tx.ctx, "tx.Commit", "",
+ func(ctx context.Context, span trace.Span) error {
+ return tx.tx.Commit()
+ })
+}
+
+func (tx *otelTx) Rollback() error {
+ return tx.instrum.withSpan(tx.ctx, "tx.Rollback", "",
+ func(ctx context.Context, span trace.Span) error {
+ return tx.tx.Rollback()
+ })
+}
diff --git a/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/version.go b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/version.go
new file mode 100644
index 000000000..97134301d
--- /dev/null
+++ b/vendor/github.com/uptrace/opentelemetry-go-extra/otelsql/version.go
@@ -0,0 +1,6 @@
+package otelsql
+
+// Version is the current release version.
+func Version() string {
+ return "0.1.21"
+}