otlpsql

package module
v1.2.115 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2024 License: MIT Imports: 17 Imported by: 1

README

otlpsql

OpenTelemetry SQL database driver wrapper.

Add an otlpsql wrapper to your existing database code to instrument the interactions with the database.

installation

go get -u github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql

initialize

To use otlpsql with your application, register an otlpsql wrapper of a database driver as shown below.

Example1:

    import (
        "database/sql"
        _ "github.com/mattn/go-sqlite3"
        "github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql"
    )
    // Register our sqlite3-otlp wrapper for the provided SQLite3 driver.
	// "sqlite3-otlp" must not be registered, set in func init(){} as recommended.
	sql.Register("sqlite3-otlp", otlpsql.Wrap(&sqlite3.SQLiteDriver{}, otlpsql.WithAllWrapperOptions()))

	// Connect to a SQLite3 database using the otlpsql driver wrapper.
	db, err := sql.Open("sqlite3-otlp", "resource.db")
	if err != nil {
		log.Fatal(err)
	}

Example2:

import (
    _ "github.com/mattn/go-sqlite3"
    "github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql"
)

var (
    driverName string
    err        error
    db         *sql.DB
)

// Register our otlpsql wrapper for the provided SQLite3 driver.
driverName, err = otlpsql.Register("sqlite3", otlpsql.WithAllTraceOptions(), otlpsql.WithInstanceName("resources"))
if err != nil {
    log.Fatalf("unable to register our otlpsql driver: %v\n", err)
}

// Connect to a SQLite3 database using the otlpsql driver wrapper.
db, err = sql.Open(driverName, "resource.db")

A more explicit and alternative way to bootstrap the otlpsql wrapper exists as shown below. This will only work if the actual database driver has its driver implementation exported.

Example:

import (
    sqlite3 "github.com/mattn/go-sqlite3"
    "github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql"
)

var (
    driver driver.Driver
    err    error
    db     *sql.DB
)

// Explicitly wrap the SQLite3 driver with otlpsql.
driver = otlpsql.Wrap(&sqlite3.SQLiteDriver{})

// Register our otlpsql wrapper as a database driver.
sql.Register("otlpsql-sqlite3", driver)

// Connect to a SQLite3 database using the otlpsql driver wrapper.
db, err = sql.Open("otlpsql-sqlite3", "resource.db")

Projects providing their own abstractions on top of database/sql/driver can also wrap an existing driver.Conn interface directly with otlpsql.

Example:

import "github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql"

func GetConn(...) driver.Conn {
    // Create custom driver.Conn.
    conn := initializeConn(...)

    // Wrap with otlpsql.
    return otlpsql.WrapConn(conn, otlpsql.WithAllTraceOptions())    
}

Finally database drivers that support the new (Go 1.10+) driver.Connector interface can be wrapped directly by otlpsql without the need for otlpsql to register a driver.Driver.

Example:

import(
    "github.com/searKing/opentelemetry-go-contrib/instrumentation/database/otlpsql"
    "github.com/lib/pq"
)

var (
    connector driver.Connector
    err       error
    db        *sql.DB
)

// Get a database driver.Connector for a fixed configuration.
connector, err = pq.NewConnector("postgres://user:passt@host:5432/db")
if err != nil {
    log.Fatalf("unable to create our postgres connector: %v\n", err)
}

// Wrap the driver.Connector with otlpsql.
connector = otlpsql.WrapConnector(connector, otlpsql.WithAllWrapperOptions())

// Use the wrapped driver.Connector.
db = sql.OpenDB(connector)

metrics

Next to tracing, otlpsql also supports Opentelemetrys stats automatically.

From Go 1.11 and up, otlpsql also has the ability to record database connection pool details. Use the RecordStats function and provide a *sql.DB to record details on, as well as the required record interval.

// Connect to a SQLite3 database using the otlpsql driver wrapper.
db, err = sql.Open("otlpsql-sqlite3", "resource.db")

