Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove superfluous otelmongodb attribute names. Conform to specification. #769

Merged
merged 8 commits into from
Aug 11, 2021
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Changed

- `otelmongodb` span attributes, name and span status now conform to specification. (#769)

## [0.22.0] - 2021-07-26

### Added
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ package otelmongo
import (
"context"
"fmt"
"strconv"
"strings"
"sync"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.opentelemetry.io/otel/trace"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/event"
)

Expand All @@ -39,24 +43,31 @@ type monitor struct {
}

func (m *monitor) Started(ctx context.Context, evt *event.CommandStartedEvent) {
var spanName string

hostname, port := peerInfo(evt)

attrs := []attribute.KeyValue{
DBOperation(evt.CommandName),
DBInstance(evt.DatabaseName),
DBSystem("mongodb"),
PeerHostname(hostname),
PeerPort(port),
semconv.DBSystemMongoDB,
semconv.DBOperationKey.String(evt.CommandName),
semconv.DBNameKey.String(evt.DatabaseName),
semconv.NetPeerNameKey.String(hostname),
semconv.NetPeerPortKey.Int(port),
semconv.NetTransportTCP,
}
if !m.cfg.CommandAttributeDisabled {
b, _ := bson.MarshalExtJSON(evt.Command, false, false)
attrs = append(attrs, DBStatement(string(b)))
attrs = append(attrs, semconv.DBStatementKey.String(sanitizeCommand(evt.Command)))
}

if collection, err := extractCollection(evt); err == nil && collection != "" {
spanName = collection + "."
attrs = append(attrs, semconv.DBMongoDBCollectionKey.String(collection))
}
spanName += evt.CommandName
opts := []trace.SpanStartOption{
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attrs...),
}
_, span := m.cfg.Tracer.Start(ctx, "mongodb.query", opts...)
_, span := m.cfg.Tracer.Start(ctx, spanName, opts...)
key := spanKey{
ConnectionID: evt.ConnectionID,
RequestID: evt.RequestID,
Expand Down Expand Up @@ -90,13 +101,38 @@ func (m *monitor) Finished(evt *event.CommandFinishedEvent, err error) {
}

if err != nil {
span.SetAttributes(Error(true))
span.SetAttributes(ErrorMsg(err.Error()))
span.SetStatus(codes.Error, err.Error())
}

span.End()
}

// TODO sanitize values where possible
// TODO limit maximum size
func sanitizeCommand(command bson.Raw) string {
b, _ := bson.MarshalExtJSON(command, false, false)
return string(b)
}

// extractCollection extracts the collection for the given mongodb command event.
// For CRUD operations, this is the first key/value string pair in the bson
// document where key == "<operation>" (e.g. key == "insert").
// For database meta-level operations, such a key may not exist.
func extractCollection(evt *event.CommandStartedEvent) (string, error) {
elt, err := evt.Command.IndexErr(0)
if err != nil {
return "", err
}
if key, err := elt.KeyErr(); err == nil && key == evt.CommandName {
var v bson.RawValue
if v, err = elt.ValueErr(); err != nil || v.Type != bsontype.String {
return "", err
}
return v.StringValue(), nil
}
return "", fmt.Errorf("collection name not found")
}

// NewMonitor creates a new mongodb event CommandMonitor.
func NewMonitor(opts ...Option) *event.CommandMonitor {
cfg := newConfig(opts...)
Expand All @@ -111,14 +147,14 @@ func NewMonitor(opts ...Option) *event.CommandMonitor {
}
}

func peerInfo(evt *event.CommandStartedEvent) (hostname, port string) {
func peerInfo(evt *event.CommandStartedEvent) (hostname string, port int) {
hostname = evt.ConnectionID
port = "27017"
port = 27017
if idx := strings.IndexByte(hostname, '['); idx >= 0 {
hostname = hostname[:idx]
}
if idx := strings.IndexByte(hostname, ':'); idx >= 0 {
port = hostname[idx+1:]
port = func(p int, e error) int { return p }(strconv.Atoi(hostname[idx+1:]))
hostname = hostname[:idx]
}
return hostname, port
Expand Down
Loading