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

Adding support for opentelemetry #207

Open
superbeeny opened this issue Mar 26, 2021 · 6 comments
Open

Adding support for opentelemetry #207

superbeeny opened this issue Mar 26, 2021 · 6 comments
Labels
enhancement New feature or request

Comments

@superbeeny
Copy link

Are there any plans to add support for opentracing to the driver along the lines of the java version - https://github.com/opentracing-contrib/java-neo4j-driver?

TIA

@fbiville
Copy link
Contributor

fbiville commented Apr 2, 2021

Hi, the Java version you are referring to is not officially maintained by the team.
There is currently no plan I know of to support this.

Could you elaborate your use case/needs for this?

@superbeeny
Copy link
Author

We use opentracing for distributed tracing through our platforms, it would be great to trace all the way to neo4j and capture that last hop

@erictg
Copy link
Contributor

erictg commented May 17, 2021

@superbeeny i'm adding open tracing support in gogm (Go Object Graph Mapper) with the next release.

@fbiville
Copy link
Contributor

Now that #72 is implemented and https://github.com/neo4j/neo4j-go-driver/releases/tag/v5.0.0-preview is out, am I right in thinking this should make the opentracing integration easier?

@fbiville fbiville changed the title [Feature Request] Adding support for opentracing [Feature Request] Adding support for opentelemetry Apr 8, 2022
@fbiville
Copy link
Contributor

fbiville commented Apr 8, 2022

Updated the issue since opentracing is now superseded by https://opentelemetry.io/

@fbiville fbiville added the enhancement New feature or request label Dec 21, 2022
@fbiville fbiville changed the title [Feature Request] Adding support for opentelemetry Adding support for opentelemetry Dec 21, 2022
@nelzkiddom
Copy link

For folks who are interested, here's a fairly quick & dirty adapter that I threw together, which seems to be mostly giving us what we want right now:

// Package neotrace provides an adapter to emit neo4j Bolt logs as OTel trace events
package neotrace

import (
	"context"
	"fmt"
	"strings"

	"github.com/neo4j/neo4j-go-driver/v4/neo4j/log"
	"go.opentelemetry.io/otel/attribute"
	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
	apitrace "go.opentelemetry.io/otel/trace"
)

const tracerName = "bolt-logger"

// New returns a BoltLogger adapter
func New(
	c context.Context,
	tp apitrace.TracerProvider, // todo: consider funtional option pattern
) *Logger {
	return &Logger{ctx: c, provider: tp}
}

// Logger is a type that adapts to the neo4j/log.BoltLogger interface
type Logger struct {
	// usually, it is considered bad form to wrap a context.Context in a
	// type that gets handed around, but this is what is necessary to
	// do the tracing around a given Neo4J Session
	ctx context.Context

	provider apitrace.TracerProvider
}

// Compile time check that Logger implements the BoltLogger interface
var _ log.BoltLogger = (*Logger)(nil)

// LogServerMessage conforms to the to the neo4j/log.BoltLogger interface
func (l *Logger) LogServerMessage(context string, msg string, args ...interface{}) {
	// too chatty for right now
}

// LogClientMessage conforms to the to the neo4j/log.BoltLogger interface
func (l *Logger) LogClientMessage(context string, msg string, args ...interface{}) {
	// figure out how we want to process a given message based on the first
	// token being treated like a "command"
	fn := defaultFn
	key := strings.Split(msg, " ")[0]
	if overrideFn, ok := processors[key]; ok {
		fn = overrideFn
	}

	// process the message
	attbs, err := fn(msg, args...)
	if err != nil {
		// error is signal that we don't want to log this one
		return
	}

	// add some of the default information we want on everything
	// coming from this package
	attbs = append(
		attbs,
		[]attribute.KeyValue{
			attribute.String("bolt.context", context),
			semconv.DBSystemNeo4j,
		}...,
	)

	// do this as a whole span, rather than an event, so they are
	// visible in Honeycomb as peers along with other DB tracing implementations
	_, span := l.provider.Tracer(tracerName).Start(
		l.ctx, msg, apitrace.WithAttributes(attbs...),
	)
	defer span.End()
}

// for log messages that we find unneccesary or unhelpful, we can squelch them
var skipFn = func(msg string, args ...interface{}) ([]attribute.KeyValue, error) {
	return nil, fmt.Errorf("skip")
}

// for unknown or unexpected messages, let's capture them for now until we decide
// we want to do something else with them
var defaultFn = func(msg string, args ...interface{}) ([]attribute.KeyValue, error) {
	return []attribute.KeyValue{
		attribute.String("bolt.msg", fmt.Sprintf(msg, args...)),
	}, nil
}

// for these known messages, here is how we want to handle them
var processors = map[string]func(string, ...interface{}) ([]attribute.KeyValue, error){
	"<HANDSHAKE>": skipFn,
	"<MAGIC>":     skipFn,
	"BEGIN":       skipFn,
	"HELLO":       skipFn,
	"PULL":        skipFn,
	"ROUTE":       skipFn,
	"RUN": func(msg string, args ...interface{}) ([]attribute.KeyValue, error) {
		// ARGS: for `RUN %q %s %s`
		//  - 0 - cypher; we wanna log this as `db.statement`
		//  - 1 - parameters; unsanitized so we do not want to log this
		//  - 2 - unknown; log it for now
		cypher := ""
		if len(args) >= 1 {
			cypher = fmt.Sprint(args[0])
		}
		unknown := ""
		if len(args) >= 3 {
			unknown = fmt.Sprint(args[2])
		}

		return []attribute.KeyValue{
			attribute.String("bolt.msg", msg),
			attribute.String("bolt.arg2", unknown),
			semconv.DBStatementKey.String(cypher),
		}, nil
	},
}

And we just inject the BoltLogger every time we create a Session:

session := n.driver.NewSession(
		neo4j.SessionConfig{
			AccessMode: neo4j.AccessModeWrite,
			BoltLogger: neotrace.New(ctx, otel.GetTracerProvider()),
		},
	)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants