Skip to content

Commit

Permalink
Instrument metrics (#74)
Browse files Browse the repository at this point in the history
* Add metrics support

* Add metrics in example

* Change the value type of connectionWaitDurationTotal to float64

* Add metric instruments in README

* Update CHANGELOG

* Add more tests
  • Loading branch information
XSAM authored Mar 27, 2022
1 parent 0b4b4fe commit a2304f4
Show file tree
Hide file tree
Showing 28 changed files with 1,037 additions and 67 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- Metrics support. (#74)

### Changed

- Upgrade OTel to `v1.6.0/v0.28.0`. (#74)

## [0.12.0] - 2022-03-18

### Added
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

It is an OpenTelemetry instrumentation for Golang `database/sql`, a port from https://github.com/open-telemetry/opentelemetry-go-contrib/pull/505.

It can only instrument traces for the present.
It instruments traces and metrics.

## Install

Expand All @@ -30,6 +30,20 @@ $ go get github.com/XSAM/otelsql
| RowsAffected, LastInsertID | If set to true, will enable the creation of spans on RowsAffected/LastInsertId calls. | Dropped | Don't know its use cases. We might add this later based on the users' feedback. |
| QueryParams | If set to true, will enable recording of parameters used with parametrized queries. | Dropped | It will cause high cardinality values and security problems. |

## Metric Instruments

| Name | Description | Units | Instrument Type | Value Type | Attribute Key(s) | Attribute Values |
| -------------------------------------------- | ---------------------------------------------------------------- | ----- | -------------------- | ---------- | ---------------- | ----------------------------------- |
| db.sql.latency | The latency of calls in milliseconds | ms | Histogram | float64 | status | ok, error |
| | | | | | method | method name, like `sql.conn.query` |
| db.sql.connection.max_open | Maximum number of open connections to the database | | Asynchronous Gauge | int64 | | |
| db.sql.connection.open | The number of established connections both in use and idle | | Asynchronous Gauge | int64 | status | idle, inuse |
| db.sql.connection.wait_total | The total number of connections waited for | | Asynchronous Counter | int64 | | |
| db.sql.connection.wait_duration_total | The total time blocked waiting for a new connection | ms | Asynchronous Counter | float64 | | |
| db.sql.connection.closed_max_idle_total | The total number of connections closed due to SetMaxIdleConns | | Asynchronous Counter | int64 | | |
| db.sql.connection.closed_max_idle_time_total | The total number of connections closed due to SetConnMaxIdleTime | | Asynchronous Counter | int64 | | |
| db.sql.connection.closed_max_lifetime_total | The total number of connections closed due to SetConnMaxLifetime | | Asynchronous Counter | int64 | | |

## Example

See [example](./example/main.go)
Expand Down
23 changes: 23 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
metricglobal "go.opentelemetry.io/otel/metric/global"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
)
Expand All @@ -27,6 +29,12 @@ const (
instrumentationName = "github.com/XSAM/otelsql"
)

var (
connectionStatusKey = attribute.Key("status")
queryStatusKey = attribute.Key("status")
queryMethodKey = attribute.Key("method")
)

// SpanNameFormatter is an interface that used to format span names.
type SpanNameFormatter interface {
Format(ctx context.Context, method Method, query string) string
Expand All @@ -36,6 +44,11 @@ type config struct {
TracerProvider trace.TracerProvider
Tracer trace.Tracer

MeterProvider metric.MeterProvider
Meter metric.Meter

Instruments *instruments

SpanOptions SpanOptions

DBSystem string
Expand Down Expand Up @@ -88,6 +101,7 @@ func (f *defaultSpanNameFormatter) Format(ctx context.Context, method Method, qu
func newConfig(dbSystem string, options ...Option) config {
cfg := config{
TracerProvider: otel.GetTracerProvider(),
MeterProvider: metricglobal.MeterProvider(),
DBSystem: dbSystem,
SpanNameFormatter: &defaultSpanNameFormatter{},
}
Expand All @@ -104,6 +118,15 @@ func newConfig(dbSystem string, options ...Option) config {
instrumentationName,
trace.WithInstrumentationVersion(Version()),
)
cfg.Meter = cfg.MeterProvider.Meter(
instrumentationName,
metric.WithInstrumentationVersion(Version()),
)

var err error
if cfg.Instruments, err = newInstruments(cfg.Meter); err != nil {
otel.Handle(err)
}

return cfg
}
Expand Down
10 changes: 10 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"
)
Expand All @@ -32,11 +34,19 @@ func TestNewConfig(t *testing.T) {
instrumentationName,
trace.WithInstrumentationVersion(Version()),
),
MeterProvider: global.MeterProvider(),
Meter: global.MeterProvider().Meter(
instrumentationName,
metric.WithInstrumentationVersion(Version()),
),
// No need to check values of instruments in this part.
Instruments: cfg.Instruments,
SpanOptions: SpanOptions{Ping: true},
DBSystem: "db",
Attributes: []attribute.KeyValue{
semconv.DBSystemKey.String(cfg.DBSystem),
},
SpanNameFormatter: &defaultSpanNameFormatter{},
}, cfg)
assert.NotNil(t, cfg.Instruments)
}
48 changes: 42 additions & 6 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,15 @@ func (c *otConn) Ping(ctx context.Context) (err error) {
return driver.ErrSkip
}

method := MethodConnPing
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

if c.cfg.SpanOptions.Ping && (c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid()) {
var span trace.Span
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnPing, ""),
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, ""),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(c.cfg.Attributes...),
)
Expand Down Expand Up @@ -84,9 +90,15 @@ func (c *otConn) ExecContext(ctx context.Context, query string, args []driver.Na
return nil, driver.ErrSkip
}

method := MethodConnExec
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
if c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnExec, query),
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, query),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(withDBStatement(c.cfg, query)...),
)
Expand Down Expand Up @@ -115,10 +127,16 @@ func (c *otConn) QueryContext(ctx context.Context, query string, args []driver.N
return nil, driver.ErrSkip
}

method := MethodConnQuery
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
queryCtx := ctx
if c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
queryCtx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnQuery, query),
queryCtx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, query),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(withDBStatement(c.cfg, query)...),
)
Expand All @@ -139,9 +157,15 @@ func (c *otConn) PrepareContext(ctx context.Context, query string) (stmt driver.
return nil, driver.ErrSkip
}

method := MethodConnPrepare
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
if c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnPrepare, query),
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, query),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(withDBStatement(c.cfg, query)...),
)
Expand All @@ -162,10 +186,16 @@ func (c *otConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.
return nil, driver.ErrSkip
}

method := MethodConnBeginTx
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
beginTxCtx := ctx
if c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
beginTxCtx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnBeginTx, ""),
beginTxCtx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, ""),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(c.cfg.Attributes...),
)
Expand All @@ -186,9 +216,15 @@ func (c *otConn) ResetSession(ctx context.Context) (err error) {
return driver.ErrSkip
}

method := MethodConnResetSession
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
if c.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnResetSession, ""),
ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, method, ""),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(c.cfg.Attributes...),
)
Expand Down
12 changes: 6 additions & 6 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func TestOtConn_Ping(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.Ping = tc.pingOption
cfg.SpanOptions.AllowRoot = tc.allowRootOption
mc := newMockConn(tc.error)
Expand Down Expand Up @@ -258,7 +258,7 @@ func TestOtConn_ExecContext(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
cfg.SpanOptions.DisableQuery = tc.disableQuery
mc := newMockConn(tc.error)
Expand Down Expand Up @@ -334,7 +334,7 @@ func TestOtConn_QueryContext(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
cfg.SpanOptions.DisableQuery = tc.disableQuery
mc := newMockConn(tc.error)
Expand Down Expand Up @@ -428,7 +428,7 @@ func TestOtConn_PrepareContext(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
cfg.SpanOptions.DisableQuery = tc.disableQuery
mc := newMockConn(tc.error)
Expand Down Expand Up @@ -499,7 +499,7 @@ func TestOtConn_BeginTx(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
mc := newMockConn(tc.error)
otelConn := newConn(mc, cfg)
Expand Down Expand Up @@ -571,7 +571,7 @@ func TestOtConn_ResetSession(t *testing.T) {
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

// New conn
cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
mc := newMockConn(tc.error)
otelConn := newConn(mc, cfg)
Expand Down
8 changes: 7 additions & 1 deletion connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ func newConnector(connector driver.Connector, otDriver *otDriver) *otConnector {
}

func (c *otConnector) Connect(ctx context.Context) (connection driver.Conn, err error) {
method := MethodConnectorConnect
onDefer := recordMetric(ctx, c.otDriver.cfg.Instruments, c.otDriver.cfg.Attributes, method)
defer func() {
onDefer(err)
}()

var span trace.Span
if c.otDriver.cfg.SpanOptions.AllowRoot || trace.SpanContextFromContext(ctx).IsValid() {
ctx, span = c.otDriver.cfg.Tracer.Start(ctx, c.otDriver.cfg.SpanNameFormatter.Format(ctx, MethodConnectorConnect, ""),
ctx, span = c.otDriver.cfg.Tracer.Start(ctx, c.otDriver.cfg.SpanNameFormatter.Format(ctx, method, ""),
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(c.otDriver.cfg.Attributes...),
)
Expand Down
2 changes: 1 addition & 1 deletion connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestOtConnector_Connect(t *testing.T) {
// Prepare traces
ctx, sr, tracer, dummySpan := prepareTraces(tc.noParentSpan)

cfg := newMockConfig(tracer)
cfg := newMockConfig(t, tracer)
mConnector := newMockConnector(nil, tc.error)
cfg.SpanOptions.AllowRoot = tc.allowRootOption
connector := newConnector(mConnector, &otDriver{cfg: cfg})
Expand Down
2 changes: 2 additions & 0 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ services:
build:
dockerfile: $PWD/Dockerfile
context: ..
ports:
- 2222:2222
depends_on:
- mysql
9 changes: 6 additions & 3 deletions example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ replace github.com/XSAM/otelsql => ../
require (
github.com/XSAM/otelsql v0.0.0
github.com/go-sql-driver/mysql v1.6.0
go.opentelemetry.io/otel v1.5.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.5.0
go.opentelemetry.io/otel/sdk v1.5.0
go.opentelemetry.io/otel v1.6.0
go.opentelemetry.io/otel/exporters/prometheus v0.28.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.6.0
go.opentelemetry.io/otel/metric v0.28.0
go.opentelemetry.io/otel/sdk v1.6.0
go.opentelemetry.io/otel/sdk/metric v0.28.0
)
Loading

0 comments on commit a2304f4

Please sign in to comment.