From 690d06d241bf87dac3f480bbfda829efc0f86212 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:44:00 +0800 Subject: [PATCH 01/10] Add option and doc --- .../database/sql/otelsql/config.go | 98 +++++++++++++++++++ instrumentation/database/sql/otelsql/doc.go | 19 ++++ instrumentation/database/sql/otelsql/go.mod | 10 ++ instrumentation/database/sql/otelsql/go.sum | 17 ++++ .../database/sql/otelsql/option.go | 65 ++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 instrumentation/database/sql/otelsql/config.go create mode 100644 instrumentation/database/sql/otelsql/doc.go create mode 100644 instrumentation/database/sql/otelsql/go.mod create mode 100644 instrumentation/database/sql/otelsql/go.sum create mode 100644 instrumentation/database/sql/otelsql/option.go diff --git a/instrumentation/database/sql/otelsql/config.go b/instrumentation/database/sql/otelsql/config.go new file mode 100644 index 00000000000..ccb66b976dd --- /dev/null +++ b/instrumentation/database/sql/otelsql/config.go @@ -0,0 +1,98 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/contrib" +) + +const ( + instrumentationName = "go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql" +) + +// SpanNameFormatter is an interface that used to format span names. +type SpanNameFormatter interface { + Format(ctx context.Context, method Method, query string) string +} + +type config struct { + TracerProvider trace.TracerProvider + Tracer trace.Tracer + + SpanOptions SpanOptions + + DBSystem string + + // Attributes will be set to each span. + Attributes []label.KeyValue + + // SpanNameFormatter will be called to produce span's name. + // Default use method as span name + SpanNameFormatter SpanNameFormatter +} + +// SpanOptions holds configuration of tracing span to decide +// whether to enable some features. +// By default all options are set to false intentionally when creating a wrapped +// driver and provide the most sensible default with both performance and +// security in mind. +type SpanOptions struct { + // Ping, if set to true, will enable the creation of spans on Ping requests. + Ping bool + + // RowsNext, if set to true, will enable the creation of events in spans on RowsNext + // calls. This can result in many events. + RowsNext bool + + // DisableErrSkip, if set to true, will suppress driver.ErrSkip errors in spans. + DisableErrSkip bool +} + +type defaultSpanNameFormatter struct{} + +func (f *defaultSpanNameFormatter) Format(ctx context.Context, method Method, query string) string { + return string(method) +} + +// newConfig returns a config with all Options set. +func newConfig(dbSystem string, options ...Option) config { + cfg := config{ + TracerProvider: otel.GetTracerProvider(), + DBSystem: dbSystem, + SpanNameFormatter: &defaultSpanNameFormatter{}, + } + for _, opt := range options { + opt.Apply(&cfg) + } + + if cfg.DBSystem != "" { + cfg.Attributes = append(cfg.Attributes, + semconv.DBSystemKey.String(cfg.DBSystem), + ) + } + cfg.Tracer = cfg.TracerProvider.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(contrib.SemVersion()), + ) + + return cfg +} diff --git a/instrumentation/database/sql/otelsql/doc.go b/instrumentation/database/sql/otelsql/doc.go new file mode 100644 index 00000000000..37c89ce329a --- /dev/null +++ b/instrumentation/database/sql/otelsql/doc.go @@ -0,0 +1,19 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql instruments the database/sql package. +// +// otelsql will trace every interface from database/sql/driver package +// which has context except driver.Pinger. +package otelsql // import "go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql" diff --git a/instrumentation/database/sql/otelsql/go.mod b/instrumentation/database/sql/otelsql/go.mod new file mode 100644 index 00000000000..b54536242cf --- /dev/null +++ b/instrumentation/database/sql/otelsql/go.mod @@ -0,0 +1,10 @@ +module go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql + +go 1.15 + +replace go.opentelemetry.io/contrib => ../../../../ + +require ( + go.opentelemetry.io/contrib v0.16.0 + go.opentelemetry.io/otel v0.16.0 +) diff --git a/instrumentation/database/sql/otelsql/go.sum b/instrumentation/database/sql/otelsql/go.sum new file mode 100644 index 00000000000..59f82d0aa93 --- /dev/null +++ b/instrumentation/database/sql/otelsql/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= +go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/instrumentation/database/sql/otelsql/option.go b/instrumentation/database/sql/otelsql/option.go new file mode 100644 index 00000000000..91ca1facd5f --- /dev/null +++ b/instrumentation/database/sql/otelsql/option.go @@ -0,0 +1,65 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" +) + +// Option is the interface that applies a configuration option. +type Option interface { + // Apply sets the Option value of a config. + Apply(*config) +} + +var _ Option = OptionFunc(nil) + +// OptionFunc implements the Option interface. +type OptionFunc func(*config) + +func (f OptionFunc) Apply(c *config) { + f(c) +} + +// WithTracerProvider specifies a tracer provider to use for creating a tracer. +// If none is specified, the global provider is used. +func WithTracerProvider(provider trace.TracerProvider) Option { + return OptionFunc(func(cfg *config) { + cfg.TracerProvider = provider + }) +} + +// WithAttributes specifies attributes that will be set to each span. +func WithAttributes(attributes ...label.KeyValue) Option { + return OptionFunc(func(cfg *config) { + cfg.Attributes = attributes + }) +} + +// WithSpanNameFormatter takes an interface that will be called on every +// operation and the returned string will become the span name. +func WithSpanNameFormatter(spanNameFormatter SpanNameFormatter) Option { + return OptionFunc(func(cfg *config) { + cfg.SpanNameFormatter = spanNameFormatter + }) +} + +// WithSpanOptions specifies configuration for span to decide whether to enable some features. +func WithSpanOptions(opts SpanOptions) Option { + return OptionFunc(func(cfg *config) { + cfg.SpanOptions = opts + }) +} From e282b88288f823193c33fa82395387d49645a3e1 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:44:54 +0800 Subject: [PATCH 02/10] Add wrappers for driver, stmt, tx, conn, rows --- instrumentation/database/sql/otelsql/conn.go | 208 ++++++++++++++++++ .../database/sql/otelsql/connector.go | 46 ++++ .../database/sql/otelsql/driver.go | 53 +++++ .../database/sql/otelsql/methods.go | 39 ++++ instrumentation/database/sql/otelsql/rows.go | 141 ++++++++++++ instrumentation/database/sql/otelsql/sql.go | 80 +++++++ instrumentation/database/sql/otelsql/stmt.go | 100 +++++++++ instrumentation/database/sql/otelsql/tx.go | 68 ++++++ instrumentation/database/sql/otelsql/utils.go | 34 +++ 9 files changed, 769 insertions(+) create mode 100644 instrumentation/database/sql/otelsql/conn.go create mode 100644 instrumentation/database/sql/otelsql/connector.go create mode 100644 instrumentation/database/sql/otelsql/driver.go create mode 100644 instrumentation/database/sql/otelsql/methods.go create mode 100644 instrumentation/database/sql/otelsql/rows.go create mode 100644 instrumentation/database/sql/otelsql/sql.go create mode 100644 instrumentation/database/sql/otelsql/stmt.go create mode 100644 instrumentation/database/sql/otelsql/tx.go create mode 100644 instrumentation/database/sql/otelsql/utils.go diff --git a/instrumentation/database/sql/otelsql/conn.go b/instrumentation/database/sql/otelsql/conn.go new file mode 100644 index 00000000000..3bf881e63d5 --- /dev/null +++ b/instrumentation/database/sql/otelsql/conn.go @@ -0,0 +1,208 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" +) + +var ( + _ driver.Pinger = (*otConn)(nil) + _ driver.Execer = (*otConn)(nil) // nolint + _ driver.ExecerContext = (*otConn)(nil) + _ driver.Queryer = (*otConn)(nil) // nolint + _ driver.QueryerContext = (*otConn)(nil) + _ driver.Conn = (*otConn)(nil) + _ driver.ConnPrepareContext = (*otConn)(nil) + _ driver.ConnBeginTx = (*otConn)(nil) + _ driver.SessionResetter = (*otConn)(nil) + _ driver.NamedValueChecker = (*otConn)(nil) +) + +type otConn struct { + driver.Conn + otDriver *otDriver + cfg config +} + +func newConn(conn driver.Conn, otDriver *otDriver) *otConn { + return &otConn{ + Conn: conn, + otDriver: otDriver, + cfg: otDriver.cfg, + } +} + +func (c *otConn) Ping(ctx context.Context) (err error) { + pinger, ok := c.Conn.(driver.Pinger) + if !ok { + return driver.ErrSkip + } + + if c.otDriver.cfg.SpanOptions.Ping { + var span trace.Span + ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnPing, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(c.cfg.Attributes...), + ) + defer func() { + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + } + span.End() + }() + } + + err = pinger.Ping(ctx) + return err +} + +func (c *otConn) Exec(query string, args []driver.Value) (driver.Result, error) { + execer, ok := c.Conn.(driver.Execer) // nolint + if !ok { + return nil, driver.ErrSkip + } + return execer.Exec(query, args) +} + +func (c *otConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (res driver.Result, err error) { + execer, ok := c.Conn.(driver.ExecerContext) + if !ok { + return nil, driver.ErrSkip + } + + ctx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnExec, query), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + append(c.cfg.Attributes, + semconv.DBStatementKey.String(query), + )...), + ) + defer span.End() + + res, err = execer.ExecContext(ctx, query, args) + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + return nil, err + } + return res, nil +} + +func (c *otConn) Query(query string, args []driver.Value) (driver.Rows, error) { + queryer, ok := c.Conn.(driver.Queryer) // nolint + if !ok { + return nil, driver.ErrSkip + } + return queryer.Query(query, args) +} + +func (c *otConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { + queryer, ok := c.Conn.(driver.QueryerContext) + if !ok { + return nil, driver.ErrSkip + } + + queryCtx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnQuery, query), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + append(c.cfg.Attributes, + semconv.DBStatementKey.String(query), + )...), + ) + defer span.End() + + rows, err = queryer.QueryContext(queryCtx, query, args) + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + return nil, err + } + return newRows(ctx, rows, c.cfg), nil +} + +func (c *otConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { + preparer, ok := c.Conn.(driver.ConnPrepareContext) + if !ok { + return nil, driver.ErrSkip + } + + ctx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnPrepare, query), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + append(c.cfg.Attributes, + semconv.DBStatementKey.String(query), + )...), + ) + defer span.End() + + stmt, err = preparer.PrepareContext(ctx, query) + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + return nil, err + } + return newStmt(stmt, c.cfg, query), nil +} + +func (c *otConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { + connBeginTx, ok := c.Conn.(driver.ConnBeginTx) + if !ok { + return nil, driver.ErrSkip + } + + ctx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnBeginTx, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(c.cfg.Attributes...), + ) + defer span.End() + + tx, err = connBeginTx.BeginTx(ctx, opts) + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + return nil, err + } + return newTx(ctx, tx, c.cfg), nil +} + +func (c *otConn) ResetSession(ctx context.Context) (err error) { + sessionResetter, ok := c.Conn.(driver.SessionResetter) + if !ok { + return driver.ErrSkip + } + + ctx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnResetSession, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(c.cfg.Attributes...), + ) + defer span.End() + + err = sessionResetter.ResetSession(ctx) + if err != nil { + recordSpanError(span, c.cfg.SpanOptions, err) + return err + } + return nil +} + +func (c *otConn) CheckNamedValue(namedValue *driver.NamedValue) error { + namedValueChecker, ok := c.Conn.(driver.NamedValueChecker) + if !ok { + return driver.ErrSkip + } + + return namedValueChecker.CheckNamedValue(namedValue) +} diff --git a/instrumentation/database/sql/otelsql/connector.go b/instrumentation/database/sql/otelsql/connector.go new file mode 100644 index 00000000000..0a87dfb8d87 --- /dev/null +++ b/instrumentation/database/sql/otelsql/connector.go @@ -0,0 +1,46 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" +) + +var _ driver.Connector = (*otConnector)(nil) + +type otConnector struct { + driver.Connector + otDriver *otDriver +} + +func newConnector(connector driver.Connector, otDriver *otDriver) *otConnector { + return &otConnector{ + Connector: connector, + otDriver: otDriver, + } +} + +func (c *otConnector) Connect(ctx context.Context) (connection driver.Conn, err error) { + connection, err = c.Connector.Connect(ctx) + if err != nil { + return nil, err + } + return newConn(connection, c.otDriver), nil +} + +func (c *otConnector) Driver() driver.Driver { + return c.otDriver +} diff --git a/instrumentation/database/sql/otelsql/driver.go b/instrumentation/database/sql/otelsql/driver.go new file mode 100644 index 00000000000..b96573c8f82 --- /dev/null +++ b/instrumentation/database/sql/otelsql/driver.go @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "database/sql/driver" +) + +var ( + _ driver.Driver = (*otDriver)(nil) + _ driver.DriverContext = (*otDriver)(nil) +) + +type otDriver struct { + driver driver.Driver + cfg config +} + +func newDriver(dri driver.Driver, cfg config) driver.Driver { + if _, ok := dri.(driver.DriverContext); ok { + return &otDriver{driver: dri, cfg: cfg} + } + // Only implements driver.Driver + return struct{ driver.Driver }{&otDriver{driver: dri, cfg: cfg}} +} + +func (d *otDriver) Open(name string) (driver.Conn, error) { + rawConn, err := d.driver.Open(name) + if err != nil { + return nil, err + } + return newConn(rawConn, d), nil +} + +func (d *otDriver) OpenConnector(name string) (driver.Connector, error) { + rawConnector, err := d.driver.(driver.DriverContext).OpenConnector(name) + if err != nil { + return nil, err + } + return newConnector(rawConnector, d), err +} diff --git a/instrumentation/database/sql/otelsql/methods.go b/instrumentation/database/sql/otelsql/methods.go new file mode 100644 index 00000000000..58f3e2ca327 --- /dev/null +++ b/instrumentation/database/sql/otelsql/methods.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +// Method specifics operation in the database/sql package. +type Method string + +// Method specifics events in the database/sql package. +type Event string + +const ( + MethodConnPing Method = "sql.conn.ping" + MethodConnExec Method = "sql.conn.exec" + MethodConnQuery Method = "sql.conn.query" + MethodConnPrepare Method = "sql.conn.prepare" + MethodConnBeginTx Method = "sql.conn.begin_tx" + MethodConnResetSession Method = "sql.conn.reset_session" + MethodTxCommit Method = "sql.tx.commit" + MethodTxRollback Method = "sql.tx.rollback" + MethodStmtExec Method = "sql.stmt.exec" + MethodStmtQuery Method = "sql.stmt.query" + MethodRows Method = "sql.rows" +) + +const ( + EventRowsNext Event = "sql.rows.next" +) diff --git a/instrumentation/database/sql/otelsql/rows.go b/instrumentation/database/sql/otelsql/rows.go new file mode 100644 index 00000000000..a216253963f --- /dev/null +++ b/instrumentation/database/sql/otelsql/rows.go @@ -0,0 +1,141 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "io" + + "go.opentelemetry.io/otel/trace" +) + +var ( + _ driver.Rows = (*otRows)(nil) + _ driver.RowsNextResultSet = (*otRows)(nil) + _ driver.RowsColumnTypeDatabaseTypeName = (*otRows)(nil) + _ driver.RowsColumnTypeLength = (*otRows)(nil) + _ driver.RowsColumnTypeNullable = (*otRows)(nil) + _ driver.RowsColumnTypePrecisionScale = (*otRows)(nil) +) + +type otRows struct { + driver.Rows + + span trace.Span + cfg config +} + +func newRows(ctx context.Context, rows driver.Rows, cfg config) *otRows { + _, span := cfg.Tracer.Start(ctx, cfg.SpanNameFormatter.Format(ctx, MethodRows, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(cfg.Attributes...), + ) + + return &otRows{ + Rows: rows, + span: span, + cfg: cfg, + } +} + +// HasNextResultSet calls the implements the driver.RowsNextResultSet for otRows. +// It returns the the underlying result of HasNextResultSet from the otRows.parent +// if the parent implements driver.RowsNextResultSet. +func (r otRows) HasNextResultSet() bool { + if v, ok := r.Rows.(driver.RowsNextResultSet); ok { + return v.HasNextResultSet() + } + + return false +} + +// NextResultsSet calls the implements the driver.RowsNextResultSet for otRows. +// It returns the the underlying result of NextResultSet from the otRows.parent +// if the parent implements driver.RowsNextResultSet. +func (r otRows) NextResultSet() error { + if v, ok := r.Rows.(driver.RowsNextResultSet); ok { + return v.NextResultSet() + } + + return io.EOF +} + +// ColumnTypeDatabaseTypeName calls the implements the driver.RowsColumnTypeDatabaseTypeName for otRows. +// It returns the the underlying result of ColumnTypeDatabaseTypeName from the otRows.Rows +// if the Rows implements driver.RowsColumnTypeDatabaseTypeName. +func (r otRows) ColumnTypeDatabaseTypeName(index int) string { + if v, ok := r.Rows.(driver.RowsColumnTypeDatabaseTypeName); ok { + return v.ColumnTypeDatabaseTypeName(index) + } + + return "" +} + +// ColumnTypeLength calls the implements the driver.RowsColumnTypeLength for otRows. +// It returns the the underlying result of ColumnTypeLength from the otRows.Rows +// if the Rows implements driver.RowsColumnTypeLength. +func (r otRows) ColumnTypeLength(index int) (length int64, ok bool) { + if v, ok := r.Rows.(driver.RowsColumnTypeLength); ok { + return v.ColumnTypeLength(index) + } + + return 0, false +} + +// ColumnTypeNullable calls the implements the driver.RowsColumnTypeNullable for otRows. +// It returns the the underlying result of ColumnTypeNullable from the otRows.Rows +// if the Rows implements driver.RowsColumnTypeNullable. +func (r otRows) ColumnTypeNullable(index int) (nullable, ok bool) { + if v, ok := r.Rows.(driver.RowsColumnTypeNullable); ok { + return v.ColumnTypeNullable(index) + } + + return false, false +} + +// ColumnTypePrecisionScale calls the implements the driver.RowsColumnTypePrecisionScale for otRows. +// It returns the the underlying result of ColumnTypePrecisionScale from the otRows.Rows +// if the Rows implements driver.RowsColumnTypePrecisionScale. +func (r otRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { + if v, ok := r.Rows.(driver.RowsColumnTypePrecisionScale); ok { + return v.ColumnTypePrecisionScale(index) + } + + return 0, 0, false +} + +func (r otRows) Close() (err error) { + defer r.span.End() + + err = r.Rows.Close() + if err != nil { + recordSpanError(r.span, r.cfg.SpanOptions, err) + } + return +} + +func (r otRows) Next(dest []driver.Value) (err error) { + if r.cfg.SpanOptions.RowsNext { + r.span.AddEvent(string(EventRowsNext)) + } + + err = r.Rows.Next(dest) + // io.EOF is not an error. It is expected to happen during iteration. + if err != nil && err != io.EOF { + recordSpanError(r.span, r.cfg.SpanOptions, err) + } + return +} diff --git a/instrumentation/database/sql/otelsql/sql.go b/instrumentation/database/sql/otelsql/sql.go new file mode 100644 index 00000000000..94f5c096867 --- /dev/null +++ b/instrumentation/database/sql/otelsql/sql.go @@ -0,0 +1,80 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "database/sql" + "database/sql/driver" + "errors" + "strconv" + "sync" +) + +var registerLock sync.Mutex + +// Register initializes and registers our OTel wrapped database driver +// identified by its driverName, using provided Option. +// It is possible to register multiple wrappers for the same database driver if +// needing different Option for different connections. +// Parameter dbSystem is an identifier for the database management system (DBMS) +// product being used. +// +// For more information, see semantic conventions for database +// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md +func Register(driverName string, dbSystem string, options ...Option) (string, error) { + // Retrieve the driver implementation we need to wrap with instrumentation + db, err := sql.Open(driverName, "") + if err != nil { + return "", err + } + dri := db.Driver() + if err = db.Close(); err != nil { + return "", err + } + + registerLock.Lock() + defer registerLock.Unlock() + + // Since we might want to register multiple OTel drivers to have different + // configurations, but potentially the same underlying database driver, we + // cycle through to find available driver names. + driverName = driverName + "-otelsql-" + for i := int64(0); i < 1000; i++ { + var ( + found = false + regName = driverName + strconv.FormatInt(i, 10) + ) + for _, name := range sql.Drivers() { + if name == regName { + found = true + } + } + if !found { + sql.Register(regName, newDriver(dri, newConfig(dbSystem, options...))) + return regName, nil + } + } + return "", errors.New("unable to register driver, all slots have been taken") +} + +// WrapDriver takes a SQL driver and wraps it with OTel instrumentation. +// Parameter dbSystem is an identifier for the database management system (DBMS) +// product being used. +// +// For more information, see semantic conventions for database +// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md +func WrapDriver(dri driver.Driver, dbSystem string, options ...Option) driver.Driver { + return newDriver(dri, newConfig(dbSystem, options...)) +} diff --git a/instrumentation/database/sql/otelsql/stmt.go b/instrumentation/database/sql/otelsql/stmt.go new file mode 100644 index 00000000000..3f7551f6815 --- /dev/null +++ b/instrumentation/database/sql/otelsql/stmt.go @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" +) + +var ( + _ driver.Stmt = (*otStmt)(nil) + _ driver.StmtExecContext = (*otStmt)(nil) + _ driver.StmtQueryContext = (*otStmt)(nil) + _ driver.NamedValueChecker = (*otStmt)(nil) +) + +type otStmt struct { + driver.Stmt + cfg config + + query string +} + +func newStmt(stmt driver.Stmt, cfg config, query string) *otStmt { + return &otStmt{ + Stmt: stmt, + cfg: cfg, + query: query, + } +} + +func (s *otStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (result driver.Result, err error) { + execer, ok := s.Stmt.(driver.StmtExecContext) + if !ok { + return nil, driver.ErrSkip + } + + ctx, span := s.cfg.Tracer.Start(ctx, s.cfg.SpanNameFormatter.Format(ctx, MethodStmtExec, s.query), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + append(s.cfg.Attributes, + semconv.DBStatementKey.String(s.query), + )...), + ) + defer span.End() + + result, err = execer.ExecContext(ctx, args) + if err != nil { + recordSpanError(span, s.cfg.SpanOptions, err) + return nil, err + } + return result, nil +} + +func (s *otStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { + query, ok := s.Stmt.(driver.StmtQueryContext) + if !ok { + return nil, driver.ErrSkip + } + + queryCtx, span := s.cfg.Tracer.Start(ctx, s.cfg.SpanNameFormatter.Format(ctx, MethodStmtQuery, s.query), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes( + append(s.cfg.Attributes, + semconv.DBStatementKey.String(s.query), + )...), + ) + defer span.End() + + rows, err = query.QueryContext(queryCtx, args) + if err != nil { + recordSpanError(span, s.cfg.SpanOptions, err) + return nil, err + } + return newRows(ctx, rows, s.cfg), nil +} + +func (s *otStmt) CheckNamedValue(namedValue *driver.NamedValue) error { + namedValueChecker, ok := s.Stmt.(driver.NamedValueChecker) + if !ok { + return driver.ErrSkip + } + + return namedValueChecker.CheckNamedValue(namedValue) +} diff --git a/instrumentation/database/sql/otelsql/tx.go b/instrumentation/database/sql/otelsql/tx.go new file mode 100644 index 00000000000..4b7470bf3e5 --- /dev/null +++ b/instrumentation/database/sql/otelsql/tx.go @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + + "go.opentelemetry.io/otel/trace" +) + +var _ driver.Tx = (*otTx)(nil) + +type otTx struct { + tx driver.Tx + ctx context.Context + cfg config +} + +func newTx(ctx context.Context, tx driver.Tx, cfg config) *otTx { + return &otTx{ + tx: tx, + ctx: ctx, + cfg: cfg, + } +} + +func (t *otTx) Commit() (err error) { + _, span := t.cfg.Tracer.Start(t.ctx, t.cfg.SpanNameFormatter.Format(t.ctx, MethodTxCommit, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(t.cfg.Attributes...), + ) + defer span.End() + + err = t.tx.Commit() + if err != nil { + recordSpanError(span, t.cfg.SpanOptions, err) + return err + } + return nil +} + +func (t *otTx) Rollback() (err error) { + _, span := t.cfg.Tracer.Start(t.ctx, t.cfg.SpanNameFormatter.Format(t.ctx, MethodTxRollback, ""), + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(t.cfg.Attributes...), + ) + defer span.End() + + err = t.tx.Rollback() + if err != nil { + recordSpanError(span, t.cfg.SpanOptions, err) + return err + } + return nil +} diff --git a/instrumentation/database/sql/otelsql/utils.go b/instrumentation/database/sql/otelsql/utils.go new file mode 100644 index 00000000000..a175b5a4835 --- /dev/null +++ b/instrumentation/database/sql/otelsql/utils.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "database/sql/driver" + + "go.opentelemetry.io/otel/trace" +) + +func recordSpanError(span trace.Span, opts SpanOptions, err error) { + switch err { + case nil: + return + case driver.ErrSkip: + if !opts.DisableErrSkip { + span.RecordError(err) + } + default: + span.RecordError(err) + } +} From 164fde4f377614144071b9981d46defa1a93b89a Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:45:57 +0800 Subject: [PATCH 03/10] Update README --- instrumentation/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/instrumentation/README.md b/instrumentation/README.md index f6787fcb54a..37562141747 100644 --- a/instrumentation/README.md +++ b/instrumentation/README.md @@ -23,6 +23,7 @@ The following instrumentation packages are provided for popular Go packages and | [net/http](./net/http/otelhttp) | ✓ | ✓ | | [net/http/httptrace](./net/http/httptrace/otelhttptrace) | | ✓ | | [runtime](./runtime) | ✓ | | +| [database/sql](./database/sql/otelsql) | | ✓ | Additionally, these are the known instrumentation packages that exist outside of this repository for popular Go packages. From 10dc6fe51e73291a53f76cdd82af01b74ceb9fc5 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:48:01 +0800 Subject: [PATCH 04/10] Add example --- .../database/sql/otelsql/example/Dockerfile | 20 ++++ .../database/sql/otelsql/example/README.md | 25 +++++ .../sql/otelsql/example/docker-compose.yml | 29 ++++++ .../database/sql/otelsql/example/go.mod | 16 +++ .../database/sql/otelsql/example/go.sum | 25 +++++ .../database/sql/otelsql/example/main.go | 98 +++++++++++++++++++ 6 files changed, 213 insertions(+) create mode 100644 instrumentation/database/sql/otelsql/example/Dockerfile create mode 100644 instrumentation/database/sql/otelsql/example/README.md create mode 100644 instrumentation/database/sql/otelsql/example/docker-compose.yml create mode 100644 instrumentation/database/sql/otelsql/example/go.mod create mode 100644 instrumentation/database/sql/otelsql/example/go.sum create mode 100644 instrumentation/database/sql/otelsql/example/main.go diff --git a/instrumentation/database/sql/otelsql/example/Dockerfile b/instrumentation/database/sql/otelsql/example/Dockerfile new file mode 100644 index 00000000000..a0a3788717d --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/Dockerfile @@ -0,0 +1,20 @@ +# Copyright The OpenTelemetry Authors +# +# 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. +FROM golang:alpine AS base +COPY . /src/ +WORKDIR /src/instrumentation/database/sql/otelsql/example + +FROM base +RUN go install main.go +CMD ["/go/bin/main"] diff --git a/instrumentation/database/sql/otelsql/example/README.md b/instrumentation/database/sql/otelsql/example/README.md new file mode 100644 index 00000000000..fa8c92b868b --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/README.md @@ -0,0 +1,25 @@ +# database/sql instrumentation example + +A MySQL client using database/sql with instrumentation. + +These instructions expect you have +[docker-compose](https://docs.docker.com/compose/) installed. + +Bring up the `Mysql` services to run the +example: + +```sh +docker-compose up -d mysql +``` + +Then up the `client` service to make request with `MySQL`: + +```sh +docker-compose up client +``` + +Shut down the services when you are finished with the example: + +```sh +docker-compose down +``` diff --git a/instrumentation/database/sql/otelsql/example/docker-compose.yml b/instrumentation/database/sql/otelsql/example/docker-compose.yml new file mode 100644 index 00000000000..464f79c1d52 --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/docker-compose.yml @@ -0,0 +1,29 @@ +# Copyright The OpenTelemetry Authors +# +# 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. +version: "3.7" +services: + mysql: + image: mysql:5.7 + ports: + - 3306:3306 + environment: + - MYSQL_ROOT_PASSWORD=otel_password + - MYSQL_DATABASE=db + + client: + build: + dockerfile: $PWD/Dockerfile + context: ../../../../.. + depends_on: + - mysql diff --git a/instrumentation/database/sql/otelsql/example/go.mod b/instrumentation/database/sql/otelsql/example/go.mod new file mode 100644 index 00000000000..8303ba11cac --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/go.mod @@ -0,0 +1,16 @@ +module go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql/example + +go 1.15 + +replace ( + go.opentelemetry.io/contrib => ../../../../../ + go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql => ../ +) + +require ( + github.com/go-sql-driver/mysql v1.5.0 + go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql v0.16.0 + go.opentelemetry.io/otel v0.16.0 + go.opentelemetry.io/otel/exporters/stdout v0.16.0 + go.opentelemetry.io/otel/sdk v0.16.0 +) diff --git a/instrumentation/database/sql/otelsql/example/go.sum b/instrumentation/database/sql/otelsql/example/go.sum new file mode 100644 index 00000000000..dd400554e9b --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/go.sum @@ -0,0 +1,25 @@ +github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= +go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= +go.opentelemetry.io/otel/exporters/stdout v0.16.0 h1:lQG6ZZYLh3NxnmrHltRmqZolT/jPJ8Qfl74lWT8g69Y= +go.opentelemetry.io/otel/exporters/stdout v0.16.0/go.mod h1:bq7m22M7WIxz30KnxH9lI4RLKPajk0lnLsd5P2MsSv8= +go.opentelemetry.io/otel/sdk v0.16.0 h1:5o+fkNsOfH5Mix1bHUApNBqeDcAYczHDa7Ix+R73K2U= +go.opentelemetry.io/otel/sdk v0.16.0/go.mod h1:Jb0B4wrxerxtBeapvstmAZvJGQmvah4dHgKSngDpiCo= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/instrumentation/database/sql/otelsql/example/main.go b/instrumentation/database/sql/otelsql/example/main.go new file mode 100644 index 00000000000..05b3b8fa382 --- /dev/null +++ b/instrumentation/database/sql/otelsql/example/main.go @@ -0,0 +1,98 @@ +// Copyright The OpenTelemetry Authors +// +// 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 main + +import ( + "context" + "database/sql" + "fmt" + "log" + "time" + + _ "github.com/go-sql-driver/mysql" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/semconv" + + "go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql" +) + +const instrumentationName = "go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql/example" + +var mysqlDSN = "root:otel_password@tcp(mysql)/db?parseTime=true" + +func initTracer() { + exporter, err := stdout.NewExporter(stdout.WithPrettyPrint()) + if err != nil { + log.Fatal(err) + } + cfg := sdktrace.Config{ + DefaultSampler: sdktrace.AlwaysSample(), + } + tp := sdktrace.NewTracerProvider( + sdktrace.WithConfig(cfg), + sdktrace.WithSyncer(exporter), + ) + + otel.SetTracerProvider(tp) +} + +func main() { + initTracer() + + // Register an OTel driver + driverName, err := otelsql.Register("mysql", semconv.DBSystemMySQL.Value.AsString()) + if err != nil { + panic(err) + } + + // Connect to database + db, err := sql.Open(driverName, mysqlDSN) + if err != nil { + panic(err) + } + defer db.Close() + + err = query(db) + if err != nil { + panic(err) + } +} + +func query(db *sql.DB) error { + // Create a span + tracer := otel.GetTracerProvider() + ctx, span := tracer.Tracer(instrumentationName).Start(context.Background(), "example") + defer span.End() + + // Make a query + rows, err := db.QueryContext(ctx, `SELECT CURRENT_TIMESTAMP`) + if err != nil { + return err + } + defer rows.Close() + + var currentTime time.Time + for rows.Next() { + err = rows.Scan(¤tTime) + if err != nil { + return err + } + } + fmt.Println(currentTime) + return nil +} From 8f74efef12ab09696d3b604ab1602997bee5b9e7 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:48:28 +0800 Subject: [PATCH 05/10] Add dependabot --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2f2281b17a6..9404372b693 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -456,3 +456,21 @@ updates: schedule: interval: "weekly" day: "sunday" + - package-ecosystem: "gomod" + directory: "/instrumentation/database/sql/otelsql" + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + interval: "weekly" + day: "sunday" + - package-ecosystem: "gomod" + directory: "/instrumentation/database/sql/otelsql/example" + labels: + - dependencies + - go + - "Skip Changelog" + schedule: + interval: "weekly" + day: "sunday" From 62b237a590e1b53a82e5d85d0f6c1d3bea8df05b Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 16:49:35 +0800 Subject: [PATCH 06/10] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91b685de7d9..0ea09cbce8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [0.16.0] - 2021-01-13 +### Added + +- Tracing instrumentation for database/sql. (#505) + ### Fixed - Fix module path for AWS ECS resource detector (#517) From 32af7c84bff72ded3b18bc971570ac714d94030e Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Tue, 26 Jan 2021 19:02:12 +0800 Subject: [PATCH 07/10] Fix typo in CHANGELOG --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ea09cbce8a..d14ddfd836c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] -## [0.16.0] - 2021-01-13 - ### Added - Tracing instrumentation for database/sql. (#505) +## [0.16.0] - 2021-01-13 + ### Fixed - Fix module path for AWS ECS resource detector (#517) From d403894091a35a0d03d9d450df1fe67f9e027578 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Thu, 28 Jan 2021 20:05:51 +0800 Subject: [PATCH 08/10] Add tests --- .../database/sql/otelsql/config_test.go | 45 ++ instrumentation/database/sql/otelsql/conn.go | 16 +- .../database/sql/otelsql/conn_test.go | 488 ++++++++++++++++++ .../database/sql/otelsql/connector.go | 2 +- .../database/sql/otelsql/connector_test.go | 101 ++++ .../database/sql/otelsql/driver.go | 2 +- .../database/sql/otelsql/driver_test.go | 139 +++++ .../database/sql/otelsql/example/go.sum | 2 + instrumentation/database/sql/otelsql/go.mod | 1 + instrumentation/database/sql/otelsql/go.sum | 2 + .../database/sql/otelsql/option_test.go | 71 +++ .../database/sql/otelsql/rows_test.go | 196 +++++++ instrumentation/database/sql/otelsql/sql.go | 6 +- .../database/sql/otelsql/sql_test.go | 69 +++ .../database/sql/otelsql/stmt_test.go | 184 +++++++ .../database/sql/otelsql/tx_test.go | 162 ++++++ .../database/sql/otelsql/utils_test.go | 110 ++++ 17 files changed, 1583 insertions(+), 13 deletions(-) create mode 100644 instrumentation/database/sql/otelsql/config_test.go create mode 100644 instrumentation/database/sql/otelsql/conn_test.go create mode 100644 instrumentation/database/sql/otelsql/connector_test.go create mode 100644 instrumentation/database/sql/otelsql/driver_test.go create mode 100644 instrumentation/database/sql/otelsql/option_test.go create mode 100644 instrumentation/database/sql/otelsql/rows_test.go create mode 100644 instrumentation/database/sql/otelsql/sql_test.go create mode 100644 instrumentation/database/sql/otelsql/stmt_test.go create mode 100644 instrumentation/database/sql/otelsql/tx_test.go create mode 100644 instrumentation/database/sql/otelsql/utils_test.go diff --git a/instrumentation/database/sql/otelsql/config_test.go b/instrumentation/database/sql/otelsql/config_test.go new file mode 100644 index 00000000000..1224f247078 --- /dev/null +++ b/instrumentation/database/sql/otelsql/config_test.go @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/contrib" +) + +func TestNewConfig(t *testing.T) { + cfg := newConfig("db", WithSpanOptions(SpanOptions{Ping: true})) + assert.Equal(t, config{ + TracerProvider: otel.GetTracerProvider(), + Tracer: otel.GetTracerProvider().Tracer( + instrumentationName, + trace.WithInstrumentationVersion(contrib.SemVersion()), + ), + SpanOptions: SpanOptions{Ping: true}, + DBSystem: "db", + Attributes: []label.KeyValue{ + semconv.DBSystemKey.String(cfg.DBSystem), + }, + SpanNameFormatter: &defaultSpanNameFormatter{}, + }, cfg) +} diff --git a/instrumentation/database/sql/otelsql/conn.go b/instrumentation/database/sql/otelsql/conn.go index 3bf881e63d5..2dd68501933 100644 --- a/instrumentation/database/sql/otelsql/conn.go +++ b/instrumentation/database/sql/otelsql/conn.go @@ -37,15 +37,13 @@ var ( type otConn struct { driver.Conn - otDriver *otDriver - cfg config + cfg config } -func newConn(conn driver.Conn, otDriver *otDriver) *otConn { +func newConn(conn driver.Conn, cfg config) *otConn { return &otConn{ - Conn: conn, - otDriver: otDriver, - cfg: otDriver.cfg, + Conn: conn, + cfg: cfg, } } @@ -55,7 +53,7 @@ func (c *otConn) Ping(ctx context.Context) (err error) { return driver.ErrSkip } - if c.otDriver.cfg.SpanOptions.Ping { + if c.cfg.SpanOptions.Ping { var span trace.Span ctx, span = c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnPing, ""), trace.WithSpanKind(trace.SpanKindClient), @@ -164,13 +162,13 @@ func (c *otConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver. return nil, driver.ErrSkip } - ctx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnBeginTx, ""), + beginTxCtx, span := c.cfg.Tracer.Start(ctx, c.cfg.SpanNameFormatter.Format(ctx, MethodConnBeginTx, ""), trace.WithSpanKind(trace.SpanKindClient), trace.WithAttributes(c.cfg.Attributes...), ) defer span.End() - tx, err = connBeginTx.BeginTx(ctx, opts) + tx, err = connBeginTx.BeginTx(beginTxCtx, opts) if err != nil { recordSpanError(span, c.cfg.SpanOptions, err) return nil, err diff --git a/instrumentation/database/sql/otelsql/conn_test.go b/instrumentation/database/sql/otelsql/conn_test.go new file mode 100644 index 00000000000..6ee39ac1829 --- /dev/null +++ b/instrumentation/database/sql/otelsql/conn_test.go @@ -0,0 +1,488 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/oteltest" + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" +) + +type mockConn struct { + shouldError bool + + resetSessionCount int + resetSessionCtx context.Context + + beginTxCount int + beginTxCtx context.Context + + prepareContextCount int + prepareContextCtx context.Context + prepareContextQuery string + + queryContextCount int + queryContextCtx context.Context + queryContextQuery string + + execContextCount int + execContextCtx context.Context + execContextQuery string + + pingCount int + pingCtx context.Context +} + +func newMockConn(shouldError bool) *mockConn { + return &mockConn{shouldError: shouldError} +} + +func (m *mockConn) ResetSession(ctx context.Context) error { + m.resetSessionCtx = ctx + m.resetSessionCount++ + if m.shouldError { + return errors.New("resetSession") + } + return nil +} + +func (m *mockConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + m.beginTxCount++ + m.beginTxCtx = ctx + if m.shouldError { + return nil, errors.New("beginTx") + } + return newMockTx(false), nil +} + +func (m *mockConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + m.prepareContextCount++ + m.prepareContextCtx = ctx + m.prepareContextQuery = query + if m.shouldError { + return nil, errors.New("prepareContext") + } + return newMockStmt(false), nil +} + +func (m *mockConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + m.queryContextCount++ + m.queryContextCtx = ctx + m.queryContextQuery = query + if m.shouldError { + return nil, errors.New("queryContext") + } + return newMockRows(false), nil +} + +func (m *mockConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + m.execContextCount++ + m.execContextCtx = ctx + m.execContextQuery = query + if m.shouldError { + return nil, errors.New("execContext") + } + return nil, nil +} + +func (m *mockConn) Ping(ctx context.Context) error { + m.pingCount++ + m.pingCtx = ctx + if m.shouldError { + return errors.New("execContext") + } + return nil +} + +func (m *mockConn) Prepare(query string) (driver.Stmt, error) { + return newMockStmt(false), nil +} + +func (m *mockConn) Close() error { + return nil +} + +func (m *mockConn) Begin() (driver.Tx, error) { + return newMockTx(false), nil +} + +var ( + _ driver.Pinger = (*mockConn)(nil) + _ driver.ExecerContext = (*mockConn)(nil) + _ driver.QueryerContext = (*mockConn)(nil) + _ driver.Conn = (*mockConn)(nil) + _ driver.ConnPrepareContext = (*mockConn)(nil) + _ driver.ConnBeginTx = (*mockConn)(nil) + _ driver.SessionResetter = (*mockConn)(nil) +) + +func TestOtConn_Ping(t *testing.T) { + testCases := []struct { + name string + error bool + pingOption bool + }{ + { + name: "ping enabled", + pingOption: true, + }, + { + name: "ping enabled with error", + pingOption: true, + error: true, + }, + { + name: "ping disabled", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + cfg.SpanOptions.Ping = tc.pingOption + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + err := otelConn.Ping(ctx) + if tc.error { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + if tc.pingOption { + spanList := sr.Completed() + // One dummy span and one span created in Ping + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodConnPing), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.pingCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.pingCtx)) + if tc.error { + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + assert.Equal(t, codes.Unset, span.StatusCode()) + } + } + }) + } +} + +func TestOtConn_ExecContext(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + _, err := otelConn.ExecContext(ctx, "query", nil) + + spanList := sr.Completed() + // One dummy span and one span created in ExecContext + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + cfg.Attributes...)), span.Attributes()) + assert.Equal(t, string(MethodConnExec), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.execContextCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.execContextCtx)) + assert.Equal(t, "query", mc.execContextQuery) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} + +func TestOtConn_QueryContext(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + rows, err := otelConn.QueryContext(ctx, "query", nil) + + spanList := sr.Completed() + // One dummy span and one span created in QueryContext + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + cfg.Attributes...)), span.Attributes()) + assert.Equal(t, string(MethodConnQuery), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.queryContextCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.queryContextCtx)) + assert.Equal(t, "query", mc.queryContextQuery) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + + otelRows, ok := rows.(*otRows) + require.True(t, ok) + assert.Equal(t, dummySpan.SpanContext().TraceID, otelRows.span.SpanContext().TraceID) + // Span that creates in newRows() is the child of the dummySpan + assert.Equal(t, dummySpan.SpanContext().SpanID, otelRows.span.(*oteltest.Span).ParentSpanID()) + } + }) + } +} + +func TestOtConn_PrepareContext(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + stmt, err := otelConn.PrepareContext(ctx, "query") + + spanList := sr.Completed() + // One dummy span and one span created in PrepareContext + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + cfg.Attributes...)), span.Attributes()) + assert.Equal(t, string(MethodConnPrepare), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.prepareContextCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.prepareContextCtx)) + assert.Equal(t, "query", mc.prepareContextQuery) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + + otelStmt, ok := stmt.(*otStmt) + require.True(t, ok) + assert.Equal(t, "query", otelStmt.query) + } + }) + } +} + +func TestOtConn_BeginTx(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + tx, err := otelConn.BeginTx(ctx, driver.TxOptions{}) + + spanList := sr.Completed() + // One dummy span and one span created in BeginTx + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodConnBeginTx), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.beginTxCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.beginTxCtx)) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + + otelTx, ok := tx.(*otTx) + require.True(t, ok) + assert.Equal(t, dummySpan.SpanContext(), trace.SpanContextFromContext(otelTx.ctx)) + } + }) + } +} + +func TestOtConn_ResetSession(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + // New conn + cfg := newMockConfig(tracer) + mc := newMockConn(tc.error) + otelConn := newConn(mc, cfg) + + err := otelConn.ResetSession(ctx) + + spanList := sr.Completed() + // One dummy span and one span created in ResetSession + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodConnResetSession), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mc.resetSessionCount) + assert.Equal(t, span.SpanContext(), trace.SpanContextFromContext(mc.resetSessionCtx)) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} diff --git a/instrumentation/database/sql/otelsql/connector.go b/instrumentation/database/sql/otelsql/connector.go index 0a87dfb8d87..20252b98292 100644 --- a/instrumentation/database/sql/otelsql/connector.go +++ b/instrumentation/database/sql/otelsql/connector.go @@ -38,7 +38,7 @@ func (c *otConnector) Connect(ctx context.Context) (connection driver.Conn, err if err != nil { return nil, err } - return newConn(connection, c.otDriver), nil + return newConn(connection, c.otDriver.cfg), nil } func (c *otConnector) Driver() driver.Driver { diff --git a/instrumentation/database/sql/otelsql/connector_test.go b/instrumentation/database/sql/otelsql/connector_test.go new file mode 100644 index 00000000000..e167e411cf0 --- /dev/null +++ b/instrumentation/database/sql/otelsql/connector_test.go @@ -0,0 +1,101 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockConnector struct { + driver driver.Driver + + shouldError bool + connectContext context.Context + connectCount int +} + +func newMockConnector(driver driver.Driver, shouldError bool) *mockConnector { + return &mockConnector{driver: driver, shouldError: shouldError} +} + +func (m *mockConnector) Connect(ctx context.Context) (driver.Conn, error) { + m.connectContext = ctx + m.connectCount++ + if m.shouldError { + return nil, errors.New("connect") + } + return newMockConn(false), nil +} + +func (m *mockConnector) Driver() driver.Driver { + return m.driver +} + +var _ driver.Connector = (*mockConnector)(nil) + +func TestNewConnector(t *testing.T) { + mConnector := newMockConnector(nil, false) + otelDriver := &otDriver{} + + connector := newConnector(mConnector, otelDriver) + + assert.Equal(t, mConnector, connector.Connector) + assert.Equal(t, otelDriver, connector.otDriver) + +} + +func TestOtConnector_Connect(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mConnector := newMockConnector(nil, tc.error) + + connector := newConnector(mConnector, &otDriver{}) + conn, err := connector.Connect(context.Background()) + if tc.error { + assert.Error(t, err) + } else { + otelConn, ok := conn.(*otConn) + require.True(t, ok) + assert.IsType(t, &mockConn{}, otelConn.Conn) + } + }) + } +} + +func TestOtConnector_Driver(t *testing.T) { + otelDriver := &otDriver{} + connector := newConnector(nil, otelDriver) + + assert.Equal(t, otelDriver, connector.Driver()) +} diff --git a/instrumentation/database/sql/otelsql/driver.go b/instrumentation/database/sql/otelsql/driver.go index b96573c8f82..7c32255587b 100644 --- a/instrumentation/database/sql/otelsql/driver.go +++ b/instrumentation/database/sql/otelsql/driver.go @@ -41,7 +41,7 @@ func (d *otDriver) Open(name string) (driver.Conn, error) { if err != nil { return nil, err } - return newConn(rawConn, d), nil + return newConn(rawConn, d.cfg), nil } func (d *otDriver) OpenConnector(name string) (driver.Connector, error) { diff --git a/instrumentation/database/sql/otelsql/driver_test.go b/instrumentation/database/sql/otelsql/driver_test.go new file mode 100644 index 00000000000..08a603df018 --- /dev/null +++ b/instrumentation/database/sql/otelsql/driver_test.go @@ -0,0 +1,139 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockDriver struct { + shouldError bool + + openCount, openConnectorCount int + openConnectorName string + openName string +} + +func newMockDriver(shouldError bool) *mockDriver { + return &mockDriver{shouldError: shouldError} +} + +func (m *mockDriver) OpenConnector(name string) (driver.Connector, error) { + m.openConnectorName = name + m.openConnectorCount++ + if m.shouldError { + return nil, errors.New("openConnector") + } + return newMockConnector(m, false), nil +} + +func (m *mockDriver) Open(name string) (driver.Conn, error) { + m.openName = name + m.openCount++ + if m.shouldError { + return nil, errors.New("open") + } + return newMockConn(false), nil +} + +var ( + _ driver.Driver = (*mockDriver)(nil) + _ driver.DriverContext = (*mockDriver)(nil) +) + +func TestNewDriver(t *testing.T) { + d := newDriver(newMockDriver(false), config{DBSystem: "test"}) + + otelDriver, ok := d.(*otDriver) + require.True(t, ok) + assert.Equal(t, newMockDriver(false), otelDriver.driver) + assert.Equal(t, config{DBSystem: "test"}, otelDriver.cfg) +} + +func TestOtDriver_Open(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + md := newMockDriver(tc.error) + d := newDriver(md, config{}) + + conn, err := d.Open("test") + + assert.Equal(t, "test", md.openName) + assert.Equal(t, 1, md.openCount) + + if tc.error { + assert.Error(t, err) + } else { + otelConn, ok := conn.(*otConn) + require.True(t, ok) + assert.IsType(t, &mockConn{}, otelConn.Conn) + } + }) + } +} + +func TestOtDriver_OpenConnector(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + md := newMockDriver(tc.error) + d := newDriver(md, config{}) + + otelDriver := d.(*otDriver) + connector, err := otelDriver.OpenConnector("test") + + assert.Equal(t, "test", md.openConnectorName) + assert.Equal(t, 1, md.openConnectorCount) + + if tc.error { + assert.Error(t, err) + } else { + otelConnector, ok := connector.(*otConnector) + require.True(t, ok) + assert.IsType(t, &mockConnector{}, otelConnector.Connector) + } + }) + } +} diff --git a/instrumentation/database/sql/otelsql/example/go.sum b/instrumentation/database/sql/otelsql/example/go.sum index dd400554e9b..581e4840ccb 100644 --- a/instrumentation/database/sql/otelsql/example/go.sum +++ b/instrumentation/database/sql/otelsql/example/go.sum @@ -11,6 +11,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= go.opentelemetry.io/otel/exporters/stdout v0.16.0 h1:lQG6ZZYLh3NxnmrHltRmqZolT/jPJ8Qfl74lWT8g69Y= diff --git a/instrumentation/database/sql/otelsql/go.mod b/instrumentation/database/sql/otelsql/go.mod index b54536242cf..719509be768 100644 --- a/instrumentation/database/sql/otelsql/go.mod +++ b/instrumentation/database/sql/otelsql/go.mod @@ -5,6 +5,7 @@ go 1.15 replace go.opentelemetry.io/contrib => ../../../../ require ( + github.com/stretchr/testify v1.7.0 go.opentelemetry.io/contrib v0.16.0 go.opentelemetry.io/otel v0.16.0 ) diff --git a/instrumentation/database/sql/otelsql/go.sum b/instrumentation/database/sql/otelsql/go.sum index 59f82d0aa93..9f36a526a85 100644 --- a/instrumentation/database/sql/otelsql/go.sum +++ b/instrumentation/database/sql/otelsql/go.sum @@ -7,6 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/instrumentation/database/sql/otelsql/option_test.go b/instrumentation/database/sql/otelsql/option_test.go new file mode 100644 index 00000000000..b47c3c1798d --- /dev/null +++ b/instrumentation/database/sql/otelsql/option_test.go @@ -0,0 +1,71 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/oteltest" +) + +func TestOptions(t *testing.T) { + tracerProvider := oteltest.NewTracerProvider() + + testCases := []struct { + name string + option Option + expectedConfig config + }{ + { + name: "WithTracerProvider", + option: WithTracerProvider(tracerProvider), + expectedConfig: config{TracerProvider: tracerProvider}, + }, + { + name: "WithAttributes", + option: WithAttributes( + label.String("foo", "bar"), + label.String("foo2", "bar2"), + ), + expectedConfig: config{Attributes: []label.KeyValue{ + label.String("foo", "bar"), + label.String("foo2", "bar2"), + }}, + }, + { + name: "WithSpanNameFormatter", + option: WithSpanNameFormatter(&defaultSpanNameFormatter{}), + expectedConfig: config{SpanNameFormatter: &defaultSpanNameFormatter{}}, + }, + { + name: "WithSpanOptions", + option: WithSpanOptions(SpanOptions{Ping: true}), + expectedConfig: config{SpanOptions: SpanOptions{Ping: true}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var cfg config + + tc.option.Apply(&cfg) + + assert.Equal(t, tc.expectedConfig, cfg) + }) + } +} diff --git a/instrumentation/database/sql/otelsql/rows_test.go b/instrumentation/database/sql/otelsql/rows_test.go new file mode 100644 index 00000000000..2fe321e6fd8 --- /dev/null +++ b/instrumentation/database/sql/otelsql/rows_test.go @@ -0,0 +1,196 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +type mockRows struct { + shouldError bool + + closeCount, nextCount int + nextDest []driver.Value +} + +func (m *mockRows) Columns() []string { + return nil +} + +func (m *mockRows) Close() error { + m.closeCount++ + if m.shouldError { + return errors.New("close") + } + return nil +} + +func (m *mockRows) Next(dest []driver.Value) error { + m.nextDest = dest + m.nextCount++ + if m.shouldError { + return errors.New("next") + } + return nil +} + +func newMockRows(shouldError bool) *mockRows { + return &mockRows{shouldError: shouldError} +} + +var ( + _ driver.Rows = (*mockRows)(nil) +) + +func TestOtRows_Close(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + + mr := newMockRows(tc.error) + cfg := newMockConfig(tracer) + + // New rows + rows := newRows(context.Background(), mr, cfg) + // Close + err := rows.Close() + + spanList := sr.Completed() + // A span created in newRows() + require.Equal(t, 1, len(spanList)) + span := spanList[0] + assert.True(t, span.Ended()) + + assert.Equal(t, 1, mr.closeCount) + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + assert.Len(t, span.Events(), 1) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} + +func TestOtRows_Next(t *testing.T) { + testCases := []struct { + name string + error bool + rowsNextOption bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + { + name: "with RowsNextOption", + rowsNextOption: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + + mr := newMockRows(tc.error) + cfg := newMockConfig(tracer) + cfg.SpanOptions.RowsNext = tc.rowsNextOption + + // New rows + rows := newRows(context.Background(), mr, cfg) + // Next + err := rows.Next([]driver.Value{"test"}) + + spanList := sr.Started() + // A span created in newRows() + require.Equal(t, 1, len(spanList)) + span := spanList[0] + assert.False(t, span.Ended()) + + assert.Equal(t, 1, mr.nextCount) + assert.Equal(t, []driver.Value{"test"}, mr.nextDest) + var expectedEventCount int + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + expectedEventCount++ + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + + if tc.rowsNextOption { + expectedEventCount++ + } + assert.Len(t, span.Events(), expectedEventCount) + }) + } +} + +func TestNewRows(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + + mr := newMockRows(false) + cfg := newMockConfig(tracer) + + // New rows + rows := newRows(ctx, mr, cfg) + + spanList := sr.Started() + // One dummy span and one span created in newRows() + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.False(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodRows), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + assert.Equal(t, mr, rows.Rows) +} diff --git a/instrumentation/database/sql/otelsql/sql.go b/instrumentation/database/sql/otelsql/sql.go index 94f5c096867..09bf667ae94 100644 --- a/instrumentation/database/sql/otelsql/sql.go +++ b/instrumentation/database/sql/otelsql/sql.go @@ -24,6 +24,8 @@ import ( var registerLock sync.Mutex +var maxDriverSlot = 1000 + // Register initializes and registers our OTel wrapped database driver // identified by its driverName, using provided Option. // It is possible to register multiple wrappers for the same database driver if @@ -51,10 +53,10 @@ func Register(driverName string, dbSystem string, options ...Option) (string, er // configurations, but potentially the same underlying database driver, we // cycle through to find available driver names. driverName = driverName + "-otelsql-" - for i := int64(0); i < 1000; i++ { + for i := 0; i < maxDriverSlot; i++ { var ( found = false - regName = driverName + strconv.FormatInt(i, 10) + regName = driverName + strconv.FormatInt(int64(i), 10) ) for _, name := range sql.Drivers() { if name == regName { diff --git a/instrumentation/database/sql/otelsql/sql_test.go b/instrumentation/database/sql/otelsql/sql_test.go new file mode 100644 index 00000000000..652fbef640e --- /dev/null +++ b/instrumentation/database/sql/otelsql/sql_test.go @@ -0,0 +1,69 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "database/sql" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/semconv" +) + +func init() { + sql.Register("test-driver", newMockDriver(false)) + maxDriverSlot = 1 +} + +func TestRegister(t *testing.T) { + driverName, err := Register("test-driver", "test-db", + WithAttributes(label.String("foo", "bar")), + ) + require.NoError(t, err) + assert.Equal(t, "test-driver-otelsql-0", driverName) + + // Expected driver + db, err := sql.Open(driverName, "") + require.NoError(t, err) + otelDriver, ok := db.Driver().(*otDriver) + require.True(t, ok) + assert.Equal(t, &mockDriver{openConnectorCount: 2}, otelDriver.driver) + assert.ElementsMatch(t, []label.KeyValue{ + semconv.DBSystemKey.String("test-db"), + label.String("foo", "bar"), + }, otelDriver.cfg.Attributes) + + // Exceed max slot count + _, err = Register("test-driver", "test-db") + assert.Error(t, err) +} + +func TestWrapDriver(t *testing.T) { + driver := WrapDriver(newMockDriver(false), "test-db", + WithAttributes(label.String("foo", "bar")), + ) + + // Expected driver + otelDriver, ok := driver.(*otDriver) + require.True(t, ok) + assert.Equal(t, &mockDriver{}, otelDriver.driver) + assert.ElementsMatch(t, []label.KeyValue{ + semconv.DBSystemKey.String("test-db"), + label.String("foo", "bar"), + }, otelDriver.cfg.Attributes) +} diff --git a/instrumentation/database/sql/otelsql/stmt_test.go b/instrumentation/database/sql/otelsql/stmt_test.go new file mode 100644 index 00000000000..0390a57e3a9 --- /dev/null +++ b/instrumentation/database/sql/otelsql/stmt_test.go @@ -0,0 +1,184 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" +) + +type mockStmt struct { + driver.Stmt + + shouldError bool + queryCount int + execCount int + + queryContextArgs []driver.NamedValue + ExecContextArgs []driver.NamedValue +} + +func newMockStmt(shouldError bool) *mockStmt { + return &mockStmt{shouldError: shouldError} +} + +func (m *mockStmt) CheckNamedValue(value *driver.NamedValue) error { + if m.shouldError { + return errors.New("checkNamedValue") + } + return nil +} + +func (m *mockStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + m.queryContextArgs = args + m.queryCount++ + if m.shouldError { + return nil, errors.New("queryContext") + } + return nil, nil +} + +func (m *mockStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + m.ExecContextArgs = args + m.execCount++ + if m.shouldError { + return nil, errors.New("execContext") + } + return nil, nil +} + +var ( + _ driver.Stmt = (*mockStmt)(nil) + _ driver.StmtExecContext = (*mockStmt)(nil) + _ driver.StmtQueryContext = (*mockStmt)(nil) + _ driver.NamedValueChecker = (*mockStmt)(nil) +) + +func TestOtStmt_ExecContext(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + ms := newMockStmt(tc.error) + + // New stmt + cfg := newMockConfig(tracer) + stmt := newStmt(ms, cfg, "query") + // Exec + _, err := stmt.ExecContext(ctx, []driver.NamedValue{{Name: "test"}}) + + spanList := sr.Completed() + // One dummy span and a span created in tx + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + cfg.Attributes...)), span.Attributes()) + assert.Equal(t, string(MethodStmtExec), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, ms.execCount) + assert.Equal(t, []driver.NamedValue{{Name: "test"}}, ms.ExecContextArgs) + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} + +func TestOtStmt_QueryContext(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + ms := newMockStmt(tc.error) + + // New stmt + cfg := newMockConfig(tracer) + stmt := newStmt(ms, cfg, "query") + // Query + rows, err := stmt.QueryContext(ctx, []driver.NamedValue{{Name: "test"}}) + + spanList := sr.Completed() + // One dummy span and a span created in tx + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + cfg.Attributes...)), span.Attributes()) + assert.Equal(t, string(MethodStmtQuery), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, ms.queryCount) + assert.Equal(t, []driver.NamedValue{{Name: "test"}}, ms.queryContextArgs) + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + assert.IsType(t, &otRows{}, rows) + } + }) + } +} diff --git a/instrumentation/database/sql/otelsql/tx_test.go b/instrumentation/database/sql/otelsql/tx_test.go new file mode 100644 index 00000000000..91424bdef00 --- /dev/null +++ b/instrumentation/database/sql/otelsql/tx_test.go @@ -0,0 +1,162 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" +) + +type mockTx struct { + shouldError bool + + commitCount int + rollbackCount int +} + +func newMockTx(shouldError bool) *mockTx { + return &mockTx{shouldError: shouldError} +} + +func (m *mockTx) Commit() error { + m.commitCount++ + if m.shouldError { + return errors.New("commit") + } + return nil +} + +func (m *mockTx) Rollback() error { + m.rollbackCount++ + if m.shouldError { + return errors.New("rollback") + } + return nil +} + +var _ driver.Tx = (*mockTx)(nil) + +var defaultLabel = label.Key("test").String("foo") + +func TestOtTx_Commit(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + mt := newMockTx(tc.error) + + // New tx + cfg := newMockConfig(tracer) + tx := newTx(ctx, mt, cfg) + // Commit + err := tx.Commit() + + spanList := sr.Completed() + // One dummy span and one span created in tx + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodTxCommit), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + + assert.Equal(t, 1, mt.commitCount) + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} + +func TestOtTx_Rollback(t *testing.T) { + testCases := []struct { + name string + error bool + }{ + { + name: "no error", + }, + { + name: "with error", + error: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Prepare traces + sr, provider := newTracerProvider() + tracer := provider.Tracer("test") + ctx, dummySpan := createDummySpan(context.Background(), tracer) + mt := newMockTx(tc.error) + + // New tx + cfg := newMockConfig(tracer) + tx := newTx(ctx, mt, cfg) + // Rollback + err := tx.Rollback() + + spanList := sr.Completed() + // One dummy span and a span created in tx + require.Equal(t, 2, len(spanList)) + span := spanList[1] + assert.True(t, span.Ended()) + assert.Equal(t, trace.SpanKindClient, span.SpanKind()) + assert.Equal(t, attributesListToMap(cfg.Attributes), span.Attributes()) + assert.Equal(t, string(MethodTxRollback), span.Name()) + assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) + assert.Equal(t, dummySpan.SpanContext().SpanID, span.ParentSpanID()) + assert.Equal(t, 1, mt.rollbackCount) + + if tc.error { + require.Error(t, err) + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + require.NoError(t, err) + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} diff --git a/instrumentation/database/sql/otelsql/utils_test.go b/instrumentation/database/sql/otelsql/utils_test.go new file mode 100644 index 00000000000..30d0db2f3f4 --- /dev/null +++ b/instrumentation/database/sql/otelsql/utils_test.go @@ -0,0 +1,110 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otelsql + +import ( + "context" + "database/sql/driver" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/oteltest" + "go.opentelemetry.io/otel/trace" +) + +func TestRecordSpanError(t *testing.T) { + testCases := []struct { + name string + opts SpanOptions + err error + expectedError bool + }{ + { + name: "no error", + err: nil, + expectedError: false, + }, + { + name: "normal error", + err: errors.New("error"), + expectedError: true, + }, + { + name: "normal error with DisableErrSkip", + err: errors.New("error"), + opts: SpanOptions{DisableErrSkip: true}, + expectedError: true, + }, + { + name: "ErrSkip error", + err: driver.ErrSkip, + expectedError: true, + }, + { + name: "ErrSkip error with DisableErrSkip", + err: driver.ErrSkip, + opts: SpanOptions{DisableErrSkip: true}, + expectedError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var span oteltest.Span + recordSpanError(&span, tc.opts, tc.err) + + if tc.expectedError { + assert.Equal(t, codes.Error, span.StatusCode()) + } else { + assert.Equal(t, codes.Unset, span.StatusCode()) + } + }) + } +} + +func newTracerProvider() (*oteltest.StandardSpanRecorder, *oteltest.TracerProvider) { + var sr oteltest.StandardSpanRecorder + provider := oteltest.NewTracerProvider( + oteltest.WithSpanRecorder(&sr), + ) + return &sr, provider +} + +func createDummySpan(ctx context.Context, tracer trace.Tracer) (context.Context, trace.Span) { + ctx, span := tracer.Start(ctx, "dummy") + defer span.End() + return ctx, span +} + +func newMockConfig(tracer trace.Tracer) config { + return config{ + Tracer: tracer, + Attributes: []label.KeyValue{defaultLabel}, + SpanNameFormatter: &defaultSpanNameFormatter{}, + } +} + +func attributesListToMap(labels []label.KeyValue) map[label.Key]label.Value { + attributes := make(map[label.Key]label.Value) + + for _, v := range labels { + attributes[v.Key] = v.Value + } + return attributes +} From 93de4d2504150865d407f0622484ee97fc82aac6 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Sun, 21 Feb 2021 23:16:36 +0800 Subject: [PATCH 09/10] Depend on OTel 0.17.0 --- CHANGELOG.md | 5 +++- .../database/sql/otelsql/example/go.mod | 8 +++---- .../database/sql/otelsql/example/go.sum | 24 ++++++++++++------- instrumentation/database/sql/otelsql/go.mod | 6 +++-- instrumentation/database/sql/otelsql/go.sum | 12 ++++++---- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3fd7c43111..734c7c680a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Added + +- Tracing instrumentation for database/sql. (#505) + ### Fixed - `otelmemcache` no longer sets span status to OK instead of leaving it unset. (#477) @@ -21,7 +25,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add `ot-tracer` propagator (#562) -- Tracing instrumentation for database/sql. (#505) ### Changed diff --git a/instrumentation/database/sql/otelsql/example/go.mod b/instrumentation/database/sql/otelsql/example/go.mod index 8303ba11cac..9a1428e74c7 100644 --- a/instrumentation/database/sql/otelsql/example/go.mod +++ b/instrumentation/database/sql/otelsql/example/go.mod @@ -9,8 +9,8 @@ replace ( require ( github.com/go-sql-driver/mysql v1.5.0 - go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql v0.16.0 - go.opentelemetry.io/otel v0.16.0 - go.opentelemetry.io/otel/exporters/stdout v0.16.0 - go.opentelemetry.io/otel/sdk v0.16.0 + go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql v0.17.0 + go.opentelemetry.io/otel v0.17.0 + go.opentelemetry.io/otel/exporters/stdout v0.17.0 + go.opentelemetry.io/otel/sdk v0.17.0 ) diff --git a/instrumentation/database/sql/otelsql/example/go.sum b/instrumentation/database/sql/otelsql/example/go.sum index 581e4840ccb..1f181128c75 100644 --- a/instrumentation/database/sql/otelsql/example/go.sum +++ b/instrumentation/database/sql/otelsql/example/go.sum @@ -9,16 +9,24 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= -go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= -go.opentelemetry.io/otel/exporters/stdout v0.16.0 h1:lQG6ZZYLh3NxnmrHltRmqZolT/jPJ8Qfl74lWT8g69Y= -go.opentelemetry.io/otel/exporters/stdout v0.16.0/go.mod h1:bq7m22M7WIxz30KnxH9lI4RLKPajk0lnLsd5P2MsSv8= -go.opentelemetry.io/otel/sdk v0.16.0 h1:5o+fkNsOfH5Mix1bHUApNBqeDcAYczHDa7Ix+R73K2U= -go.opentelemetry.io/otel/sdk v0.16.0/go.mod h1:Jb0B4wrxerxtBeapvstmAZvJGQmvah4dHgKSngDpiCo= +go.opentelemetry.io/otel v0.17.0 h1:6MKOu8WY4hmfpQ4oQn34u6rYhnf2sWf1LXYO/UFm71U= +go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s= +go.opentelemetry.io/otel/exporters/stdout v0.17.0 h1:QfS/okW9h99eT7m20E9un/TDz+Q1woZADvAgUWR8YQI= +go.opentelemetry.io/otel/exporters/stdout v0.17.0/go.mod h1:NJ6kp8glOLKmXyjTM3I/ChQwUcE6rSdWd8AqGO/Av/w= +go.opentelemetry.io/otel/metric v0.17.0 h1:t+5EioN8YFXQ2EH+1j6FHCKMUj+57zIDSnSGr/mWuug= +go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0= +go.opentelemetry.io/otel/oteltest v0.17.0 h1:TyAihUowTDLqb4+m5ePAsR71xPJaTBJl4KDArIdi9k4= +go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE= +go.opentelemetry.io/otel/sdk v0.17.0 h1:eHXQwanmbtSHM/GcJYbJ8FyyH/sT9a0e+1Z9ZWkF7Ug= +go.opentelemetry.io/otel/sdk v0.17.0/go.mod h1:INs1PePjjF2hf842AXsxGTe5lH023QfLTZRFPiV/RUk= +go.opentelemetry.io/otel/sdk/export/metric v0.17.0 h1:RKOa26LDq4JBRwUnWwY64ccc27v1rA20z0q71aq4WFs= +go.opentelemetry.io/otel/sdk/export/metric v0.17.0/go.mod h1:G9SxRFvGmGpdmJ8TEXnTEnnRuR5p3cg/tRvWkA/XHvo= +go.opentelemetry.io/otel/sdk/metric v0.17.0 h1:l9W/OcHwyq3ZPqk4V6OS5ED50z9A6yI8N9gWeKS7zAY= +go.opentelemetry.io/otel/sdk/metric v0.17.0/go.mod h1:zAX55SrmDMpZwfQrz1PKIPbCP5beU+JPQTfNko01deo= +go.opentelemetry.io/otel/trace v0.17.0 h1:SBOj64/GAOyWzs5F680yW1ITIfJkm6cJWL2YAvuL9xY= +go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/instrumentation/database/sql/otelsql/go.mod b/instrumentation/database/sql/otelsql/go.mod index 719509be768..fc3e0fbbb9e 100644 --- a/instrumentation/database/sql/otelsql/go.mod +++ b/instrumentation/database/sql/otelsql/go.mod @@ -6,6 +6,8 @@ replace go.opentelemetry.io/contrib => ../../../../ require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/contrib v0.16.0 - go.opentelemetry.io/otel v0.16.0 + go.opentelemetry.io/contrib v0.17.0 + go.opentelemetry.io/otel v0.17.0 + go.opentelemetry.io/otel/oteltest v0.17.0 + go.opentelemetry.io/otel/trace v0.17.0 ) diff --git a/instrumentation/database/sql/otelsql/go.sum b/instrumentation/database/sql/otelsql/go.sum index 9f36a526a85..128831ced02 100644 --- a/instrumentation/database/sql/otelsql/go.sum +++ b/instrumentation/database/sql/otelsql/go.sum @@ -5,12 +5,16 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= -go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= +go.opentelemetry.io/otel v0.17.0 h1:6MKOu8WY4hmfpQ4oQn34u6rYhnf2sWf1LXYO/UFm71U= +go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s= +go.opentelemetry.io/otel/metric v0.17.0 h1:t+5EioN8YFXQ2EH+1j6FHCKMUj+57zIDSnSGr/mWuug= +go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0= +go.opentelemetry.io/otel/oteltest v0.17.0 h1:TyAihUowTDLqb4+m5ePAsR71xPJaTBJl4KDArIdi9k4= +go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE= +go.opentelemetry.io/otel/trace v0.17.0 h1:SBOj64/GAOyWzs5F680yW1ITIfJkm6cJWL2YAvuL9xY= +go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 5f4a773f97404f75e331b0b3b57158c32aca30e5 Mon Sep 17 00:00:00 2001 From: Sam Xie Date: Fri, 5 Mar 2021 11:20:32 +0800 Subject: [PATCH 10/10] Depend on OTel 0.18.0 --- .../database/sql/otelsql/config.go | 4 +-- .../database/sql/otelsql/config_test.go | 4 +-- .../database/sql/otelsql/conn_test.go | 8 ++--- .../database/sql/otelsql/example/go.mod | 8 ++--- .../database/sql/otelsql/example/go.sum | 32 +++++++++---------- instrumentation/database/sql/otelsql/go.mod | 8 ++--- instrumentation/database/sql/otelsql/go.sum | 16 +++++----- .../database/sql/otelsql/option.go | 4 +-- .../database/sql/otelsql/option_test.go | 12 +++---- .../database/sql/otelsql/sql_test.go | 14 ++++---- .../database/sql/otelsql/stmt_test.go | 6 ++-- .../database/sql/otelsql/tx_test.go | 4 +-- .../database/sql/otelsql/utils_test.go | 18 +++++------ 13 files changed, 69 insertions(+), 69 deletions(-) diff --git a/instrumentation/database/sql/otelsql/config.go b/instrumentation/database/sql/otelsql/config.go index ccb66b976dd..8a90e096871 100644 --- a/instrumentation/database/sql/otelsql/config.go +++ b/instrumentation/database/sql/otelsql/config.go @@ -18,7 +18,7 @@ import ( "context" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/semconv" "go.opentelemetry.io/otel/trace" @@ -43,7 +43,7 @@ type config struct { DBSystem string // Attributes will be set to each span. - Attributes []label.KeyValue + Attributes []attribute.KeyValue // SpanNameFormatter will be called to produce span's name. // Default use method as span name diff --git a/instrumentation/database/sql/otelsql/config_test.go b/instrumentation/database/sql/otelsql/config_test.go index 1224f247078..402736d6af7 100644 --- a/instrumentation/database/sql/otelsql/config_test.go +++ b/instrumentation/database/sql/otelsql/config_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/semconv" "go.opentelemetry.io/otel/trace" @@ -37,7 +37,7 @@ func TestNewConfig(t *testing.T) { ), SpanOptions: SpanOptions{Ping: true}, DBSystem: "db", - Attributes: []label.KeyValue{ + Attributes: []attribute.KeyValue{ semconv.DBSystemKey.String(cfg.DBSystem), }, SpanNameFormatter: &defaultSpanNameFormatter{}, diff --git a/instrumentation/database/sql/otelsql/conn_test.go b/instrumentation/database/sql/otelsql/conn_test.go index 6ee39ac1829..1e66bdafaea 100644 --- a/instrumentation/database/sql/otelsql/conn_test.go +++ b/instrumentation/database/sql/otelsql/conn_test.go @@ -23,8 +23,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/oteltest" "go.opentelemetry.io/otel/semconv" "go.opentelemetry.io/otel/trace" @@ -236,7 +236,7 @@ func TestOtConn_ExecContext(t *testing.T) { span := spanList[1] assert.True(t, span.Ended()) assert.Equal(t, trace.SpanKindClient, span.SpanKind()) - assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + assert.Equal(t, attributesListToMap(append([]attribute.KeyValue{semconv.DBStatementKey.String("query")}, cfg.Attributes...)), span.Attributes()) assert.Equal(t, string(MethodConnExec), span.Name()) assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) @@ -291,7 +291,7 @@ func TestOtConn_QueryContext(t *testing.T) { span := spanList[1] assert.True(t, span.Ended()) assert.Equal(t, trace.SpanKindClient, span.SpanKind()) - assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + assert.Equal(t, attributesListToMap(append([]attribute.KeyValue{semconv.DBStatementKey.String("query")}, cfg.Attributes...)), span.Attributes()) assert.Equal(t, string(MethodConnQuery), span.Name()) assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) @@ -352,7 +352,7 @@ func TestOtConn_PrepareContext(t *testing.T) { span := spanList[1] assert.True(t, span.Ended()) assert.Equal(t, trace.SpanKindClient, span.SpanKind()) - assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + assert.Equal(t, attributesListToMap(append([]attribute.KeyValue{semconv.DBStatementKey.String("query")}, cfg.Attributes...)), span.Attributes()) assert.Equal(t, string(MethodConnPrepare), span.Name()) assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) diff --git a/instrumentation/database/sql/otelsql/example/go.mod b/instrumentation/database/sql/otelsql/example/go.mod index 9a1428e74c7..dc0647cb2d5 100644 --- a/instrumentation/database/sql/otelsql/example/go.mod +++ b/instrumentation/database/sql/otelsql/example/go.mod @@ -9,8 +9,8 @@ replace ( require ( github.com/go-sql-driver/mysql v1.5.0 - go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql v0.17.0 - go.opentelemetry.io/otel v0.17.0 - go.opentelemetry.io/otel/exporters/stdout v0.17.0 - go.opentelemetry.io/otel/sdk v0.17.0 + go.opentelemetry.io/contrib/instrumentation/database/sql/otelsql v0.18.0 + go.opentelemetry.io/otel v0.18.0 + go.opentelemetry.io/otel/exporters/stdout v0.18.0 + go.opentelemetry.io/otel/sdk v0.18.0 ) diff --git a/instrumentation/database/sql/otelsql/example/go.sum b/instrumentation/database/sql/otelsql/example/go.sum index 1f181128c75..35db35c5a8f 100644 --- a/instrumentation/database/sql/otelsql/example/go.sum +++ b/instrumentation/database/sql/otelsql/example/go.sum @@ -11,22 +11,22 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.17.0 h1:6MKOu8WY4hmfpQ4oQn34u6rYhnf2sWf1LXYO/UFm71U= -go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s= -go.opentelemetry.io/otel/exporters/stdout v0.17.0 h1:QfS/okW9h99eT7m20E9un/TDz+Q1woZADvAgUWR8YQI= -go.opentelemetry.io/otel/exporters/stdout v0.17.0/go.mod h1:NJ6kp8glOLKmXyjTM3I/ChQwUcE6rSdWd8AqGO/Av/w= -go.opentelemetry.io/otel/metric v0.17.0 h1:t+5EioN8YFXQ2EH+1j6FHCKMUj+57zIDSnSGr/mWuug= -go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0= -go.opentelemetry.io/otel/oteltest v0.17.0 h1:TyAihUowTDLqb4+m5ePAsR71xPJaTBJl4KDArIdi9k4= -go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE= -go.opentelemetry.io/otel/sdk v0.17.0 h1:eHXQwanmbtSHM/GcJYbJ8FyyH/sT9a0e+1Z9ZWkF7Ug= -go.opentelemetry.io/otel/sdk v0.17.0/go.mod h1:INs1PePjjF2hf842AXsxGTe5lH023QfLTZRFPiV/RUk= -go.opentelemetry.io/otel/sdk/export/metric v0.17.0 h1:RKOa26LDq4JBRwUnWwY64ccc27v1rA20z0q71aq4WFs= -go.opentelemetry.io/otel/sdk/export/metric v0.17.0/go.mod h1:G9SxRFvGmGpdmJ8TEXnTEnnRuR5p3cg/tRvWkA/XHvo= -go.opentelemetry.io/otel/sdk/metric v0.17.0 h1:l9W/OcHwyq3ZPqk4V6OS5ED50z9A6yI8N9gWeKS7zAY= -go.opentelemetry.io/otel/sdk/metric v0.17.0/go.mod h1:zAX55SrmDMpZwfQrz1PKIPbCP5beU+JPQTfNko01deo= -go.opentelemetry.io/otel/trace v0.17.0 h1:SBOj64/GAOyWzs5F680yW1ITIfJkm6cJWL2YAvuL9xY= -go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg= +go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8= +go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= +go.opentelemetry.io/otel/exporters/stdout v0.18.0 h1:DnB3C9IdAa3/6LqbpBYmO2QqljsBj3Mr2oSpIMnXbCc= +go.opentelemetry.io/otel/exporters/stdout v0.18.0/go.mod h1:c4vRVKdmtlGOnPriMiPhLzVzdMzH/RlM2NJioEhm+so= +go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY= +go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= +go.opentelemetry.io/otel/oteltest v0.18.0 h1:FbKDFm/LnQDOHuGjED+fy3s5YMVg0z019GJ9Er66hYo= +go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= +go.opentelemetry.io/otel/sdk v0.18.0 h1:/UiFHiJxJyEoUN2tQ6l+5f0/P01V0G9YuHeVarktRDw= +go.opentelemetry.io/otel/sdk v0.18.0/go.mod h1:nT+UdAeGQWSeTnz9vY8BBq7SEGpmWAetyo/xHUcQvxo= +go.opentelemetry.io/otel/sdk/export/metric v0.18.0 h1:0CP4KxCGeaVO2l69NNzRCULaaGiW6UGPDSF/b6gRqDs= +go.opentelemetry.io/otel/sdk/export/metric v0.18.0/go.mod h1:CFUAd+HdaQT3efTnVFYaXXp56b6bFUqkck4iRB9wu0g= +go.opentelemetry.io/otel/sdk/metric v0.18.0 h1:16ryqzWeYMl6uzwz7or3IQlCDf366Ppfm50215Mte5I= +go.opentelemetry.io/otel/sdk/metric v0.18.0/go.mod h1:NY9c56grMpjqdaYvOFon8nnsgMPBaXpde5SO1ulDyCo= +go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk= +go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/instrumentation/database/sql/otelsql/go.mod b/instrumentation/database/sql/otelsql/go.mod index fc3e0fbbb9e..df7c1b2b350 100644 --- a/instrumentation/database/sql/otelsql/go.mod +++ b/instrumentation/database/sql/otelsql/go.mod @@ -6,8 +6,8 @@ replace go.opentelemetry.io/contrib => ../../../../ require ( github.com/stretchr/testify v1.7.0 - go.opentelemetry.io/contrib v0.17.0 - go.opentelemetry.io/otel v0.17.0 - go.opentelemetry.io/otel/oteltest v0.17.0 - go.opentelemetry.io/otel/trace v0.17.0 + go.opentelemetry.io/contrib v0.18.0 + go.opentelemetry.io/otel v0.18.0 + go.opentelemetry.io/otel/oteltest v0.18.0 + go.opentelemetry.io/otel/trace v0.18.0 ) diff --git a/instrumentation/database/sql/otelsql/go.sum b/instrumentation/database/sql/otelsql/go.sum index 128831ced02..0b94559400b 100644 --- a/instrumentation/database/sql/otelsql/go.sum +++ b/instrumentation/database/sql/otelsql/go.sum @@ -7,14 +7,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.17.0 h1:6MKOu8WY4hmfpQ4oQn34u6rYhnf2sWf1LXYO/UFm71U= -go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s= -go.opentelemetry.io/otel/metric v0.17.0 h1:t+5EioN8YFXQ2EH+1j6FHCKMUj+57zIDSnSGr/mWuug= -go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0= -go.opentelemetry.io/otel/oteltest v0.17.0 h1:TyAihUowTDLqb4+m5ePAsR71xPJaTBJl4KDArIdi9k4= -go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE= -go.opentelemetry.io/otel/trace v0.17.0 h1:SBOj64/GAOyWzs5F680yW1ITIfJkm6cJWL2YAvuL9xY= -go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg= +go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8= +go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= +go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY= +go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= +go.opentelemetry.io/otel/oteltest v0.18.0 h1:FbKDFm/LnQDOHuGjED+fy3s5YMVg0z019GJ9Er66hYo= +go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= +go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk= +go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/instrumentation/database/sql/otelsql/option.go b/instrumentation/database/sql/otelsql/option.go index 91ca1facd5f..ebcd4ead56d 100644 --- a/instrumentation/database/sql/otelsql/option.go +++ b/instrumentation/database/sql/otelsql/option.go @@ -15,7 +15,7 @@ package otelsql import ( - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -43,7 +43,7 @@ func WithTracerProvider(provider trace.TracerProvider) Option { } // WithAttributes specifies attributes that will be set to each span. -func WithAttributes(attributes ...label.KeyValue) Option { +func WithAttributes(attributes ...attribute.KeyValue) Option { return OptionFunc(func(cfg *config) { cfg.Attributes = attributes }) diff --git a/instrumentation/database/sql/otelsql/option_test.go b/instrumentation/database/sql/otelsql/option_test.go index b47c3c1798d..876c5cd1424 100644 --- a/instrumentation/database/sql/otelsql/option_test.go +++ b/instrumentation/database/sql/otelsql/option_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/assert" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/oteltest" ) @@ -39,12 +39,12 @@ func TestOptions(t *testing.T) { { name: "WithAttributes", option: WithAttributes( - label.String("foo", "bar"), - label.String("foo2", "bar2"), + attribute.String("foo", "bar"), + attribute.String("foo2", "bar2"), ), - expectedConfig: config{Attributes: []label.KeyValue{ - label.String("foo", "bar"), - label.String("foo2", "bar2"), + expectedConfig: config{Attributes: []attribute.KeyValue{ + attribute.String("foo", "bar"), + attribute.String("foo2", "bar2"), }}, }, { diff --git a/instrumentation/database/sql/otelsql/sql_test.go b/instrumentation/database/sql/otelsql/sql_test.go index 652fbef640e..680946cb1c0 100644 --- a/instrumentation/database/sql/otelsql/sql_test.go +++ b/instrumentation/database/sql/otelsql/sql_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/semconv" ) @@ -32,7 +32,7 @@ func init() { func TestRegister(t *testing.T) { driverName, err := Register("test-driver", "test-db", - WithAttributes(label.String("foo", "bar")), + WithAttributes(attribute.String("foo", "bar")), ) require.NoError(t, err) assert.Equal(t, "test-driver-otelsql-0", driverName) @@ -43,9 +43,9 @@ func TestRegister(t *testing.T) { otelDriver, ok := db.Driver().(*otDriver) require.True(t, ok) assert.Equal(t, &mockDriver{openConnectorCount: 2}, otelDriver.driver) - assert.ElementsMatch(t, []label.KeyValue{ + assert.ElementsMatch(t, []attribute.KeyValue{ semconv.DBSystemKey.String("test-db"), - label.String("foo", "bar"), + attribute.String("foo", "bar"), }, otelDriver.cfg.Attributes) // Exceed max slot count @@ -55,15 +55,15 @@ func TestRegister(t *testing.T) { func TestWrapDriver(t *testing.T) { driver := WrapDriver(newMockDriver(false), "test-db", - WithAttributes(label.String("foo", "bar")), + WithAttributes(attribute.String("foo", "bar")), ) // Expected driver otelDriver, ok := driver.(*otDriver) require.True(t, ok) assert.Equal(t, &mockDriver{}, otelDriver.driver) - assert.ElementsMatch(t, []label.KeyValue{ + assert.ElementsMatch(t, []attribute.KeyValue{ semconv.DBSystemKey.String("test-db"), - label.String("foo", "bar"), + attribute.String("foo", "bar"), }, otelDriver.cfg.Attributes) } diff --git a/instrumentation/database/sql/otelsql/stmt_test.go b/instrumentation/database/sql/otelsql/stmt_test.go index 0390a57e3a9..bf2ad021105 100644 --- a/instrumentation/database/sql/otelsql/stmt_test.go +++ b/instrumentation/database/sql/otelsql/stmt_test.go @@ -23,8 +23,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/semconv" "go.opentelemetry.io/otel/trace" ) @@ -110,7 +110,7 @@ func TestOtStmt_ExecContext(t *testing.T) { span := spanList[1] assert.True(t, span.Ended()) assert.Equal(t, trace.SpanKindClient, span.SpanKind()) - assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + assert.Equal(t, attributesListToMap(append([]attribute.KeyValue{semconv.DBStatementKey.String("query")}, cfg.Attributes...)), span.Attributes()) assert.Equal(t, string(MethodStmtExec), span.Name()) assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) @@ -163,7 +163,7 @@ func TestOtStmt_QueryContext(t *testing.T) { span := spanList[1] assert.True(t, span.Ended()) assert.Equal(t, trace.SpanKindClient, span.SpanKind()) - assert.Equal(t, attributesListToMap(append([]label.KeyValue{semconv.DBStatementKey.String("query")}, + assert.Equal(t, attributesListToMap(append([]attribute.KeyValue{semconv.DBStatementKey.String("query")}, cfg.Attributes...)), span.Attributes()) assert.Equal(t, string(MethodStmtQuery), span.Name()) assert.Equal(t, dummySpan.SpanContext().TraceID, span.SpanContext().TraceID) diff --git a/instrumentation/database/sql/otelsql/tx_test.go b/instrumentation/database/sql/otelsql/tx_test.go index 91424bdef00..d5d94cfc2e4 100644 --- a/instrumentation/database/sql/otelsql/tx_test.go +++ b/instrumentation/database/sql/otelsql/tx_test.go @@ -23,8 +23,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -57,7 +57,7 @@ func (m *mockTx) Rollback() error { var _ driver.Tx = (*mockTx)(nil) -var defaultLabel = label.Key("test").String("foo") +var defaultattribute = attribute.Key("test").String("foo") func TestOtTx_Commit(t *testing.T) { testCases := []struct { diff --git a/instrumentation/database/sql/otelsql/utils_test.go b/instrumentation/database/sql/otelsql/utils_test.go index 30d0db2f3f4..1665ab430fd 100644 --- a/instrumentation/database/sql/otelsql/utils_test.go +++ b/instrumentation/database/sql/otelsql/utils_test.go @@ -22,8 +22,8 @@ import ( "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/oteltest" "go.opentelemetry.io/otel/trace" ) @@ -78,8 +78,8 @@ func TestRecordSpanError(t *testing.T) { } } -func newTracerProvider() (*oteltest.StandardSpanRecorder, *oteltest.TracerProvider) { - var sr oteltest.StandardSpanRecorder +func newTracerProvider() (*oteltest.SpanRecorder, *oteltest.TracerProvider) { + var sr oteltest.SpanRecorder provider := oteltest.NewTracerProvider( oteltest.WithSpanRecorder(&sr), ) @@ -95,16 +95,16 @@ func createDummySpan(ctx context.Context, tracer trace.Tracer) (context.Context, func newMockConfig(tracer trace.Tracer) config { return config{ Tracer: tracer, - Attributes: []label.KeyValue{defaultLabel}, + Attributes: []attribute.KeyValue{defaultattribute}, SpanNameFormatter: &defaultSpanNameFormatter{}, } } -func attributesListToMap(labels []label.KeyValue) map[label.Key]label.Value { - attributes := make(map[label.Key]label.Value) +func attributesListToMap(attributes []attribute.KeyValue) map[attribute.Key]attribute.Value { + attributesMap := make(map[attribute.Key]attribute.Value) - for _, v := range labels { - attributes[v.Key] = v.Value + for _, v := range attributes { + attributesMap[v.Key] = v.Value } - return attributes + return attributesMap }