// Record DB stats every 5 seconds until we exit.
defer otlpsql.RecordStats(db, 5 * time.Second)()

Recorded metrics

Metric Search suffix Additional tags
Latency in milliseconds "go_sql_client_latency_milliseconds" "method", "error", "status"

If using RecordStats:

Metric Search suffix
Number of open connections "go_sql_connections_open"
Number of idle connections "go_sql_connections_idle"
Number of active connections "go_sql_connections_active"
Total number of connections waited for "go_sql_connections_wait_count"
Total time blocked waiting for new connections "go_sql_connections_wait_duration_milliseconds"
Total number of closed connections by SetMaxIdleConns "go_sql_connections_idle_closed"
Total number of closed connections by SetConnMaxLifetime "go_sql_connections_lifetime_closed"

jmoiron/sqlx

If using the sqlx library with named queries you will need to use the sqlx.NewDb function to wrap an existing *sql.DB connection. Do not use the sqlx.Open and sqlx.Connect methods. sqlx uses the driver name to figure out which database is being used. It uses this knowledge to convert named queries to the correct bind type (dollar sign, question mark) if named queries are not supported natively by the database. Since otlpsql creates a new driver name it will not be recognized by sqlx and named queries will fail.

Use one of the above methods to first create a *sql.DB connection and then create a *sqlx.DB connection by wrapping the *sql.DB like this:

    // Register our otlpsql wrapper for the provided Postgres driver.
    driverName, err := otlpsql.Register("postgres", otlpsql.WithAllTraceOptions())
    if err != nil { ... }

    // Connect to a Postgres database using the otlpsql driver wrapper.
    db, err := sql.Open(driverName, "postgres://localhost:5432/my_database")
    if err != nil { ... }

    // Wrap our *sql.DB with sqlx. use the original db driver name!!!
    dbx := sqlx.NewDB(db, "postgres")

context

To really take advantage of otlpsql, all database calls should be made using the *Context methods. Failing to do so will result in many orphaned otlpsql traces if the AllowRoot TraceOption is set to true. By default AllowRoot is disabled and will result in otlpsql not tracing the database calls if context or parent spans are missing.

Old New
*DB.Begin *DB.BeginTx
*DB.Exec *DB.ExecContext
*DB.Ping *DB.PingContext
*DB.Prepare *DB.PrepareContext
*DB.Query *DB.QueryContext
*DB.QueryRow *DB.QueryRowContext
*Stmt.Exec *Stmt.ExecContext
*Stmt.Query *Stmt.QueryContext
*Stmt.QueryRow *Stmt.QueryRowContext
*Tx.Exec *Tx.ExecContext
*Tx.Prepare *Tx.PrepareContext
*Tx.Query *Tx.QueryContext
*Tx.QueryRow *Tx.QueryRowContext

Example:

func (s *svc) GetDevice(ctx context.Context, id int) (*Device, error) {
    // Assume we have instrumented our service transports and ctx holds a span.
    var device Device
    if err := s.db.QueryRowContext(
        ctx, "SELECT * FROM device WHERE id = ?", id,
        ).Scan(&device); err != nil {
        return nil, err
    }
    return device
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// GoSQLInstance is the SQL instance name.
	GoSQLInstance = attribute.Key("go_sql_instance")
	// GoSQLMethod is the SQL method called.
	GoSQLMethod = attribute.Key("go_sql_method")
	// GoSQLError is the error received while calling a SQL method.
	GoSQLError = attribute.Key("go_sql_error")
	// GoSQLStatus identifies success vs. error from the SQL method response.
	GoSQLStatus = attribute.Key("go_sql_status")
)

The following tags are applied to stats recorded by this package.

View Source
var (
	// InstrumentationName is the name of this instrumentation package.
	InstrumentationName = "go.sql"
	// InstrumentationVersion is the version of this instrumentation package.
	InstrumentationVersion = otelcontrib.Version()
)
View Source
var (
	MeasureLatencyMs, _ = Meter().Int64Histogram("go_sql_client_latency_milliseconds",
		metric.WithDescription("The latency of calls in milliseconds."),
		metric.WithUnit(uMilliseconds))
	MeasureOpenConnections, _ = Meter().Int64Histogram("go_sql_connections_open",
		metric.WithDescription("Count of open connections in the pool."),
		metric.WithUnit(uDimensionless))
	MeasureIdleConnections, _ = Meter().Int64Histogram("go_sql_connections_idle",
		metric.WithDescription("Count of idle connections in the pool."),
		metric.WithUnit(uDimensionless))
	MeasureActiveConnections, _ = Meter().Int64Histogram("go_sql_connections_active",
		metric.WithDescription("Count of active connections in the pool."),
		metric.WithUnit(uDimensionless))
	MeasureWaitCount, _ = Meter().Int64Histogram("go_sql_connections_wait_count",
		metric.WithDescription("The total number of connections waited for."),
		metric.WithUnit(uDimensionless))
	MeasureWaitDuration, _ = Meter().Int64Histogram("go_sql_connections_wait_duration_milliseconds",
		metric.WithDescription("The total time blocked waiting for a new connection."),
		metric.WithUnit(uMilliseconds))
	MeasureIdleClosed, _ = Meter().Int64Histogram("go_sql_connections_idle_closed",
		metric.WithDescription("The total number of connections closed due to SetMaxIdleConns."),
		metric.WithUnit(uDimensionless))
	MeasureLifetimeClosed, _ = Meter().Int64Histogram("go_sql_connections_lifetime_closed",
		metric.WithDescription("The total number of connections closed due to SetConnMaxLifetime."),
		metric.WithUnit(uDimensionless))
)

The following measures are supported for use in custom views.

View Source
var AllWrapperOptions = wrapper{
	AllowRoot:         true,
	Ping:              true,
	RowsNext:          true,
	RowsClose:         true,
	RowsAffected:      true,
	LastInsertID:      true,
	Query:             true,
	QueryParams:       true,
	DefaultAttributes: []attribute.KeyValue{attribute.String("db.type", "sql")},
}

AllWrapperOptions has all tracing options enabled.

View Source
var PostCall func(ctx context.Context, err error, elapse time.Duration, attrs ...attribute.KeyValue)

PostCall called after sql executed, designed such for logger to print details

Functions

func Meter

func Meter() metric.Meter

func RecordStats

func RecordStats(db *sql.DB, interval time.Duration) (fnStop func())

RecordStats records database statistics for provided sql.DB at the provided interval.

func Register

func Register(driverName string, options ...WrapperOption) (string, error)

Register initializes and registers our otelsql wrapped database driver identified by its driverName and using provided TraceOptions. On success it returns the generated driverName to use when calling sql.Open. It is possible to register multiple wrappers for the same database driver if needing different TraceOptions for different connections.

func RegisterWithSource

func RegisterWithSource(driverName string, source string, options ...WrapperOption) (string, error)

RegisterWithSource initializes and registers our otelsql wrapped database driver identified by its driverName, using provided TraceOptions. source is useful if some drivers do not accept the empty string when opening the DB. On success it returns the generated driverName to use when calling sql.Open. It is possible to register multiple wrappers for the same database driver if needing different TraceOptions for different connections.

func Wrap

func Wrap(d driver.Driver, options ...WrapperOption) driver.Driver

Wrap takes an SQL driver and wraps it with OpenCensus instrumentation.

func WrapConn

func WrapConn(c driver.Conn, options ...WrapperOption) driver.Conn

WrapConn allows an existing driver.Conn to be wrapped by sql.

func WrapConnector

func WrapConnector(dc driver.Connector, options ...WrapperOption) driver.Connector

WrapConnector allows wrapping a database driver.Connector which eliminates the need to register otlpsql as an available driver.Driver.

Types

type EmptyWrapperOption

type EmptyWrapperOption struct{}

EmptyWrapperOption does not alter the configuration. It can be embedded in another structure to build custom options.

This API is EXPERIMENTAL.

type NamedValueChecker added in v1.2.89

type NamedValueChecker = driver.NamedValueChecker

type RowsColumnTypeScanType added in v1.2.89

type RowsColumnTypeScanType = driver.RowsColumnTypeScanType

type WrapperOption

type WrapperOption interface {
	// contains filtered or unexported methods
}

A WrapperOption sets options.

func WithAllWrapperOptions

func WithAllWrapperOptions() WrapperOption

WithAllWrapperOptions enables all available trace options.

func WithOptions

func WithOptions(options wrapper) WrapperOption

WithOptions sets our otlpsql tracing middleware options through a single WrapperOptions object.

func WithWrapperAllowRoot added in v1.2.89

func WithWrapperAllowRoot(v bool) WrapperOption

WithWrapperAllowRoot sets AllowRoot in wrapper. AllowRoot, if set to true, will allow otlpsql to create root spans in absence of existing spans or even context. Default is to not trace otlpsql calls if no existing parent span is found in context or when using methods not taking context.

func WithWrapperDefaultAttributes added in v1.2.89

func WithWrapperDefaultAttributes(v []attribute.KeyValue) WrapperOption

WithWrapperDefaultAttributes sets DefaultAttributes in wrapper. DefaultAttributes will be set to each span as default.

func WithWrapperDisableErrSkip added in v1.2.89

func WithWrapperDisableErrSkip(v bool) WrapperOption

WithWrapperDisableErrSkip sets DisableErrSkip in wrapper. DisableErrSkip, if set to true, will suppress driver.ErrSkip errors in spans.

func WithWrapperInstanceName added in v1.2.89

func WithWrapperInstanceName(v string) WrapperOption

WithWrapperInstanceName sets InstanceName in wrapper. InstanceName identifies database.

func WithWrapperLastInsertID added in v1.2.89

func WithWrapperLastInsertID(v bool) WrapperOption

WithWrapperLastInsertID sets LastInsertID in wrapper. LastInsertID, if set to true, will enable the creation of spans on LastInsertId calls.

func WithWrapperPing added in v1.2.89

func WithWrapperPing(v bool) WrapperOption

WithWrapperPing sets Ping in wrapper. Ping, if set to true, will enable the creation of spans on Ping requests.

func WithWrapperQuery added in v1.2.89

func WithWrapperQuery(v bool) WrapperOption

WithWrapperQuery sets Query in wrapper. Query, if set to true, will enable recording of sql queries in spans. Only allow this if it is safe to have queries recorded with respect to security.

func WithWrapperQueryParams added in v1.2.89

func WithWrapperQueryParams(v bool) WrapperOption

WithWrapperQueryParams sets QueryParams in wrapper. QueryParams, if set to true, will enable recording of parameters used with parametrized queries. Only allow this if it is safe to have parameters recorded with respect to security. This setting is a noop if the Query option is set to false.

func WithWrapperRowsAffected added in v1.2.89

func WithWrapperRowsAffected(v bool) WrapperOption

WithWrapperRowsAffected sets RowsAffected in wrapper. RowsAffected, if set to true, will enable the creation of spans on RowsAffected calls.

func WithWrapperRowsClose added in v1.2.89

func WithWrapperRowsClose(v bool) WrapperOption

WithWrapperRowsClose sets RowsClose in wrapper. RowsClose, if set to true, will enable the creation of spans on RowsClose calls.

func WithWrapperRowsNext added in v1.2.89

func WithWrapperRowsNext(v bool) WrapperOption

WithWrapperRowsNext sets RowsNext in wrapper. RowsNext, if set to true, will enable the creation of spans on RowsNext calls. This can result in many spans.

type WrapperOptionFunc

type WrapperOptionFunc func(*wrapper)

WrapperOptionFunc wraps a function that modifies wrapper into an implementation of the WrapperOption interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL