plugin/etcdv3: Add etcd v3 plugin (#1702)
* Update dependencies and add etcdv3 client * Update etcd plugin to support etcd v3 clients Fixes #341
This commit is contained in:
parent
f3afd70021
commit
6fe27d99be
10327 changed files with 4196998 additions and 82 deletions
|
@ -15,10 +15,10 @@ git:
|
|||
depth: 3
|
||||
|
||||
env:
|
||||
- TEST_TYPE=coverage ETCD_VERSION=2.3.1
|
||||
- TEST_TYPE=integration ETCD_VERSION=2.3.1
|
||||
- TEST_TYPE=core ETCD_VERSION=2.3.1
|
||||
- TEST_TYPE=plugin ETCD_VERSION=2.3.1
|
||||
- TEST_TYPE=coverage ETCD_VERSION=3.3.8
|
||||
- TEST_TYPE=integration ETCD_VERSION=3.3.8
|
||||
- TEST_TYPE=core ETCD_VERSION=3.3.8
|
||||
- TEST_TYPE=plugin ETCD_VERSION=3.3.8
|
||||
|
||||
# In the Travis VM-based build environment, IPv6 networking is not
|
||||
# enabled by default. The sysctl operations below enable IPv6.
|
||||
|
|
10
Gopkg.lock
generated
10
Gopkg.lock
generated
|
@ -60,7 +60,12 @@
|
|||
[[projects]]
|
||||
name = "github.com/coreos/etcd"
|
||||
packages = [
|
||||
"auth/authpb",
|
||||
"client",
|
||||
"clientv3",
|
||||
"etcdserver/api/v3rpc/rpctypes",
|
||||
"etcdserver/etcdserverpb",
|
||||
"mvcc/mvccpb",
|
||||
"pkg/pathutil",
|
||||
"pkg/srv",
|
||||
"pkg/types",
|
||||
|
@ -132,7 +137,9 @@
|
|||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"gogoproto",
|
||||
"proto",
|
||||
"protoc-gen-gogo/descriptor",
|
||||
"sortkeys"
|
||||
]
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
|
@ -391,6 +398,7 @@
|
|||
"encoding/proto",
|
||||
"grpclb/grpc_lb_v1/messages",
|
||||
"grpclog",
|
||||
"health/grpc_health_v1",
|
||||
"internal",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
|
@ -560,6 +568,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "dbbdcbcd4c0e11f040230e43a145f113ed7e67ff2c52b2a5830e117c16a23630"
|
||||
inputs-digest = "435926fcc83a4f1a93fd257248f2b1256eaa5a212159b07743408b8cafdbffff"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -26,3 +26,7 @@ ignored = [
|
|||
[[override]]
|
||||
name = "github.com/ugorji/go"
|
||||
revision = "f3cacc17c85ecb7f1b6a9e373ee85d1480919868"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/etcd"
|
||||
version = "3.3.5"
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
## Name
|
||||
|
||||
*etcd* - enables reading zone data from an etcd instance.
|
||||
*etcd* - enables reading zone data from an etcd version 3 instance.
|
||||
|
||||
## Description
|
||||
|
||||
The data in etcd has to be encoded as
|
||||
The data in etcd instance has to be encoded as
|
||||
a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26)
|
||||
like [SkyDNS](https://github.com/skynetservices/skydns). It should also work just like SkyDNS.
|
||||
|
||||
|
@ -21,7 +21,7 @@ etcd [ZONES...]
|
|||
|
||||
* **ZONES** zones etcd should be authoritative for.
|
||||
|
||||
The path will default to `/skydns` the local etcd proxy (http://localhost:2379). If no zones are
|
||||
The path will default to `/skydns` the local etcd3 proxy (http://localhost:2379). If no zones are
|
||||
specified the block's zone will be used as the zone.
|
||||
|
||||
If you want to `round robin` A and AAAA responses look at the `loadbalance` plugin.
|
||||
|
@ -169,7 +169,3 @@ dig +short skydns.local AAAA @localhost
|
|||
2003::8:1
|
||||
2003::8:2
|
||||
~~~
|
||||
|
||||
## Bugs
|
||||
|
||||
Only the etcdv2 protocol is supported.
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Package etcd provides the etcd backend plugin.
|
||||
// Package etcd provides the etcd version 3 backend plugin.
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -15,10 +16,19 @@ import (
|
|||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
etcdc "github.com/coreos/etcd/client"
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/mvcc/mvccpb"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
priority = 10 // default priority when nothing is set
|
||||
ttl = 300 // default ttl when nothing is set
|
||||
etcdTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var errKeyNotFound = errors.New("Key not found")
|
||||
|
||||
// Etcd is a plugin talks to an etcd cluster.
|
||||
type Etcd struct {
|
||||
Next plugin.Handler
|
||||
|
@ -26,7 +36,7 @@ type Etcd struct {
|
|||
Zones []string
|
||||
PathPrefix string
|
||||
Upstream upstream.Upstream // Proxy for looking up names during the resolution process
|
||||
Client etcdc.KeysAPI
|
||||
Client *etcdcv3.Client
|
||||
Ctx context.Context
|
||||
Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving.
|
||||
|
||||
|
@ -56,10 +66,7 @@ func (e *Etcd) Lookup(state request.Request, name string, typ uint16) (*dns.Msg,
|
|||
|
||||
// IsNameError implements the ServiceBackend interface.
|
||||
func (e *Etcd) IsNameError(err error) bool {
|
||||
if ee, ok := err.(etcdc.Error); ok && ee.Code == etcdc.ErrorCodeKeyNotFound {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return err == errKeyNotFound
|
||||
}
|
||||
|
||||
// Records looks up records in etcd. If exact is true, it will lookup just this
|
||||
|
@ -73,51 +80,50 @@ func (e *Etcd) Records(state request.Request, exact bool) ([]msg.Service, error)
|
|||
return nil, err
|
||||
}
|
||||
segments := strings.Split(msg.Path(name, e.PathPrefix), "/")
|
||||
switch {
|
||||
case exact && r.Node.Dir:
|
||||
return nil, nil
|
||||
case r.Node.Dir:
|
||||
return e.loopNodes(r.Node.Nodes, segments, star, nil)
|
||||
default:
|
||||
return e.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
|
||||
}
|
||||
return e.loopNodes(r.Kvs, segments, star)
|
||||
}
|
||||
|
||||
// get is a wrapper for client.Get
|
||||
func (e *Etcd) get(path string, recursive bool) (*etcdc.Response, error) {
|
||||
func (e *Etcd) get(path string, recursive bool) (*etcdcv3.GetResponse, error) {
|
||||
ctx, cancel := context.WithTimeout(e.Ctx, etcdTimeout)
|
||||
defer cancel()
|
||||
r, err := e.Client.Get(ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive})
|
||||
if recursive == true {
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
path = path + "/"
|
||||
}
|
||||
r, err := e.Client.Get(ctx, path, etcdcv3.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Count == 0 {
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
r, err = e.Client.Get(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Count == 0 {
|
||||
return nil, errKeyNotFound
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
r, err := e.Client.Get(ctx, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Count == 0 {
|
||||
return nil, errKeyNotFound
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// skydns/local/skydns/east/staging/web
|
||||
// skydns/local/skydns/west/production/web
|
||||
//
|
||||
// skydns/local/skydns/*/*/web
|
||||
// skydns/local/skydns/*/web
|
||||
|
||||
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
|
||||
// will be match against any wildcards when star is true.
|
||||
func (e *Etcd) loopNodes(ns []*etcdc.Node, nameParts []string, star bool, bx map[msg.Service]bool) (sx []msg.Service, err error) {
|
||||
if bx == nil {
|
||||
bx = make(map[msg.Service]bool)
|
||||
}
|
||||
func (e *Etcd) loopNodes(kv []*mvccpb.KeyValue, nameParts []string, star bool) (sx []msg.Service, err error) {
|
||||
bx := make(map[msg.Service]bool)
|
||||
Nodes:
|
||||
for _, n := range ns {
|
||||
if n.Dir {
|
||||
nodes, err := e.loopNodes(n.Nodes, nameParts, star, bx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sx = append(sx, nodes...)
|
||||
continue
|
||||
}
|
||||
for _, n := range kv {
|
||||
if star {
|
||||
keyParts := strings.Split(n.Key, "/")
|
||||
s := string(n.Key)
|
||||
keyParts := strings.Split(s, "/")
|
||||
for i, n := range nameParts {
|
||||
if i > len(keyParts)-1 {
|
||||
// name is longer than key
|
||||
|
@ -132,16 +138,16 @@ Nodes:
|
|||
}
|
||||
}
|
||||
serv := new(msg.Service)
|
||||
if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
|
||||
if err := json.Unmarshal(n.Value, serv); err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
|
||||
}
|
||||
b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: n.Key}
|
||||
b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: string(n.Key)}
|
||||
if _, ok := bx[b]; ok {
|
||||
continue
|
||||
}
|
||||
bx[b] = true
|
||||
|
||||
serv.Key = n.Key
|
||||
serv.Key = string(n.Key)
|
||||
serv.TTL = e.TTL(n, serv)
|
||||
if serv.Priority == 0 {
|
||||
serv.Priority = priority
|
||||
|
@ -153,8 +159,8 @@ Nodes:
|
|||
|
||||
// TTL returns the smaller of the etcd TTL and the service's
|
||||
// TTL. If neither of these are set (have a zero value), a default is used.
|
||||
func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 {
|
||||
etcdTTL := uint32(node.TTL)
|
||||
func (e *Etcd) TTL(kv *mvccpb.KeyValue, serv *msg.Service) uint32 {
|
||||
etcdTTL := uint32(kv.Lease)
|
||||
|
||||
if etcdTTL == 0 && serv.TTL == 0 {
|
||||
return ttl
|
||||
|
@ -170,9 +176,3 @@ func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 {
|
|||
}
|
||||
return serv.TTL
|
||||
}
|
||||
|
||||
const (
|
||||
priority = 10 // default priority when nothing is set
|
||||
ttl = 300 // default ttl when nothing is set
|
||||
etcdTimeout = 5 * time.Second
|
||||
)
|
||||
|
|
|
@ -65,8 +65,7 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||
// Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
|
||||
_, err = plugin.A(e, zone, state, nil, opt)
|
||||
}
|
||||
|
||||
if e.IsNameError(err) {
|
||||
if err != nil && e.IsNameError(err) {
|
||||
if e.Fall.Through(state.Name()) {
|
||||
return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
|
||||
etcdc "github.com/coreos/etcd/client"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -300,12 +299,12 @@ func set(t *testing.T, e *Etcd, k string, ttl time.Duration, m *msg.Service) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
path, _ := msg.PathWithWildcard(k, e.PathPrefix)
|
||||
e.Client.Set(ctxt, path, string(b), &etcdc.SetOptions{TTL: ttl})
|
||||
e.Client.KV.Put(ctxt, path, string(b))
|
||||
}
|
||||
|
||||
func delete(t *testing.T, e *Etcd, k string) {
|
||||
path, _ := msg.PathWithWildcard(k, e.PathPrefix)
|
||||
e.Client.Delete(ctxt, path, &etcdc.DeleteOptions{Recursive: false})
|
||||
e.Client.Delete(ctxt, path)
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
|
||||
etcdc "github.com/coreos/etcd/client"
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
|
@ -124,22 +124,22 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
|
|||
}
|
||||
etc.Client = client
|
||||
etc.endpoints = endpoints
|
||||
|
||||
|
||||
return &etc, stubzones, nil
|
||||
}
|
||||
return &Etcd{}, false, nil
|
||||
}
|
||||
|
||||
func newEtcdClient(endpoints []string, cc *tls.Config) (etcdc.KeysAPI, error) {
|
||||
etcdCfg := etcdc.Config{
|
||||
func newEtcdClient(endpoints []string, cc *tls.Config) (*etcdcv3.Client, error) {
|
||||
etcdCfg := etcdcv3.Config{
|
||||
Endpoints: endpoints,
|
||||
Transport: mwtls.NewHTTPSTransport(cc),
|
||||
TLS: cc,
|
||||
}
|
||||
cli, err := etcdc.New(etcdCfg)
|
||||
cli, err := etcdcv3.New(etcdCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return etcdc.NewKeysAPI(cli), nil
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
const defaultEndpoint = "http://localhost:2379"
|
||||
|
|
|
@ -16,17 +16,16 @@ import (
|
|||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
etcdc "github.com/coreos/etcd/client"
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func etcdPlugin() *etcd.Etcd {
|
||||
etcdCfg := etcdc.Config{
|
||||
etcdCfg := etcdcv3.Config{
|
||||
Endpoints: []string{"http://localhost:2379"},
|
||||
}
|
||||
cli, _ := etcdc.New(etcdCfg)
|
||||
client := etcdc.NewKeysAPI(cli)
|
||||
return &etcd.Etcd{Client: client, PathPrefix: "/skydns"}
|
||||
cli, _ := etcdcv3.New(etcdCfg)
|
||||
return &etcd.Etcd{Client: cli, PathPrefix: "/skydns"}
|
||||
}
|
||||
|
||||
// This test starts two coredns servers (and needs etcd). Configure a stubzones in both (that will loop) and
|
||||
|
@ -94,11 +93,11 @@ func set(ctx context.Context, t *testing.T, e *etcd.Etcd, k string, ttl time.Dur
|
|||
t.Fatal(err)
|
||||
}
|
||||
path, _ := msg.PathWithWildcard(k, e.PathPrefix)
|
||||
e.Client.Set(ctx, path, string(b), &etcdc.SetOptions{TTL: ttl})
|
||||
e.Client.KV.Put(ctx, path, string(b))
|
||||
}
|
||||
|
||||
// Copied from plugin/etcd/setup_test.go
|
||||
func delete(ctx context.Context, t *testing.T, e *etcd.Etcd, k string) {
|
||||
path, _ := msg.PathWithWildcard(k, e.PathPrefix)
|
||||
e.Client.Delete(ctx, path, &etcdc.DeleteOptions{Recursive: false})
|
||||
e.Client.Delete(ctx, path)
|
||||
}
|
||||
|
|
36
vendor/github.com/DataDog/dd-trace-go/contrib/README.md
generated
vendored
Normal file
36
vendor/github.com/DataDog/dd-trace-go/contrib/README.md
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Libraries supported for tracing
|
||||
|
||||
All of these libraries are supported by our Application Performance Monitoring tool.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Check if your library is supported (*i.e.* you find it in this directory).
|
||||
*ex:* if you're using the `net/http` package for your server, you see it's present in this directory.
|
||||
|
||||
2. In your app, replace your import by our traced version of the library.
|
||||
*ex:*
|
||||
```go
|
||||
import "net/http"
|
||||
```
|
||||
becomes
|
||||
```go
|
||||
import "github.com/DataDog/dd-trace-go/contrib/net/http"
|
||||
```
|
||||
|
||||
3. Read through the `example_test.go` present in each folder of the libraries to understand how to trace your app.
|
||||
*ex:* for `net/http`, see [net/http/example_test.go](https://github.com/DataDog/dd-trace-go/blob/master/contrib/net/http/example_test.go)
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
### 1. Follow the package naming convention
|
||||
|
||||
If a library looks like this: `github.com/user/lib`, the contribution must looks like this `user/lib`.
|
||||
In the case of the standard library, just use the path after `src`.
|
||||
*E.g.* `src/database/sql` becomes `database/sql`.
|
||||
|
||||
### 2. Respect the original API
|
||||
|
||||
Keep the original names for exported functions, don't use the prefix or suffix `trace`.
|
||||
*E.g.* prefer `Open` instead of `OpenTrace`.
|
||||
|
||||
Of course you can modify the number of arguments of a function if you need to pass the tracer for example.
|
169
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/example_test.go
generated
vendored
Normal file
169
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package sql_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
sqltrace "github.com/DataDog/dd-trace-go/contrib/database/sql"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// To trace the sql calls, you just need to open your sql.DB with OpenTraced.
|
||||
// All calls through this sql.DB object will then be traced.
|
||||
func Example() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltrace.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// All calls through the database/sql API will then be traced.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// If you want to link your db calls with existing traces, you need to use
|
||||
// the context version of the database/sql API.
|
||||
// Just make sure you are passing the parent span within the context.
|
||||
func Example_context() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltrace.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// We create a parent span and put it within the context.
|
||||
span := tracer.NewRootSpan("postgres.parent", "web-backend", "query-parent")
|
||||
ctx := tracer.ContextWithSpan(context.Background(), span)
|
||||
|
||||
// We need to use the context version of the database/sql API
|
||||
// in order to link this call with the parent span.
|
||||
db.PingContext(ctx)
|
||||
rows, _ := db.QueryContext(ctx, "SELECT * FROM city LIMIT 5")
|
||||
rows.Close()
|
||||
|
||||
stmt, _ := db.PrepareContext(ctx, "INSERT INTO city(name) VALUES($1)")
|
||||
stmt.Exec("New York")
|
||||
stmt, _ = db.PrepareContext(ctx, "SELECT name FROM city LIMIT $1")
|
||||
rows, _ = stmt.Query(1)
|
||||
rows.Close()
|
||||
stmt.Close()
|
||||
|
||||
tx, _ := db.BeginTx(ctx, nil)
|
||||
tx.ExecContext(ctx, "INSERT INTO city(name) VALUES('New York')")
|
||||
rows, _ = tx.QueryContext(ctx, "SELECT * FROM city LIMIT 5")
|
||||
rows.Close()
|
||||
stmt, _ = tx.PrepareContext(ctx, "SELECT name FROM city LIMIT $1")
|
||||
rows, _ = stmt.Query(1)
|
||||
rows.Close()
|
||||
stmt.Close()
|
||||
tx.Commit()
|
||||
|
||||
// Calling span.Finish() will send the span into the tracer's buffer
|
||||
// and then being processed.
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
// You can trace all drivers implementing the database/sql/driver interface.
|
||||
// For example, you can trace the go-sql-driver/mysql with the following code.
|
||||
func Example_mySQL() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltrace.OpenTraced(&mysql.MySQLDriver{}, "user:password@/dbname", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// All calls through the database/sql API will then be traced.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
func ExampleOpenTraced() {
|
||||
// The first argument is a reference to the driver to trace.
|
||||
// The second argument is the dataSourceName.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
// The last argument allows you to specify a custom tracer to use for tracing.
|
||||
db, err := sqltrace.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// You can use a custom tracer by passing it through the optional last argument of OpenTraced.
|
||||
func ExampleOpenTraced_tracer() {
|
||||
// Create and customize a new tracer that will forward 50% of generated traces to the agent.
|
||||
// (useful to manage resource usage in high-throughput environments)
|
||||
trc := tracer.NewTracer()
|
||||
trc.SetSampleRate(0.5)
|
||||
|
||||
// Pass your custom tracer through the last argument of OpenTraced to trace your db calls with it.
|
||||
db, err := sqltrace.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// If you need more granularity, you can register the traced driver seperately from the Open call.
|
||||
func ExampleRegister() {
|
||||
// Register a traced version of your driver.
|
||||
sqltrace.Register("postgres", &pq.Driver{})
|
||||
|
||||
// Returns a sql.DB object that holds the traced connection to the database.
|
||||
// Note: the sql.DB object returned by sql.Open will not be traced so make sure to use sql.Open.
|
||||
db, _ := sqltrace.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
defer db.Close()
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// You can use a custom tracer by passing it through the optional last argument of Register.
|
||||
func ExampleRegister_tracer() {
|
||||
// Create and customize a new tracer that will forward 50% of generated traces to the agent.
|
||||
// (useful to manage resource usage in high-throughput environments)
|
||||
trc := tracer.NewTracer()
|
||||
trc.SetSampleRate(0.5)
|
||||
|
||||
// Register a traced version of your driver and specify to use the previous tracer
|
||||
// to send the traces to the agent.
|
||||
sqltrace.Register("postgres", &pq.Driver{}, trc)
|
||||
|
||||
// Returns a sql.DB object that holds the traced connection to the database.
|
||||
// Note: the sql.DB object returned by sql.Open will not be traced so make sure to use sql.Open.
|
||||
db, _ := sqltrace.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
defer db.Close()
|
||||
}
|
41
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/mysql_test.go
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/mysql_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/contrib/database/sql/sqltest"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func TestMySQL(t *testing.T) {
|
||||
trc, transport := tracertest.GetTestTracer()
|
||||
db, err := OpenTraced(&mysql.MySQLDriver{}, "test:test@tcp(127.0.0.1:53306)/test", "mysql-test", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
testDB := &sqltest.DB{
|
||||
DB: db,
|
||||
Tracer: trc,
|
||||
Transport: transport,
|
||||
DriverName: "mysql",
|
||||
}
|
||||
|
||||
expectedSpan := &tracer.Span{
|
||||
Name: "mysql.query",
|
||||
Service: "mysql-test",
|
||||
Type: "sql",
|
||||
}
|
||||
expectedSpan.Meta = map[string]string{
|
||||
"db.user": "test",
|
||||
"out.host": "127.0.0.1",
|
||||
"out.port": "53306",
|
||||
"db.name": "test",
|
||||
}
|
||||
|
||||
sqltest.AllSQLTests(t, testDB, expectedSpan)
|
||||
}
|
42
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parse.go
generated
vendored
Normal file
42
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn"
|
||||
)
|
||||
|
||||
// parseDSN returns all information passed through the DSN:
|
||||
func parseDSN(driverName, dsn string) (meta map[string]string, err error) {
|
||||
switch driverName {
|
||||
case "mysql":
|
||||
meta, err = parsedsn.MySQL(dsn)
|
||||
case "postgres":
|
||||
meta, err = parsedsn.Postgres(dsn)
|
||||
}
|
||||
meta = normalize(meta)
|
||||
return meta, err
|
||||
}
|
||||
|
||||
func normalize(meta map[string]string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
if nk, ok := normalizeKey(k); ok {
|
||||
m[nk] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func normalizeKey(k string) (string, bool) {
|
||||
switch k {
|
||||
case "user":
|
||||
return "db.user", true
|
||||
case "application_name":
|
||||
return "db.application", true
|
||||
case "dbname":
|
||||
return "db.name", true
|
||||
case "host", "port":
|
||||
return "out." + k, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
44
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parse_test.go
generated
vendored
Normal file
44
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parse_test.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseDSN(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
expected := map[string]string{
|
||||
"db.user": "bob",
|
||||
"out.host": "1.2.3.4",
|
||||
"out.port": "5432",
|
||||
"db.name": "mydb",
|
||||
}
|
||||
m, err := parseDSN("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
|
||||
expected = map[string]string{
|
||||
"db.user": "bob",
|
||||
"out.host": "1.2.3.4",
|
||||
"out.port": "5432",
|
||||
"db.name": "mydb",
|
||||
}
|
||||
m, err = parseDSN("mysql", "bob:secret@tcp(1.2.3.4:5432)/mydb")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
|
||||
expected = map[string]string{
|
||||
"out.port": "5433",
|
||||
"out.host": "master-db-master-active.postgres.service.consul",
|
||||
"db.name": "dogdatastaging",
|
||||
"db.application": "trace-api",
|
||||
"db.user": "dog",
|
||||
}
|
||||
dsn := "connect_timeout=0 binary_parameters=no password=zMWmQz26GORmgVVKEbEl dbname=dogdatastaging application_name=trace-api port=5433 sslmode=disable host=master-db-master-active.postgres.service.consul user=dog"
|
||||
m, err = parseDSN("postgres", dsn)
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
}
|
25
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/collations.go
generated
vendored
Normal file
25
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/collations.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
const defaultCollation = "utf8_general_ci"
|
||||
|
||||
// A blacklist of collations which is unsafe to interpolate parameters.
|
||||
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
||||
var unsafeCollations = map[string]bool{
|
||||
"big5_chinese_ci": true,
|
||||
"sjis_japanese_ci": true,
|
||||
"gbk_chinese_ci": true,
|
||||
"big5_bin": true,
|
||||
"gb2312_bin": true,
|
||||
"gbk_bin": true,
|
||||
"sjis_bin": true,
|
||||
"cp932_japanese_ci": true,
|
||||
"cp932_bin": true,
|
||||
}
|
148
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/dsn.go
generated
vendored
Normal file
148
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/dsn.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
|
||||
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
|
||||
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
|
||||
errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
|
||||
)
|
||||
|
||||
// Config is a configuration parsed from a DSN string
|
||||
type Config struct {
|
||||
User string // Username
|
||||
Passwd string // Password (requires User)
|
||||
Net string // Network type
|
||||
Addr string // Network address (requires Net)
|
||||
DBName string // Database name
|
||||
Params map[string]string // Connection parameters
|
||||
Collation string // Connection collation
|
||||
Loc *time.Location // Location for time.Time values
|
||||
MaxAllowedPacket int // Max packet size allowed
|
||||
TLSConfig string // TLS configuration name
|
||||
tls *tls.Config // TLS configuration
|
||||
Timeout time.Duration // Dial timeout
|
||||
ReadTimeout time.Duration // I/O read timeout
|
||||
WriteTimeout time.Duration // I/O write timeout
|
||||
|
||||
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
|
||||
AllowCleartextPasswords bool // Allows the cleartext client side plugin
|
||||
AllowNativePasswords bool // Allows the native password authentication method
|
||||
AllowOldPasswords bool // Allows the old insecure password method
|
||||
ClientFoundRows bool // Return number of matching rows instead of rows changed
|
||||
ColumnsWithAlias bool // Prepend table alias to column names
|
||||
InterpolateParams bool // Interpolate placeholders into query string
|
||||
MultiStatements bool // Allow multiple statements in one query
|
||||
ParseTime bool // Parse time values to time.Time
|
||||
Strict bool // Return warnings as errors
|
||||
}
|
||||
|
||||
// ParseDSN parses the DSN string to a Config
|
||||
func ParseDSN(dsn string) (cfg *Config, err error) {
|
||||
// New config with some default values
|
||||
cfg = &Config{
|
||||
Loc: time.UTC,
|
||||
Collation: defaultCollation,
|
||||
}
|
||||
|
||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
||||
foundSlash := false
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
foundSlash = true
|
||||
var j, k int
|
||||
|
||||
// left part is empty if i <= 0
|
||||
if i > 0 {
|
||||
// [username[:password]@][protocol[(address)]]
|
||||
// Find the last '@' in dsn[:i]
|
||||
for j = i; j >= 0; j-- {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
if dsn[k] == ':' {
|
||||
cfg.Passwd = dsn[k+1 : j]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.User = dsn[:k]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// [protocol[(address)]]
|
||||
// Find the first '(' in dsn[j+1:i]
|
||||
for k = j + 1; k < i; k++ {
|
||||
if dsn[k] == '(' {
|
||||
// dsn[i-1] must be == ')' if an address is specified
|
||||
if dsn[i-1] != ')' {
|
||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
||||
return nil, errInvalidDSNUnescaped
|
||||
}
|
||||
return nil, errInvalidDSNAddr
|
||||
}
|
||||
cfg.Addr = dsn[k+1 : i-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.Net = dsn[j+1 : k]
|
||||
}
|
||||
|
||||
// dbname[?param1=value1&...¶mN=valueN]
|
||||
// Find the first '?' in dsn[i+1:]
|
||||
for j = i + 1; j < len(dsn); j++ {
|
||||
if dsn[j] == '?' {
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.DBName = dsn[i+1 : j]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSlash && len(dsn) > 0 {
|
||||
return nil, errInvalidDSNNoSlash
|
||||
}
|
||||
|
||||
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
||||
return nil, errInvalidDSNUnsafeCollation
|
||||
}
|
||||
|
||||
// Set default network if empty
|
||||
if cfg.Net == "" {
|
||||
cfg.Net = "tcp"
|
||||
}
|
||||
|
||||
// Set default address if empty
|
||||
if cfg.Addr == "" {
|
||||
switch cfg.Net {
|
||||
case "tcp":
|
||||
cfg.Addr = "127.0.0.1:3306"
|
||||
case "unix":
|
||||
cfg.Addr = "/tmp/mysql.sock"
|
||||
default:
|
||||
return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
3
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/mysql.go
generated
vendored
Normal file
3
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/mysql.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Package mysql is the minimal fork of go-sql-driver/mysql so we can use their code
|
||||
// to parse the mysql DSNs
|
||||
package mysql
|
23
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/utils.go
generated
vendored
Normal file
23
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
// Returns the bool value of the input.
|
||||
// The 2nd return value indicates if the input was a valid bool value
|
||||
func readBool(input string) (value bool, valid bool) {
|
||||
switch input {
|
||||
case "1", "true", "TRUE", "True":
|
||||
return true, true
|
||||
case "0", "false", "FALSE", "False":
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Not a valid bool value
|
||||
return
|
||||
}
|
47
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/parsedsn.go
generated
vendored
Normal file
47
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/parsedsn.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Package parsedsn provides functions to parse any kind of DSNs into a map[string]string
|
||||
package parsedsn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/mysql"
|
||||
"github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq"
|
||||
)
|
||||
|
||||
// Postgres parses a postgres-type dsn into a map
|
||||
func Postgres(dsn string) (map[string]string, error) {
|
||||
var err error
|
||||
meta := make(map[string]string)
|
||||
|
||||
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
|
||||
dsn, err = pq.ParseURL(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := pq.ParseOpts(dsn, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Assure that we do not pass the user secret
|
||||
delete(meta, "password")
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// MySQL parses a mysql-type dsn into a map
|
||||
func MySQL(dsn string) (m map[string]string, err error) {
|
||||
var cfg *mysql.Config
|
||||
if cfg, err = mysql.ParseDSN(dsn); err == nil {
|
||||
addr := strings.Split(cfg.Addr, ":")
|
||||
m = map[string]string{
|
||||
"user": cfg.User,
|
||||
"host": addr[0],
|
||||
"port": addr[1],
|
||||
"dbname": cfg.DBName,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
49
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/parsedsn_test.go
generated
vendored
Normal file
49
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/parsedsn_test.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package parsedsn
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMySQL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
expected := map[string]string{
|
||||
"user": "bob",
|
||||
"host": "1.2.3.4",
|
||||
"port": "5432",
|
||||
"dbname": "mydb",
|
||||
}
|
||||
m, err := MySQL("bob:secret@tcp(1.2.3.4:5432)/mydb")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
}
|
||||
|
||||
func TestPostgres(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
expected := map[string]string{
|
||||
"user": "bob",
|
||||
"host": "1.2.3.4",
|
||||
"port": "5432",
|
||||
"dbname": "mydb",
|
||||
"sslmode": "verify-full",
|
||||
}
|
||||
m, err := Postgres("postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
|
||||
expected = map[string]string{
|
||||
"user": "dog",
|
||||
"port": "5433",
|
||||
"host": "master-db-master-active.postgres.service.consul",
|
||||
"dbname": "dogdatastaging",
|
||||
"application_name": "trace-api",
|
||||
}
|
||||
dsn := "password=zMWmQz26GORmgVVKEbEl dbname=dogdatastaging application_name=trace-api port=5433 host=master-db-master-active.postgres.service.consul user=dog"
|
||||
m, err = Postgres(dsn)
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
}
|
118
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/conn.go
generated
vendored
Normal file
118
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
package pq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type values map[string]string
|
||||
|
||||
// scanner implements a tokenizer for libpq-style option strings.
|
||||
type scanner struct {
|
||||
s []rune
|
||||
i int
|
||||
}
|
||||
|
||||
// newScanner returns a new scanner initialized with the option string s.
|
||||
func newScanner(s string) *scanner {
|
||||
return &scanner{[]rune(s), 0}
|
||||
}
|
||||
|
||||
// Next returns the next rune.
|
||||
// It returns 0, false if the end of the text has been reached.
|
||||
func (s *scanner) Next() (rune, bool) {
|
||||
if s.i >= len(s.s) {
|
||||
return 0, false
|
||||
}
|
||||
r := s.s[s.i]
|
||||
s.i++
|
||||
return r, true
|
||||
}
|
||||
|
||||
// SkipSpaces returns the next non-whitespace rune.
|
||||
// It returns 0, false if the end of the text has been reached.
|
||||
func (s *scanner) SkipSpaces() (rune, bool) {
|
||||
r, ok := s.Next()
|
||||
for unicode.IsSpace(r) && ok {
|
||||
r, ok = s.Next()
|
||||
}
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// ParseOpts parses the options from name and adds them to the values.
|
||||
// The parsing code is based on conninfo_parse from libpq's fe-connect.c
|
||||
func ParseOpts(name string, o values) error {
|
||||
s := newScanner(name)
|
||||
|
||||
for {
|
||||
var (
|
||||
keyRunes, valRunes []rune
|
||||
r rune
|
||||
ok bool
|
||||
)
|
||||
|
||||
if r, ok = s.SkipSpaces(); !ok {
|
||||
break
|
||||
}
|
||||
|
||||
// Scan the key
|
||||
for !unicode.IsSpace(r) && r != '=' {
|
||||
keyRunes = append(keyRunes, r)
|
||||
if r, ok = s.Next(); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Skip any whitespace if we're not at the = yet
|
||||
if r != '=' {
|
||||
r, ok = s.SkipSpaces()
|
||||
}
|
||||
|
||||
// The current character should be =
|
||||
if r != '=' || !ok {
|
||||
return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes))
|
||||
}
|
||||
|
||||
// Skip any whitespace after the =
|
||||
if r, ok = s.SkipSpaces(); !ok {
|
||||
// If we reach the end here, the last value is just an empty string as per libpq.
|
||||
o[string(keyRunes)] = ""
|
||||
break
|
||||
}
|
||||
|
||||
if r != '\'' {
|
||||
for !unicode.IsSpace(r) {
|
||||
if r == '\\' {
|
||||
if r, ok = s.Next(); !ok {
|
||||
return fmt.Errorf(`missing character after backslash`)
|
||||
}
|
||||
}
|
||||
valRunes = append(valRunes, r)
|
||||
|
||||
if r, ok = s.Next(); !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote:
|
||||
for {
|
||||
if r, ok = s.Next(); !ok {
|
||||
return fmt.Errorf(`unterminated quoted string literal in connection string`)
|
||||
}
|
||||
switch r {
|
||||
case '\'':
|
||||
break quote
|
||||
case '\\':
|
||||
r, _ = s.Next()
|
||||
fallthrough
|
||||
default:
|
||||
valRunes = append(valRunes, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o[string(keyRunes)] = string(valRunes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
2
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/pq.go
generated
vendored
Normal file
2
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/pq.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package pq is the minimal fork of lib/pq so we can use their code to parse the postgres DSNs
|
||||
package pq
|
76
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/url.go
generated
vendored
Normal file
76
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/parsedsn/pq/url.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package pq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
nurl "net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseURL no longer needs to be used by clients of this library since supplying a URL as a
|
||||
// connection string to sql.Open() is now supported:
|
||||
//
|
||||
// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
|
||||
//
|
||||
// It remains exported here for backwards-compatibility.
|
||||
//
|
||||
// ParseURL converts a url to a connection string for driver.Open.
|
||||
// Example:
|
||||
//
|
||||
// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full"
|
||||
//
|
||||
// converts to:
|
||||
//
|
||||
// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full"
|
||||
//
|
||||
// A minimal example:
|
||||
//
|
||||
// "postgres://"
|
||||
//
|
||||
// This will be blank, causing driver.Open to use all of the defaults
|
||||
func ParseURL(url string) (string, error) {
|
||||
u, err := nurl.Parse(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.Scheme != "postgres" && u.Scheme != "postgresql" {
|
||||
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
|
||||
}
|
||||
|
||||
var kvs []string
|
||||
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
|
||||
accrue := func(k, v string) {
|
||||
if v != "" {
|
||||
kvs = append(kvs, k+"="+escaper.Replace(v))
|
||||
}
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
v := u.User.Username()
|
||||
accrue("user", v)
|
||||
|
||||
v, _ = u.User.Password()
|
||||
accrue("password", v)
|
||||
}
|
||||
|
||||
if host, port, err := net.SplitHostPort(u.Host); err != nil {
|
||||
accrue("host", u.Host)
|
||||
} else {
|
||||
accrue("host", host)
|
||||
accrue("port", port)
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
accrue("dbname", u.Path[1:])
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
for k := range q {
|
||||
accrue(k, q.Get(k))
|
||||
}
|
||||
|
||||
sort.Strings(kvs) // Makes testing easier (not a performance concern)
|
||||
return strings.Join(kvs, " "), nil
|
||||
}
|
41
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/pq_test.go
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/pq_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/contrib/database/sql/sqltest"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func TestPostgres(t *testing.T) {
|
||||
trc, transport := tracertest.GetTestTracer()
|
||||
db, err := OpenTraced(&pq.Driver{}, "postgres://postgres:postgres@127.0.0.1:55432/postgres?sslmode=disable", "postgres-test", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
testDB := &sqltest.DB{
|
||||
DB: db,
|
||||
Tracer: trc,
|
||||
Transport: transport,
|
||||
DriverName: "postgres",
|
||||
}
|
||||
|
||||
expectedSpan := &tracer.Span{
|
||||
Name: "postgres.query",
|
||||
Service: "postgres-test",
|
||||
Type: "sql",
|
||||
}
|
||||
expectedSpan.Meta = map[string]string{
|
||||
"db.user": "postgres",
|
||||
"out.host": "127.0.0.1",
|
||||
"out.port": "55432",
|
||||
"db.name": "postgres",
|
||||
}
|
||||
|
||||
sqltest.AllSQLTests(t, testDB, expectedSpan)
|
||||
}
|
384
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sql.go
generated
vendored
Normal file
384
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sql.go
generated
vendored
Normal file
|
@ -0,0 +1,384 @@
|
|||
// Package sqltraced provides a traced version of any driver implementing the database/sql/driver interface.
|
||||
// To trace jmoiron/sqlx, see https://godoc.org/github.com/DataDog/dd-trace-go/tracer/contrib/sqlxtraced.
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
log "github.com/cihub/seelog"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/sqlutils"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
)
|
||||
|
||||
// OpenTraced will first register the traced version of the `driver` if not yet registered and will then open a connection with it.
|
||||
// This is usually the only function to use when there is no need for the granularity offered by Register and Open.
|
||||
// The last parameter is optional and enables you to use a custom tracer.
|
||||
func OpenTraced(driver driver.Driver, dataSourceName, service string, trcv ...*tracer.Tracer) (*sql.DB, error) {
|
||||
driverName := sqlutils.GetDriverName(driver)
|
||||
Register(driverName, driver, trcv...)
|
||||
return Open(driverName, dataSourceName, service)
|
||||
}
|
||||
|
||||
// Register takes a driver and registers a traced version of this one.
|
||||
// The last parameter is optional and enables you to use a custom tracer.
|
||||
func Register(driverName string, driver driver.Driver, trcv ...*tracer.Tracer) {
|
||||
if driver == nil {
|
||||
log.Error("RegisterTracedDriver: driver is nil")
|
||||
return
|
||||
}
|
||||
var trc *tracer.Tracer
|
||||
if len(trcv) == 0 || (len(trcv) > 0 && trcv[0] == nil) {
|
||||
trc = tracer.DefaultTracer
|
||||
} else {
|
||||
trc = trcv[0]
|
||||
}
|
||||
|
||||
tracedDriverName := sqlutils.GetTracedDriverName(driverName)
|
||||
if !stringInSlice(sql.Drivers(), tracedDriverName) {
|
||||
td := tracedDriver{
|
||||
Driver: driver,
|
||||
tracer: trc,
|
||||
driverName: driverName,
|
||||
}
|
||||
sql.Register(tracedDriverName, td)
|
||||
log.Infof("Register %s driver", tracedDriverName)
|
||||
} else {
|
||||
log.Warnf("RegisterTracedDriver: %s already registered", tracedDriverName)
|
||||
}
|
||||
}
|
||||
|
||||
// Open extends the usual API of sql.Open so you can specify the name of the service
|
||||
// under which the traces will appear in the datadog app.
|
||||
func Open(driverName, dataSourceName, service string) (*sql.DB, error) {
|
||||
tracedDriverName := sqlutils.GetTracedDriverName(driverName)
|
||||
// The service is passed through the DSN
|
||||
dsnAndService := newDSNAndService(dataSourceName, service)
|
||||
return sql.Open(tracedDriverName, dsnAndService)
|
||||
}
|
||||
|
||||
// tracedDriver is a driver we use as a middleware between the database/sql package
|
||||
// and the driver chosen (e.g. mysql, postgresql...).
|
||||
// It implements the driver.Driver interface and add the tracing features on top
|
||||
// of the driver's methods.
|
||||
type tracedDriver struct {
|
||||
driver.Driver
|
||||
tracer *tracer.Tracer
|
||||
driverName string
|
||||
}
|
||||
|
||||
// Open returns a tracedConn so that we can pass all the info we get from the DSN
|
||||
// all along the tracing
|
||||
func (td tracedDriver) Open(dsnAndService string) (c driver.Conn, err error) {
|
||||
var meta map[string]string
|
||||
var conn driver.Conn
|
||||
|
||||
dsn, service := parseDSNAndService(dsnAndService)
|
||||
|
||||
// Register the service to Datadog tracing API
|
||||
td.tracer.SetServiceInfo(service, td.driverName, ext.AppTypeDB)
|
||||
|
||||
// Get all kinds of information from the DSN
|
||||
meta, err = parseDSN(td.driverName, dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err = td.Driver.Open(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ti := traceInfo{
|
||||
tracer: td.tracer,
|
||||
driverName: td.driverName,
|
||||
service: service,
|
||||
meta: meta,
|
||||
}
|
||||
return &tracedConn{conn, ti}, err
|
||||
}
|
||||
|
||||
// traceInfo stores all information relative to the tracing
|
||||
type traceInfo struct {
|
||||
tracer *tracer.Tracer
|
||||
driverName string
|
||||
service string
|
||||
resource string
|
||||
meta map[string]string
|
||||
}
|
||||
|
||||
func (ti traceInfo) getSpan(ctx context.Context, resource string, query ...string) *tracer.Span {
|
||||
name := fmt.Sprintf("%s.%s", ti.driverName, "query")
|
||||
span := ti.tracer.NewChildSpanFromContext(name, ctx)
|
||||
span.Type = ext.SQLType
|
||||
span.Service = ti.service
|
||||
span.Resource = resource
|
||||
if len(query) > 0 {
|
||||
span.Resource = query[0]
|
||||
span.SetMeta(ext.SQLQuery, query[0])
|
||||
}
|
||||
for k, v := range ti.meta {
|
||||
span.SetMeta(k, v)
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
type tracedConn struct {
|
||||
driver.Conn
|
||||
traceInfo
|
||||
}
|
||||
|
||||
func (tc tracedConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) {
|
||||
span := tc.getSpan(ctx, "Begin")
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
if connBeginTx, ok := tc.Conn.(driver.ConnBeginTx); ok {
|
||||
tx, err = connBeginTx.BeginTx(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tracedTx{tx, tc.traceInfo, ctx}, nil
|
||||
}
|
||||
|
||||
tx, err = tc.Conn.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tracedTx{tx, tc.traceInfo, ctx}, nil
|
||||
}
|
||||
|
||||
func (tc tracedConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
|
||||
span := tc.getSpan(ctx, "Prepare", query)
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
// Check if the driver implements PrepareContext
|
||||
if connPrepareCtx, ok := tc.Conn.(driver.ConnPrepareContext); ok {
|
||||
stmt, err := connPrepareCtx.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tracedStmt{stmt, tc.traceInfo, ctx, query}, nil
|
||||
}
|
||||
|
||||
// If the driver does not implement PrepareContex (lib/pq for example)
|
||||
stmt, err = tc.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tracedStmt{stmt, tc.traceInfo, ctx, query}, nil
|
||||
}
|
||||
|
||||
func (tc tracedConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if execer, ok := tc.Conn.(driver.Execer); ok {
|
||||
return execer.Exec(query, args)
|
||||
}
|
||||
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
|
||||
func (tc tracedConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) {
|
||||
span := tc.getSpan(ctx, "Exec", query)
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
if execContext, ok := tc.Conn.(driver.ExecerContext); ok {
|
||||
res, err := execContext.ExecContext(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Fallback implementation
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
return tc.Exec(query, dargs)
|
||||
}
|
||||
|
||||
// tracedConn has a Ping method in order to implement the pinger interface
|
||||
func (tc tracedConn) Ping(ctx context.Context) (err error) {
|
||||
span := tc.getSpan(ctx, "Ping")
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
if pinger, ok := tc.Conn.(driver.Pinger); ok {
|
||||
err = pinger.Ping(ctx)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (tc tracedConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
if queryer, ok := tc.Conn.(driver.Queryer); ok {
|
||||
return queryer.Query(query, args)
|
||||
}
|
||||
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
|
||||
func (tc tracedConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) {
|
||||
span := tc.getSpan(ctx, "Query", query)
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
if queryerContext, ok := tc.Conn.(driver.QueryerContext); ok {
|
||||
rows, err := queryerContext.QueryContext(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
return tc.Query(query, dargs)
|
||||
}
|
||||
|
||||
// tracedTx is a traced version of sql.Tx
|
||||
type tracedTx struct {
|
||||
driver.Tx
|
||||
traceInfo
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Commit sends a span at the end of the transaction
|
||||
func (t tracedTx) Commit() (err error) {
|
||||
span := t.getSpan(t.ctx, "Commit")
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
return t.Tx.Commit()
|
||||
}
|
||||
|
||||
// Rollback sends a span if the connection is aborted
|
||||
func (t tracedTx) Rollback() (err error) {
|
||||
span := t.getSpan(t.ctx, "Rollback")
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
return t.Tx.Rollback()
|
||||
}
|
||||
|
||||
// tracedStmt is traced version of sql.Stmt
|
||||
type tracedStmt struct {
|
||||
driver.Stmt
|
||||
traceInfo
|
||||
ctx context.Context
|
||||
query string
|
||||
}
|
||||
|
||||
// Close sends a span before closing a statement
|
||||
func (s tracedStmt) Close() (err error) {
|
||||
span := s.getSpan(s.ctx, "Close")
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
return s.Stmt.Close()
|
||||
}
|
||||
|
||||
// ExecContext is needed to implement the driver.StmtExecContext interface
|
||||
func (s tracedStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) {
|
||||
span := s.getSpan(s.ctx, "Exec", s.query)
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
if stmtExecContext, ok := s.Stmt.(driver.StmtExecContext); ok {
|
||||
res, err = stmtExecContext.ExecContext(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Fallback implementation
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
return s.Exec(dargs)
|
||||
}
|
||||
|
||||
// QueryContext is needed to implement the driver.StmtQueryContext interface
|
||||
func (s tracedStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) {
|
||||
span := s.getSpan(s.ctx, "Query", s.query)
|
||||
defer func() {
|
||||
span.SetError(err)
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
if stmtQueryContext, ok := s.Stmt.(driver.StmtQueryContext); ok {
|
||||
rows, err = stmtQueryContext.QueryContext(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// Fallback implementation
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
return s.Query(dargs)
|
||||
}
|
211
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqltest/sqltest.go
generated
vendored
Normal file
211
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqltest/sqltest.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
// Package sqltest is used for testing sql packages
|
||||
package sqltest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// setupTestCase initializes MySQL or Postgres databases and returns a
|
||||
// teardown function that must be executed via `defer`
|
||||
func setupTestCase(t *testing.T, db *DB) func(t *testing.T, db *DB) {
|
||||
// creates the database
|
||||
db.Exec("DROP TABLE IF EXISTS city")
|
||||
db.Exec("CREATE TABLE city (id integer NOT NULL DEFAULT '0', name text)")
|
||||
|
||||
// Empty the tracer
|
||||
db.Tracer.ForceFlush()
|
||||
db.Transport.Traces()
|
||||
|
||||
return func(t *testing.T, db *DB) {
|
||||
// drop the table
|
||||
db.Exec("DROP TABLE city")
|
||||
}
|
||||
}
|
||||
|
||||
// AllSQLTests applies a sequence of unit tests to check the correct tracing of sql features.
|
||||
func AllSQLTests(t *testing.T, db *DB, expectedSpan *tracer.Span) {
|
||||
// database setup and cleanup
|
||||
tearDown := setupTestCase(t, db)
|
||||
defer tearDown(t, db)
|
||||
|
||||
testDB(t, db, expectedSpan)
|
||||
testStatement(t, db, expectedSpan)
|
||||
testTransaction(t, db, expectedSpan)
|
||||
}
|
||||
|
||||
func testDB(t *testing.T, db *DB, expectedSpan *tracer.Span) {
|
||||
assert := assert.New(t)
|
||||
const query = "SELECT id, name FROM city LIMIT 5"
|
||||
|
||||
// Test db.Ping
|
||||
err := db.Ping()
|
||||
assert.Equal(nil, err)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces := db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
actualSpan := spans[0]
|
||||
pingSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
pingSpan.Resource = "Ping"
|
||||
tracertest.CompareSpan(t, pingSpan, actualSpan)
|
||||
|
||||
// Test db.Query
|
||||
rows, err := db.Query(query)
|
||||
defer rows.Close()
|
||||
assert.Equal(nil, err)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces = db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
actualSpan = spans[0]
|
||||
querySpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
querySpan.Resource = query
|
||||
querySpan.SetMeta("sql.query", query)
|
||||
tracertest.CompareSpan(t, querySpan, actualSpan)
|
||||
delete(expectedSpan.Meta, "sql.query")
|
||||
}
|
||||
|
||||
func testStatement(t *testing.T, db *DB, expectedSpan *tracer.Span) {
|
||||
assert := assert.New(t)
|
||||
query := "INSERT INTO city(name) VALUES(%s)"
|
||||
switch db.DriverName {
|
||||
case "postgres":
|
||||
query = fmt.Sprintf(query, "$1")
|
||||
case "mysql":
|
||||
query = fmt.Sprintf(query, "?")
|
||||
}
|
||||
|
||||
// Test TracedConn.PrepareContext
|
||||
stmt, err := db.Prepare(query)
|
||||
assert.Equal(nil, err)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces := db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
actualSpan := spans[0]
|
||||
prepareSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
prepareSpan.Resource = query
|
||||
prepareSpan.SetMeta("sql.query", query)
|
||||
tracertest.CompareSpan(t, prepareSpan, actualSpan)
|
||||
delete(expectedSpan.Meta, "sql.query")
|
||||
|
||||
// Test Exec
|
||||
_, err2 := stmt.Exec("New York")
|
||||
assert.Equal(nil, err2)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces = db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 1)
|
||||
actualSpan = spans[0]
|
||||
|
||||
execSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
execSpan.Resource = query
|
||||
execSpan.SetMeta("sql.query", query)
|
||||
tracertest.CompareSpan(t, execSpan, actualSpan)
|
||||
delete(expectedSpan.Meta, "sql.query")
|
||||
}
|
||||
|
||||
func testTransaction(t *testing.T, db *DB, expectedSpan *tracer.Span) {
|
||||
assert := assert.New(t)
|
||||
query := "INSERT INTO city(name) VALUES('New York')"
|
||||
|
||||
// Test Begin
|
||||
tx, err := db.Begin()
|
||||
assert.Equal(nil, err)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces := db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
actualSpan := spans[0]
|
||||
beginSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
beginSpan.Resource = "Begin"
|
||||
tracertest.CompareSpan(t, beginSpan, actualSpan)
|
||||
|
||||
// Test Rollback
|
||||
err = tx.Rollback()
|
||||
assert.Equal(nil, err)
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces = db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 1)
|
||||
actualSpan = spans[0]
|
||||
rollbackSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
rollbackSpan.Resource = "Rollback"
|
||||
tracertest.CompareSpan(t, rollbackSpan, actualSpan)
|
||||
|
||||
// Test Exec
|
||||
parentSpan := db.Tracer.NewRootSpan("test.parent", "test", "parent")
|
||||
ctx := tracer.ContextWithSpan(context.Background(), parentSpan)
|
||||
|
||||
tx, err = db.BeginTx(ctx, nil)
|
||||
assert.Equal(nil, err)
|
||||
|
||||
_, err = tx.ExecContext(ctx, query)
|
||||
assert.Equal(nil, err)
|
||||
|
||||
err = tx.Commit()
|
||||
assert.Equal(nil, err)
|
||||
|
||||
parentSpan.Finish() // need to do this else children are not flushed at all
|
||||
|
||||
db.Tracer.ForceFlush()
|
||||
traces = db.Transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 4)
|
||||
|
||||
for _, s := range spans {
|
||||
if s.Name == expectedSpan.Name && s.Resource == query {
|
||||
actualSpan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(actualSpan)
|
||||
execSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
execSpan.Resource = query
|
||||
execSpan.SetMeta("sql.query", query)
|
||||
tracertest.CompareSpan(t, execSpan, actualSpan)
|
||||
delete(expectedSpan.Meta, "sql.query")
|
||||
|
||||
for _, s := range spans {
|
||||
if s.Name == expectedSpan.Name && s.Resource == "Commit" {
|
||||
actualSpan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(actualSpan)
|
||||
commitSpan := tracertest.CopySpan(expectedSpan, db.Tracer)
|
||||
commitSpan.Resource = "Commit"
|
||||
tracertest.CompareSpan(t, commitSpan, actualSpan)
|
||||
}
|
||||
|
||||
// DB is a struct dedicated for testing
|
||||
type DB struct {
|
||||
*sql.DB
|
||||
Tracer *tracer.Tracer
|
||||
Transport *tracertest.DummyTransport
|
||||
DriverName string
|
||||
}
|
2
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/sqlutils.go
generated
vendored
Normal file
2
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/sqlutils.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package sqlutils share some utils functions for sql packages
|
||||
package sqlutils
|
59
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/utils.go
generated
vendored
Normal file
59
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package sqlutils
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetDriverName returns the driver type.
|
||||
func GetDriverName(driver driver.Driver) string {
|
||||
if driver == nil {
|
||||
return ""
|
||||
}
|
||||
driverType := fmt.Sprintf("%s", reflect.TypeOf(driver))
|
||||
switch driverType {
|
||||
case "*mysql.MySQLDriver":
|
||||
return "mysql"
|
||||
case "*pq.Driver":
|
||||
return "postgres"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetTracedDriverName add the suffix "Traced" to the driver name.
|
||||
func GetTracedDriverName(driverName string) string {
|
||||
return driverName + "Traced"
|
||||
}
|
||||
|
||||
func newDSNAndService(dsn, service string) string {
|
||||
return dsn + "|" + service
|
||||
}
|
||||
|
||||
func parseDSNAndService(dsnAndService string) (dsn, service string) {
|
||||
tab := strings.Split(dsnAndService, "|")
|
||||
return tab[0], tab[1]
|
||||
}
|
||||
|
||||
// namedValueToValue is a helper function copied from the database/sql package.
|
||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
||||
dargs := make([]driver.Value, len(named))
|
||||
for n, param := range named {
|
||||
if len(param.Name) > 0 {
|
||||
return nil, errors.New("sql: driver does not support the use of Named Parameters")
|
||||
}
|
||||
dargs[n] = param.Value
|
||||
}
|
||||
return dargs, nil
|
||||
}
|
||||
|
||||
// stringInSlice returns true if the string s is in the list.
|
||||
func stringInSlice(list []string, s string) bool {
|
||||
sort.Strings(list)
|
||||
i := sort.SearchStrings(list, s)
|
||||
return i < len(list) && list[i] == s
|
||||
}
|
17
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/utils_test.go
generated
vendored
Normal file
17
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/sqlutils/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package sqlutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/lib/pq"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetDriverName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
assert.Equal("postgres", GetDriverName(&pq.Driver{}))
|
||||
assert.Equal("mysql", GetDriverName(&mysql.MySQLDriver{}))
|
||||
assert.Equal("", GetDriverName(nil))
|
||||
}
|
36
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/utils.go
generated
vendored
Normal file
36
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newDSNAndService(dsn, service string) string {
|
||||
return dsn + "|" + service
|
||||
}
|
||||
|
||||
func parseDSNAndService(dsnAndService string) (dsn, service string) {
|
||||
tab := strings.Split(dsnAndService, "|")
|
||||
return tab[0], tab[1]
|
||||
}
|
||||
|
||||
// namedValueToValue is a helper function copied from the database/sql package.
|
||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
||||
dargs := make([]driver.Value, len(named))
|
||||
for n, param := range named {
|
||||
if len(param.Name) > 0 {
|
||||
return nil, errors.New("sql: driver does not support the use of Named Parameters")
|
||||
}
|
||||
dargs[n] = param.Value
|
||||
}
|
||||
return dargs, nil
|
||||
}
|
||||
|
||||
// stringInSlice returns true if the string s is in the list.
|
||||
func stringInSlice(list []string, s string) bool {
|
||||
sort.Strings(list)
|
||||
i := sort.SearchStrings(list, s)
|
||||
return i < len(list) && list[i] == s
|
||||
}
|
29
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/utils_test.go
generated
vendored
Normal file
29
vendor/github.com/DataDog/dd-trace-go/contrib/database/sql/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStringInSlice(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
list := []string{"mysql", "postgres", "pq"}
|
||||
assert.True(stringInSlice(list, "pq"))
|
||||
assert.False(stringInSlice(list, "Postgres"))
|
||||
}
|
||||
|
||||
func TestDSNAndService(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
dsn := "postgres://ubuntu@127.0.0.1:5432/circle_test?sslmode=disable"
|
||||
service := "master-db"
|
||||
|
||||
dsnAndService := "postgres://ubuntu@127.0.0.1:5432/circle_test?sslmode=disable|master-db"
|
||||
assert.Equal(dsnAndService, newDSNAndService(dsn, service))
|
||||
|
||||
actualDSN, actualService := parseDSNAndService(dsnAndService)
|
||||
assert.Equal(dsn, actualDSN)
|
||||
assert.Equal(service, actualService)
|
||||
}
|
59
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/example_test.go
generated
vendored
Normal file
59
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package redigo_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
redigotrace "github.com/DataDog/dd-trace-go/contrib/garyburd/redigo"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
// To start tracing Redis commands, use the TracedDial function to create a connection,
|
||||
// passing in a service name of choice.
|
||||
func Example() {
|
||||
c, _ := redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request'
|
||||
c.Do("SET", "food", "cheese", ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
func ExampleTracedConn() {
|
||||
c, _ := redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request'
|
||||
c.Do("SET", "food", "cheese", ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
// Alternatively, provide a redis URL to the TracedDialURL function
|
||||
func Example_dialURL() {
|
||||
c, _ := redigotrace.TracedDialURL("my-redis-backend", tracer.DefaultTracer, "redis://127.0.0.1:6379")
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
}
|
||||
|
||||
// When using a redigo Pool, set your Dial function to return a traced connection
|
||||
func Example_pool() {
|
||||
pool := &redis.Pool{
|
||||
Dial: func() (redis.Conn, error) {
|
||||
return redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
},
|
||||
}
|
||||
|
||||
c := pool.Get()
|
||||
|
||||
c.Do("SET", " whiskey", " glass")
|
||||
}
|
131
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/redigo.go
generated
vendored
Normal file
131
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/redigo.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Package redigo provides tracing for the Redigo Redis client (https://github.com/garyburd/redigo)
|
||||
package redigo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
redis "github.com/garyburd/redigo/redis"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TracedConn is an implementation of the redis.Conn interface that supports tracing
|
||||
type TracedConn struct {
|
||||
redis.Conn
|
||||
p traceParams
|
||||
}
|
||||
|
||||
// traceParams contains fields and metadata useful for command tracing
|
||||
type traceParams struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
network string
|
||||
host string
|
||||
port string
|
||||
}
|
||||
|
||||
// TracedDial takes a Conn returned by redis.Dial and configures it to emit spans with the given service name
|
||||
func TracedDial(service string, tracer *tracer.Tracer, network, address string, options ...redis.DialOption) (redis.Conn, error) {
|
||||
c, err := redis.Dial(network, address, options...)
|
||||
addr := strings.Split(address, ":")
|
||||
var host, port string
|
||||
if len(addr) == 2 && addr[1] != "" {
|
||||
port = addr[1]
|
||||
} else {
|
||||
port = "6379"
|
||||
}
|
||||
host = addr[0]
|
||||
tracer.SetServiceInfo(service, "redis", ext.AppTypeDB)
|
||||
tc := TracedConn{c, traceParams{tracer, service, network, host, port}}
|
||||
return tc, err
|
||||
}
|
||||
|
||||
// TracedDialURL takes a Conn returned by redis.DialURL and configures it to emit spans with the given service name
|
||||
func TracedDialURL(service string, tracer *tracer.Tracer, rawurl string, options ...redis.DialOption) (redis.Conn, error) {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return TracedConn{}, err
|
||||
}
|
||||
|
||||
// Getting host and port, usind code from https://github.com/garyburd/redigo/blob/master/redis/conn.go#L226
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
host = u.Host
|
||||
port = "6379"
|
||||
}
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
// Set in redis.DialUrl source code
|
||||
network := "tcp"
|
||||
c, err := redis.DialURL(rawurl, options...)
|
||||
tc := TracedConn{c, traceParams{tracer, service, network, host, port}}
|
||||
return tc, err
|
||||
}
|
||||
|
||||
// NewChildSpan creates a span inheriting from the given context. It adds to the span useful metadata about the traced Redis connection
|
||||
func (tc TracedConn) NewChildSpan(ctx context.Context) *tracer.Span {
|
||||
span := tc.p.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
span.Service = tc.p.service
|
||||
span.SetMeta("out.network", tc.p.network)
|
||||
span.SetMeta("out.port", tc.p.port)
|
||||
span.SetMeta("out.host", tc.p.host)
|
||||
return span
|
||||
}
|
||||
|
||||
// Do wraps redis.Conn.Do. It sends a command to the Redis server and returns the received reply.
|
||||
// In the process it emits a span containing key information about the command sent.
|
||||
// When passed a context.Context as the final argument, Do will ensure that any span created
|
||||
// inherits from this context. The rest of the arguments are passed through to the Redis server unchanged
|
||||
func (tc TracedConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
var ctx context.Context
|
||||
var ok bool
|
||||
if len(args) > 0 {
|
||||
ctx, ok = args[len(args)-1].(context.Context)
|
||||
if ok {
|
||||
args = args[:len(args)-1]
|
||||
}
|
||||
}
|
||||
|
||||
span := tc.NewChildSpan(ctx)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
span.SetMeta("redis.args_length", strconv.Itoa(len(args)))
|
||||
|
||||
if len(commandName) > 0 {
|
||||
span.Resource = commandName
|
||||
} else {
|
||||
// When the command argument to the Do method is "", then the Do method will flush the output buffer
|
||||
// See https://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining
|
||||
span.Resource = "redigo.Conn.Flush"
|
||||
}
|
||||
var b bytes.Buffer
|
||||
b.WriteString(commandName)
|
||||
for _, arg := range args {
|
||||
b.WriteString(" ")
|
||||
switch arg := arg.(type) {
|
||||
case string:
|
||||
b.WriteString(arg)
|
||||
case int:
|
||||
b.WriteString(strconv.Itoa(arg))
|
||||
case int32:
|
||||
b.WriteString(strconv.FormatInt(int64(arg), 10))
|
||||
case int64:
|
||||
b.WriteString(strconv.FormatInt(arg, 10))
|
||||
case fmt.Stringer:
|
||||
b.WriteString(arg.String())
|
||||
}
|
||||
}
|
||||
span.SetMeta("redis.raw_command", b.String())
|
||||
return tc.Conn.Do(commandName, args...)
|
||||
}
|
214
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/redigo_test.go
generated
vendored
Normal file
214
vendor/github.com/DataDog/dd-trace-go/contrib/garyburd/redigo/redigo_test.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
package redigo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
c.Do("SET", 1, "truck")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "SET")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "SET 1 truck")
|
||||
assert.Equal(span.GetMeta("redis.args_length"), "2")
|
||||
}
|
||||
|
||||
func TestCommandError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
_, err := c.Do("NOT_A_COMMAND", context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Error())
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "NOT_A_COMMAND")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "NOT_A_COMMAND")
|
||||
}
|
||||
|
||||
func TestConnectionError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, _ := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
_, err := TracedDial("redis-service", testTracer, "tcp", "127.0.0.1:1000")
|
||||
|
||||
assert.Contains(err.Error(), "dial tcp 127.0.0.1:1000")
|
||||
}
|
||||
|
||||
func TestInheritance(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parent_span := testTracer.NewChildSpanFromContext("parent_span", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parent_span)
|
||||
client, _ := TracedDial("my_service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
client.Do("SET", "water", "bottle", ctx)
|
||||
parent_span.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var child_span, pspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "redis.command":
|
||||
child_span = s
|
||||
case "parent_span":
|
||||
pspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(child_span, "there should be a child redis.command span")
|
||||
assert.NotNil(child_span, "there should be a parent span")
|
||||
|
||||
assert.Equal(child_span.ParentID, pspan.SpanID)
|
||||
assert.Equal(child_span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(child_span.GetMeta("out.port"), "56379")
|
||||
}
|
||||
|
||||
func TestCommandsToSring(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
stringify_test := TestStruct{Cpython: 57, Cgo: 8}
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
c.Do("SADD", "testSet", "a", int(0), int32(1), int64(2), stringify_test, context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "SADD")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "SADD testSet a 0 1 2 [57, 8]")
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
pool := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 3,
|
||||
IdleTimeout: 23,
|
||||
Wait: true,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
return TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
},
|
||||
}
|
||||
|
||||
pc := pool.Get()
|
||||
pc.Do("SET", " whiskey", " glass", context.Background())
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
assert.Equal(span.GetMeta("out.network"), "tcp")
|
||||
}
|
||||
|
||||
func TestTracingDialUrl(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
url := "redis://127.0.0.1:56379"
|
||||
client, _ := TracedDialURL("redis-service", testTracer, url)
|
||||
client.Do("SET", "ONE", " TWO", context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
}
|
||||
|
||||
// TestStruct implements String interface
|
||||
type TestStruct struct {
|
||||
Cpython int
|
||||
Cgo int
|
||||
}
|
||||
|
||||
func (ts TestStruct) String() string {
|
||||
return fmt.Sprintf("[%d, %d]", ts.Cpython, ts.Cgo)
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
58
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/example_test.go
generated
vendored
Normal file
58
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package gin_test
|
||||
|
||||
import (
|
||||
gintrace "github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// To start tracing requests, add the trace middleware to your Gin router.
|
||||
func Example() {
|
||||
// Create your router and use the middleware.
|
||||
r := gin.New()
|
||||
r.Use(gintrace.Middleware("my-web-app"))
|
||||
|
||||
r.GET("/hello", func(c *gin.Context) {
|
||||
c.String(200, "hello world!")
|
||||
})
|
||||
|
||||
// Profit!
|
||||
r.Run(":8080")
|
||||
}
|
||||
|
||||
func ExampleHTML() {
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("my-web-app"))
|
||||
r.LoadHTMLGlob("templates/*")
|
||||
|
||||
r.GET("/index", func(c *gin.Context) {
|
||||
// This will render the html and trace the execution time.
|
||||
gintrace.HTML(c, 200, "index.tmpl", gin.H{
|
||||
"title": "Main website",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func ExampleSpanDefault() {
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("image-encoder"))
|
||||
|
||||
r.GET("/image/encode", func(c *gin.Context) {
|
||||
// The middleware patches a span to the request. Let's add some metadata,
|
||||
// and create a child span.
|
||||
span := gintrace.SpanDefault(c)
|
||||
span.SetMeta("user.handle", "admin")
|
||||
span.SetMeta("user.id", "1234")
|
||||
|
||||
encodeSpan := tracer.NewChildSpan("image.encode", span)
|
||||
// encode a image
|
||||
encodeSpan.Finish()
|
||||
|
||||
uploadSpan := tracer.NewChildSpan("image.upload", span)
|
||||
// upload the image
|
||||
uploadSpan.Finish()
|
||||
|
||||
c.String(200, "ok!")
|
||||
})
|
||||
|
||||
}
|
143
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/gintrace.go
generated
vendored
Normal file
143
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/gintrace.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Package gin provides tracing middleware for the Gin web framework.
|
||||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// key is the string that we'll use to store spans in the tracer.
|
||||
var key = "datadog_trace_span"
|
||||
|
||||
// Middleware returns middleware that will trace requests with the default
|
||||
// tracer.
|
||||
func Middleware(service string) gin.HandlerFunc {
|
||||
return MiddlewareTracer(service, tracer.DefaultTracer)
|
||||
}
|
||||
|
||||
// MiddlewareTracer returns middleware that will trace requests with the given
|
||||
// tracer.
|
||||
func MiddlewareTracer(service string, t *tracer.Tracer) gin.HandlerFunc {
|
||||
t.SetServiceInfo(service, "gin-gonic", ext.AppTypeWeb)
|
||||
mw := newMiddleware(service, t)
|
||||
return mw.Handle
|
||||
}
|
||||
|
||||
// middleware implements gin middleware.
|
||||
type middleware struct {
|
||||
service string
|
||||
trc *tracer.Tracer
|
||||
}
|
||||
|
||||
func newMiddleware(service string, trc *tracer.Tracer) *middleware {
|
||||
return &middleware{
|
||||
service: service,
|
||||
trc: trc,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle is a gin HandlerFunc that will add tracing to the given request.
|
||||
func (m *middleware) Handle(c *gin.Context) {
|
||||
|
||||
// bail if not enabled
|
||||
if !m.trc.Enabled() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME[matt] the handler name is a bit unwieldy and uses reflection
|
||||
// under the hood. might be better to tackle this task and do it right
|
||||
// so we can end up with "user/:user/whatever" instead of
|
||||
// "github.com/foobar/blah"
|
||||
//
|
||||
// See here: https://github.com/gin-gonic/gin/issues/649
|
||||
resource := c.HandlerName()
|
||||
|
||||
// Create our span and patch it to the context for downstream.
|
||||
span := m.trc.NewRootSpan("gin.request", m.service, resource)
|
||||
c.Set(key, span)
|
||||
|
||||
// Pass along the request.
|
||||
c.Next()
|
||||
|
||||
// Set http tags.
|
||||
span.SetMeta(ext.HTTPCode, strconv.Itoa(c.Writer.Status()))
|
||||
span.SetMeta(ext.HTTPMethod, c.Request.Method)
|
||||
span.SetMeta(ext.HTTPURL, c.Request.URL.Path)
|
||||
|
||||
// Set any error information.
|
||||
var err error
|
||||
if len(c.Errors) > 0 {
|
||||
span.SetMeta("gin.errors", c.Errors.String()) // set all errors
|
||||
err = c.Errors[0] // but use the first for standard fields
|
||||
}
|
||||
span.FinishWithErr(err)
|
||||
}
|
||||
|
||||
// Span returns the Span stored in the given Context and true. If it doesn't exist,
|
||||
// it will returns (nil, false)
|
||||
func Span(c *gin.Context) (*tracer.Span, bool) {
|
||||
if c == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
s, ok := c.Get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
switch span := s.(type) {
|
||||
case *tracer.Span:
|
||||
return span, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// SpanDefault returns the span stored in the given Context. If none exists,
|
||||
// it will return an empty span.
|
||||
func SpanDefault(c *gin.Context) *tracer.Span {
|
||||
span, ok := Span(c)
|
||||
if !ok {
|
||||
return &tracer.Span{}
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
// NewChildSpan will create a span that is the child of the span stored in
|
||||
// the context.
|
||||
func NewChildSpan(name string, c *gin.Context) *tracer.Span {
|
||||
span, ok := Span(c)
|
||||
if !ok {
|
||||
return &tracer.Span{}
|
||||
}
|
||||
return span.Tracer().NewChildSpan(name, span)
|
||||
}
|
||||
|
||||
// HTML will trace the rendering of the template as a child of the span in the
|
||||
// given context.
|
||||
func HTML(c *gin.Context, code int, name string, obj interface{}) {
|
||||
span, _ := Span(c)
|
||||
if span == nil {
|
||||
c.HTML(code, name, obj)
|
||||
return
|
||||
}
|
||||
|
||||
child := span.Tracer().NewChildSpan("gin.render.html", span)
|
||||
child.SetMeta("go.template", name)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Errorf("error rendering tmpl:%s: %s", name, r)
|
||||
child.FinishWithErr(err)
|
||||
panic(r)
|
||||
} else {
|
||||
child.Finish()
|
||||
}
|
||||
}()
|
||||
|
||||
// render
|
||||
c.HTML(code, name, obj)
|
||||
}
|
250
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/gintrace_test.go
generated
vendored
Normal file
250
vendor/github.com/DataDog/dd-trace-go/contrib/gin-gonic/gin/gintrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
package gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gin.SetMode(gin.ReleaseMode) // silence annoying log msgs
|
||||
}
|
||||
|
||||
func TestChildSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, _ := getTestTracer()
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/user/:id", func(c *gin.Context) {
|
||||
span, ok := tracer.SpanFromContext(c)
|
||||
assert.True(ok)
|
||||
assert.NotNil(span)
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/user/123", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func TestTrace200(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/user/:id", func(c *gin.Context) {
|
||||
// assert we patch the span on the request context.
|
||||
span := SpanDefault(c)
|
||||
span.SetMeta("test.gin", "ginny")
|
||||
assert.Equal(span.Service, "foobar")
|
||||
id := c.Param("id")
|
||||
c.Writer.Write([]byte(id))
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/user/123", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// do and verify the request
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
|
||||
// verify traces look good
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
if len(spans) < 1 {
|
||||
t.Fatalf("no spans")
|
||||
}
|
||||
s := spans[0]
|
||||
assert.Equal(s.Service, "foobar")
|
||||
assert.Equal(s.Name, "gin.request")
|
||||
// FIXME[matt] would be much nicer to have "/user/:id" here
|
||||
assert.True(strings.Contains(s.Resource, "gin.TestTrace200"))
|
||||
assert.Equal(s.GetMeta("test.gin"), "ginny")
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), "/user/123")
|
||||
}
|
||||
|
||||
func TestDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetEnabled(false)
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
span, ok := Span(c)
|
||||
assert.Nil(span)
|
||||
assert.False(ok)
|
||||
c.Writer.Write([]byte("ok"))
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/ping", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// do and verify the request
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
|
||||
// verify traces look good
|
||||
testTracer.ForceFlush()
|
||||
spans := testTransport.Traces()
|
||||
assert.Len(spans, 0)
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
// setup
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
|
||||
// a handler with an error and make the requests
|
||||
router.GET("/err", func(c *gin.Context) {
|
||||
c.AbortWithError(500, errors.New("oh no"))
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/err", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 500)
|
||||
|
||||
// verify the errors and status are correct
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
if len(spans) < 1 {
|
||||
t.Fatalf("no spans")
|
||||
}
|
||||
s := spans[0]
|
||||
assert.Equal(s.Service, "foobar")
|
||||
assert.Equal(s.Name, "gin.request")
|
||||
assert.Equal(s.GetMeta("http.status_code"), "500")
|
||||
assert.Equal(s.GetMeta(ext.ErrorMsg), "oh no")
|
||||
assert.Equal(s.Error, int32(1))
|
||||
}
|
||||
|
||||
func TestHTML(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
// setup
|
||||
middleware := newMiddleware("tmplservice", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
|
||||
// add a template
|
||||
tmpl := template.Must(template.New("hello").Parse("hello {{.}}"))
|
||||
router.SetHTMLTemplate(tmpl)
|
||||
|
||||
// a handler with an error and make the requests
|
||||
router.GET("/hello", func(c *gin.Context) {
|
||||
HTML(c, 200, "hello", "world")
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/hello", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
assert.Equal("hello world", w.Body.String())
|
||||
|
||||
// verify the errors and status are correct
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
for _, s := range spans {
|
||||
assert.Equal(s.Service, "tmplservice")
|
||||
}
|
||||
|
||||
var tspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// we need to pick up the span we're searching for, as the
|
||||
// order is not garanteed within the buffer
|
||||
if s.Name == "gin.render.html" {
|
||||
tspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(tspan, "we should have found a span with name gin.render.html")
|
||||
assert.Equal(tspan.GetMeta("go.template"), "hello")
|
||||
fmt.Println(spans)
|
||||
}
|
||||
|
||||
func TestGetSpanNotInstrumented(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
router := gin.New()
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
// Assert we don't have a span on the context.
|
||||
s, ok := Span(c)
|
||||
assert.False(ok)
|
||||
assert.Nil(s)
|
||||
// and the default span is empty
|
||||
s = SpanDefault(c)
|
||||
assert.Equal(s.Service, "")
|
||||
c.Writer.Write([]byte("ok"))
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/ping", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
81
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/example_test.go
generated
vendored
Normal file
81
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package redis_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
redistrace "github.com/DataDog/dd-trace-go/contrib/go-redis/redis"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
// To start tracing Redis commands, use the NewTracedClient function to create a traced Redis clienty,
|
||||
// passing in a service name of choice.
|
||||
func Example() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := redistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Set("test_key", "test_value", 0)
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When set with a context, the traced client will emit a span inheriting from 'parent.request'
|
||||
c.SetContext(ctx)
|
||||
c.Set("food", "cheese", 0)
|
||||
root.Finish()
|
||||
|
||||
// Contexts can be easily passed between Datadog integrations
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("web-admin"))
|
||||
client := redistrace.NewTracedClient(opts, tracer.DefaultTracer, "redis-img-backend")
|
||||
|
||||
r.GET("/user/settings/:id", func(ctx *gin.Context) {
|
||||
// create a span that is a child of your http request
|
||||
client.SetContext(ctx)
|
||||
client.Get(fmt.Sprintf("cached_user_details_%s", ctx.Param("id")))
|
||||
})
|
||||
}
|
||||
|
||||
// You can also trace Redis Pipelines
|
||||
func Example_pipeline() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := redistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// pipe is a TracedPipeliner
|
||||
pipe := c.Pipeline()
|
||||
pipe.Incr("pipeline_counter")
|
||||
pipe.Expire("pipeline_counter", time.Hour)
|
||||
|
||||
pipe.Exec()
|
||||
}
|
||||
|
||||
func ExampleNewTracedClient() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := redistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Set("test_key", "test_value", 0)
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When set with a context, the traced client will emit a span inheriting from 'parent.request'
|
||||
c.SetContext(ctx)
|
||||
c.Set("food", "cheese", 0)
|
||||
root.Finish()
|
||||
}
|
151
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/redis.go
generated
vendored
Normal file
151
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/redis.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
// Package redis provides tracing for the go-redis Redis client (https://github.com/go-redis/redis)
|
||||
package redis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/go-redis/redis"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TracedClient is used to trace requests to a redis server.
|
||||
type TracedClient struct {
|
||||
*redis.Client
|
||||
traceParams traceParams
|
||||
}
|
||||
|
||||
// TracedPipeline is used to trace pipelines executed on a redis server.
|
||||
type TracedPipeliner struct {
|
||||
redis.Pipeliner
|
||||
traceParams traceParams
|
||||
}
|
||||
|
||||
type traceParams struct {
|
||||
host string
|
||||
port string
|
||||
db string
|
||||
service string
|
||||
tracer *tracer.Tracer
|
||||
}
|
||||
|
||||
// NewTracedClient takes a Client returned by redis.NewClient and configures it to emit spans under the given service name
|
||||
func NewTracedClient(opt *redis.Options, t *tracer.Tracer, service string) *TracedClient {
|
||||
var host, port string
|
||||
addr := strings.Split(opt.Addr, ":")
|
||||
if len(addr) == 2 && addr[1] != "" {
|
||||
port = addr[1]
|
||||
} else {
|
||||
port = "6379"
|
||||
}
|
||||
host = addr[0]
|
||||
db := strconv.Itoa(opt.DB)
|
||||
|
||||
client := redis.NewClient(opt)
|
||||
t.SetServiceInfo(service, "redis", ext.AppTypeDB)
|
||||
tc := &TracedClient{
|
||||
client,
|
||||
traceParams{
|
||||
host,
|
||||
port,
|
||||
db,
|
||||
service,
|
||||
t},
|
||||
}
|
||||
|
||||
tc.Client.WrapProcess(createWrapperFromClient(tc))
|
||||
return tc
|
||||
}
|
||||
|
||||
// Pipeline creates a TracedPipeline from a TracedClient
|
||||
func (c *TracedClient) Pipeline() *TracedPipeliner {
|
||||
return &TracedPipeliner{
|
||||
c.Client.Pipeline(),
|
||||
c.traceParams,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecWithContext calls Pipeline.Exec(). It ensures that the resulting Redis calls
|
||||
// are traced, and that emitted spans are children of the given Context
|
||||
func (c *TracedPipeliner) ExecWithContext(ctx context.Context) ([]redis.Cmder, error) {
|
||||
span := c.traceParams.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
span.Service = c.traceParams.service
|
||||
|
||||
span.SetMeta("out.host", c.traceParams.host)
|
||||
span.SetMeta("out.port", c.traceParams.port)
|
||||
span.SetMeta("out.db", c.traceParams.db)
|
||||
|
||||
cmds, err := c.Pipeliner.Exec()
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Resource = String(cmds)
|
||||
span.SetMeta("redis.pipeline_length", strconv.Itoa(len(cmds)))
|
||||
span.Finish()
|
||||
return cmds, err
|
||||
}
|
||||
|
||||
// Exec calls Pipeline.Exec() ensuring that the resulting Redis calls are traced
|
||||
func (c *TracedPipeliner) Exec() ([]redis.Cmder, error) {
|
||||
span := c.traceParams.tracer.NewRootSpan("redis.command", c.traceParams.service, "redis")
|
||||
|
||||
span.SetMeta("out.host", c.traceParams.host)
|
||||
span.SetMeta("out.port", c.traceParams.port)
|
||||
span.SetMeta("out.db", c.traceParams.db)
|
||||
|
||||
cmds, err := c.Pipeliner.Exec()
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Resource = String(cmds)
|
||||
span.SetMeta("redis.pipeline_length", strconv.Itoa(len(cmds)))
|
||||
span.Finish()
|
||||
return cmds, err
|
||||
}
|
||||
|
||||
// String returns a string representation of a slice of redis Commands, separated by newlines
|
||||
func String(cmds []redis.Cmder) string {
|
||||
var b bytes.Buffer
|
||||
for _, cmd := range cmds {
|
||||
b.WriteString(cmd.String())
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// SetContext sets a context on a TracedClient. Use it to ensure that emitted spans have the correct parent
|
||||
func (c *TracedClient) SetContext(ctx context.Context) {
|
||||
c.Client = c.Client.WithContext(ctx)
|
||||
}
|
||||
|
||||
// createWrapperFromClient wraps tracing into redis.Process().
|
||||
func createWrapperFromClient(tc *TracedClient) func(oldProcess func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(oldProcess func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(cmd redis.Cmder) error {
|
||||
ctx := tc.Client.Context()
|
||||
|
||||
var resource string
|
||||
resource = strings.Split(cmd.String(), " ")[0]
|
||||
args_length := len(strings.Split(cmd.String(), " ")) - 1
|
||||
span := tc.traceParams.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
|
||||
span.Service = tc.traceParams.service
|
||||
span.Resource = resource
|
||||
|
||||
span.SetMeta("redis.raw_command", cmd.String())
|
||||
span.SetMeta("redis.args_length", strconv.Itoa(args_length))
|
||||
span.SetMeta("out.host", tc.traceParams.host)
|
||||
span.SetMeta("out.port", tc.traceParams.port)
|
||||
span.SetMeta("out.db", tc.traceParams.db)
|
||||
|
||||
err := oldProcess(cmd)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Finish()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
228
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/redis_test.go
generated
vendored
Normal file
228
vendor/github.com/DataDog/dd-trace-go/contrib/go-redis/redis/redis_test.go
generated
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.Set("test_key", "test_value", 0)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span := spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "set test_key test_value: ")
|
||||
assert.Equal(span.GetMeta("redis.args_length"), "3")
|
||||
}
|
||||
|
||||
func TestPipeline(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
pipeline := client.Pipeline()
|
||||
pipeline.Expire("pipeline_counter", time.Hour)
|
||||
|
||||
// Exec with context test
|
||||
pipeline.ExecWithContext(context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span := spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.pipeline_length"), "1")
|
||||
assert.Equal(span.Resource, "expire pipeline_counter 3600: false\n")
|
||||
|
||||
pipeline.Expire("pipeline_counter", time.Hour)
|
||||
pipeline.Expire("pipeline_counter_1", time.Minute)
|
||||
|
||||
// Rewriting Exec
|
||||
pipeline.Exec()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces = testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span = spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("redis.pipeline_length"), "2")
|
||||
assert.Equal(span.Resource, "expire pipeline_counter 3600: false\nexpire pipeline_counter_1 60: false\n")
|
||||
}
|
||||
|
||||
func TestChildSpan(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parent_span := testTracer.NewChildSpanFromContext("parent_span", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parent_span)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.SetContext(ctx)
|
||||
|
||||
client.Set("test_key", "test_value", 0)
|
||||
parent_span.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var child_span, pspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "redis.command":
|
||||
child_span = s
|
||||
case "parent_span":
|
||||
pspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(child_span, "there should be a child redis.command span")
|
||||
assert.NotNil(child_span, "there should be a parent span")
|
||||
|
||||
assert.Equal(child_span.ParentID, pspan.SpanID)
|
||||
assert.Equal(child_span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(child_span.GetMeta("out.port"), "56379")
|
||||
}
|
||||
|
||||
func TestMultipleCommands(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.Set("test_key", "test_value", 0)
|
||||
client.Get("test_key")
|
||||
client.Incr("int_key")
|
||||
client.ClientList()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 4)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
// Checking all commands were recorded
|
||||
var commands [4]string
|
||||
for i := 0; i < 4; i++ {
|
||||
commands[i] = traces[i][0].GetMeta("redis.raw_command")
|
||||
}
|
||||
assert.Contains(commands, "set test_key test_value: ")
|
||||
assert.Contains(commands, "get test_key: ")
|
||||
assert.Contains(commands, "incr int_key: 0")
|
||||
assert.Contains(commands, "client list: ")
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
err := client.Get("non_existent_key")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Err().Error())
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "get non_existent_key: ")
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
27
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/example_test.go
generated
vendored
Normal file
27
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package gocql_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
gocqltrace "github.com/DataDog/dd-trace-go/contrib/gocql/gocql"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
// To trace Cassandra commands, use our query wrapper TraceQuery.
|
||||
func Example() {
|
||||
// Initialise a Cassandra session as usual, create a query.
|
||||
cluster := gocql.NewCluster("127.0.0.1")
|
||||
session, _ := cluster.CreateSession()
|
||||
query := session.Query("CREATE KEYSPACE if not exists trace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor': 1}")
|
||||
|
||||
// Use context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// Wrap the query to trace it and pass the context for inheritance
|
||||
tracedQuery := gocqltrace.TraceQuery("ServiceName", tracer.DefaultTracer, query)
|
||||
tracedQuery.WithContext(ctx)
|
||||
|
||||
// Execute your query as usual
|
||||
tracedQuery.Exec()
|
||||
}
|
146
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/gocql.go
generated
vendored
Normal file
146
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/gocql.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Package gocql provides tracing for the Cassandra Gocql client (https://github.com/gocql/gocql)
|
||||
package gocql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
// TracedQuery inherits from gocql.Query, it keeps the tracer and the context.
|
||||
type TracedQuery struct {
|
||||
*gocql.Query
|
||||
p traceParams
|
||||
traceContext context.Context
|
||||
}
|
||||
|
||||
// TracedIter inherits from gocql.Iter and contains a span.
|
||||
type TracedIter struct {
|
||||
*gocql.Iter
|
||||
span *tracer.Span
|
||||
}
|
||||
|
||||
// traceParams containes fields and metadata useful for command tracing
|
||||
type traceParams struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
keyspace string
|
||||
paginated string
|
||||
consistancy string
|
||||
query string
|
||||
}
|
||||
|
||||
// TraceQuery wraps a gocql.Query into a TracedQuery
|
||||
func TraceQuery(service string, tracer *tracer.Tracer, q *gocql.Query) *TracedQuery {
|
||||
stringQuery := `"` + strings.SplitN(q.String(), "\"", 3)[1] + `"`
|
||||
stringQuery, err := strconv.Unquote(stringQuery)
|
||||
if err != nil {
|
||||
// An invalid string, so that the trace is not dropped
|
||||
// due to having an empty resource
|
||||
stringQuery = "_"
|
||||
}
|
||||
|
||||
tq := &TracedQuery{q, traceParams{tracer, service, "", "false", strconv.Itoa(int(q.GetConsistency())), stringQuery}, context.Background()}
|
||||
tracer.SetServiceInfo(service, ext.CassandraType, ext.AppTypeDB)
|
||||
return tq
|
||||
}
|
||||
|
||||
// WithContext rewrites the original function so that ctx can be used for inheritance
|
||||
func (tq *TracedQuery) WithContext(ctx context.Context) *TracedQuery {
|
||||
tq.traceContext = ctx
|
||||
tq.Query.WithContext(ctx)
|
||||
return tq
|
||||
}
|
||||
|
||||
// PageState rewrites the original function so that spans are aware of the change.
|
||||
func (tq *TracedQuery) PageState(state []byte) *TracedQuery {
|
||||
tq.p.paginated = "true"
|
||||
tq.Query = tq.Query.PageState(state)
|
||||
return tq
|
||||
}
|
||||
|
||||
// NewChildSpan creates a new span from the traceParams and the context.
|
||||
func (tq *TracedQuery) NewChildSpan(ctx context.Context) *tracer.Span {
|
||||
span := tq.p.tracer.NewChildSpanFromContext(ext.CassandraQuery, ctx)
|
||||
span.Type = ext.CassandraType
|
||||
span.Service = tq.p.service
|
||||
span.Resource = tq.p.query
|
||||
span.SetMeta(ext.CassandraPaginated, tq.p.paginated)
|
||||
span.SetMeta(ext.CassandraKeyspace, tq.p.keyspace)
|
||||
return span
|
||||
}
|
||||
|
||||
// Exec is rewritten so that it passes by our custom Iter
|
||||
func (tq *TracedQuery) Exec() error {
|
||||
return tq.Iter().Close()
|
||||
}
|
||||
|
||||
// MapScan wraps in a span query.MapScan call.
|
||||
func (tq *TracedQuery) MapScan(m map[string]interface{}) error {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
err := tq.Query.MapScan(m)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Scan wraps in a span query.Scan call.
|
||||
func (tq *TracedQuery) Scan(dest ...interface{}) error {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
err := tq.Query.Scan(dest...)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ScanCAS wraps in a span query.ScanCAS call.
|
||||
func (tq *TracedQuery) ScanCAS(dest ...interface{}) (applied bool, err error) {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
applied, err = tq.Query.ScanCAS(dest...)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return applied, err
|
||||
}
|
||||
|
||||
// Iter starts a new span at query.Iter call.
|
||||
func (tq *TracedQuery) Iter() *TracedIter {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
iter := tq.Query.Iter()
|
||||
span.SetMeta(ext.CassandraRowCount, strconv.Itoa(iter.NumRows()))
|
||||
span.SetMeta(ext.CassandraConsistencyLevel, strconv.Itoa(int(tq.GetConsistency())))
|
||||
|
||||
columns := iter.Columns()
|
||||
if len(columns) > 0 {
|
||||
span.SetMeta(ext.CassandraKeyspace, columns[0].Keyspace)
|
||||
} else {
|
||||
}
|
||||
tIter := &TracedIter{iter, span}
|
||||
if tIter.Host() != nil {
|
||||
tIter.span.SetMeta(ext.TargetHost, tIter.Iter.Host().HostID())
|
||||
tIter.span.SetMeta(ext.TargetPort, strconv.Itoa(tIter.Iter.Host().Port()))
|
||||
tIter.span.SetMeta(ext.CassandraCluster, tIter.Iter.Host().DataCenter())
|
||||
|
||||
}
|
||||
return tIter
|
||||
}
|
||||
|
||||
// Close closes the TracedIter and finish the span created on Iter call.
|
||||
func (tIter *TracedIter) Close() error {
|
||||
err := tIter.Iter.Close()
|
||||
if err != nil {
|
||||
tIter.span.SetError(err)
|
||||
}
|
||||
tIter.span.Finish()
|
||||
return err
|
||||
}
|
144
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/gocql_test.go
generated
vendored
Normal file
144
vendor/github.com/DataDog/dd-trace-go/contrib/gocql/gocql/gocql_test.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
package gocql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
CASSANDRA_HOST = "127.0.0.1:59042"
|
||||
)
|
||||
|
||||
func newCassandraCluster() *gocql.ClusterConfig {
|
||||
cluster := gocql.NewCluster(CASSANDRA_HOST)
|
||||
// the InitialHostLookup must be disabled in newer versions of
|
||||
// gocql otherwise "no connections were made when creating the session"
|
||||
// error is returned for Cassandra misconfiguration (that we don't need
|
||||
// since we're testing another behavior and not the client).
|
||||
// Check: https://github.com/gocql/gocql/issues/946
|
||||
cluster.DisableInitialHostLookup = true
|
||||
return cluster
|
||||
}
|
||||
|
||||
// TestMain sets up the Keyspace and table if they do not exist
|
||||
func TestMain(m *testing.M) {
|
||||
cluster := newCassandraCluster()
|
||||
session, _ := cluster.CreateSession()
|
||||
|
||||
// Ensures test keyspace and table person exists.
|
||||
session.Query("CREATE KEYSPACE if not exists trace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor': 1}").Exec()
|
||||
session.Query("CREATE TABLE if not exists trace.person (name text PRIMARY KEY, age int, description text)").Exec()
|
||||
session.Query("INSERT INTO trace.person (name, age, description) VALUES ('Cassandra', 100, 'A cruel mistress')").Exec()
|
||||
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestErrorWrapper(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
cluster := newCassandraCluster()
|
||||
session, _ := cluster.CreateSession()
|
||||
q := session.Query("CREATE KEYSPACE trace WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 };")
|
||||
err := TraceQuery("ServiceName", testTracer, q).Exec()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Error())
|
||||
assert.Equal(span.Name, ext.CassandraQuery)
|
||||
assert.Equal(span.Resource, "CREATE KEYSPACE trace WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 };")
|
||||
assert.Equal(span.Service, "ServiceName")
|
||||
assert.Equal(span.GetMeta(ext.CassandraConsistencyLevel), "4")
|
||||
assert.Equal(span.GetMeta(ext.CassandraPaginated), "false")
|
||||
|
||||
// Not added in case of an error
|
||||
assert.Equal(span.GetMeta(ext.TargetHost), "")
|
||||
assert.Equal(span.GetMeta(ext.TargetPort), "")
|
||||
assert.Equal(span.GetMeta(ext.CassandraCluster), "")
|
||||
assert.Equal(span.GetMeta(ext.CassandraKeyspace), "")
|
||||
}
|
||||
|
||||
func TestChildWrapperSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parentSpan := testTracer.NewChildSpanFromContext("parentSpan", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parentSpan)
|
||||
|
||||
cluster := newCassandraCluster()
|
||||
session, _ := cluster.CreateSession()
|
||||
q := session.Query("SELECT * from trace.person")
|
||||
tq := TraceQuery("TestServiceName", testTracer, q)
|
||||
tq.WithContext(ctx).Exec()
|
||||
parentSpan.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var childSpan, pSpan *tracer.Span
|
||||
if spans[0].ParentID == spans[1].SpanID {
|
||||
childSpan = spans[0]
|
||||
pSpan = spans[1]
|
||||
} else {
|
||||
childSpan = spans[1]
|
||||
pSpan = spans[0]
|
||||
}
|
||||
assert.Equal(pSpan.Name, "parentSpan")
|
||||
assert.Equal(childSpan.ParentID, pSpan.SpanID)
|
||||
assert.Equal(childSpan.Name, ext.CassandraQuery)
|
||||
assert.Equal(childSpan.Resource, "SELECT * from trace.person")
|
||||
assert.Equal(childSpan.GetMeta(ext.CassandraKeyspace), "trace")
|
||||
assert.Equal(childSpan.GetMeta(ext.TargetPort), "59042")
|
||||
assert.Equal(childSpan.GetMeta(ext.TargetHost), "127.0.0.1")
|
||||
assert.Equal(childSpan.GetMeta(ext.CassandraCluster), "datacenter1")
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
11
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/compile.sh
generated
vendored
Executable file
11
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/compile.sh
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compiles test fixtures
|
||||
set -e
|
||||
protoc -I . fixtures.proto --go_out=plugins=grpc:.
|
||||
|
||||
# FIXME[matt] hacks to move the fixtures into the testing package
|
||||
# and make it pass our lint rules. This is cheesy but very simple.
|
||||
mv fixtures.pb.go fixtures_test.go
|
||||
sed -i 's/_Fixture_Ping_Handler/fixturePingHandler/' fixtures_test.go
|
||||
sed -i 's/_Fixture_serviceDesc/fixtureServiceDesc/' fixtures_test.go
|
22
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/fixtures.proto
generated
vendored
Normal file
22
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/fixtures.proto
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.grpc.examples.testgrpc";
|
||||
option java_outer_classname = "TestGRPCProto";
|
||||
|
||||
package grpc;
|
||||
|
||||
service Fixture {
|
||||
|
||||
rpc Ping (FixtureRequest) returns (FixtureReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message FixtureRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message FixtureReply {
|
||||
string message = 1;
|
||||
}
|
164
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/fixtures_test.go
generated
vendored
Normal file
164
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/fixtures_test.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// source: fixtures.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package grpc is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
fixtures.proto
|
||||
|
||||
It has these top-level messages:
|
||||
FixtureRequest
|
||||
FixtureReply
|
||||
*/
|
||||
package grpc
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// The request message containing the user's name.
|
||||
type FixtureRequest struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FixtureRequest) Reset() { *m = FixtureRequest{} }
|
||||
func (m *FixtureRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*FixtureRequest) ProtoMessage() {}
|
||||
func (*FixtureRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *FixtureRequest) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
type FixtureReply struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FixtureReply) Reset() { *m = FixtureReply{} }
|
||||
func (m *FixtureReply) String() string { return proto.CompactTextString(m) }
|
||||
func (*FixtureReply) ProtoMessage() {}
|
||||
func (*FixtureReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *FixtureReply) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*FixtureRequest)(nil), "grpc.FixtureRequest")
|
||||
proto.RegisterType((*FixtureReply)(nil), "grpc.FixtureReply")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for Fixture service
|
||||
|
||||
type FixtureClient interface {
|
||||
Ping(ctx context.Context, in *FixtureRequest, opts ...grpc.CallOption) (*FixtureReply, error)
|
||||
}
|
||||
|
||||
type fixtureClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewFixtureClient(cc *grpc.ClientConn) FixtureClient {
|
||||
return &fixtureClient{cc}
|
||||
}
|
||||
|
||||
func (c *fixtureClient) Ping(ctx context.Context, in *FixtureRequest, opts ...grpc.CallOption) (*FixtureReply, error) {
|
||||
out := new(FixtureReply)
|
||||
err := grpc.Invoke(ctx, "/grpc.Fixture/Ping", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Fixture service
|
||||
|
||||
type FixtureServer interface {
|
||||
Ping(context.Context, *FixtureRequest) (*FixtureReply, error)
|
||||
}
|
||||
|
||||
func RegisterFixtureServer(s *grpc.Server, srv FixtureServer) {
|
||||
s.RegisterService(&fixtureServiceDesc, srv)
|
||||
}
|
||||
|
||||
func fixturePingHandler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FixtureRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(FixtureServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/grpc.Fixture/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(FixtureServer).Ping(ctx, req.(*FixtureRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var fixtureServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.Fixture",
|
||||
HandlerType: (*FixtureServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: fixturePingHandler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "fixtures.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("fixtures.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 180 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcb, 0xac, 0x28,
|
||||
0x29, 0x2d, 0x4a, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2c, 0x29, 0x4a, 0x4c,
|
||||
0x4e, 0x4d, 0x2f, 0x2a, 0x48, 0x56, 0x52, 0xe1, 0xe2, 0x73, 0x83, 0x48, 0x06, 0xa5, 0x16, 0x96,
|
||||
0xa6, 0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a,
|
||||
0x70, 0x06, 0x81, 0xd9, 0x4a, 0x1a, 0x5c, 0x3c, 0x70, 0x55, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c,
|
||||
0xec, 0xb9, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x65, 0x30, 0xae, 0x91, 0x3b, 0x17, 0x3b, 0x54,
|
||||
0xa5, 0x90, 0x0d, 0x17, 0x4b, 0x40, 0x66, 0x5e, 0xba, 0x90, 0xa4, 0x1e, 0xdc, 0x3a, 0x3d, 0x54,
|
||||
0xbb, 0xa4, 0xc4, 0xb1, 0x49, 0x15, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0xe9, 0x70, 0x49, 0x66, 0xe6,
|
||||
0xeb, 0x81, 0x65, 0x52, 0x2b, 0x12, 0x73, 0x0b, 0x72, 0x52, 0x8b, 0xf5, 0x4a, 0x52, 0x8b, 0x4b,
|
||||
0x40, 0x22, 0x4e, 0xbc, 0x21, 0xa9, 0xc5, 0x25, 0xee, 0x41, 0x01, 0xce, 0x01, 0x20, 0xff, 0x04,
|
||||
0x30, 0x26, 0xb1, 0x81, 0x3d, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x68, 0x5d, 0x74, 0x4a,
|
||||
0xea, 0x00, 0x00, 0x00,
|
||||
}
|
118
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/grpc.go
generated
vendored
Normal file
118
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/grpc.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
|
||||
context "golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// pass trace ids with these headers
|
||||
const (
|
||||
traceIDKey = "x-datadog-trace-id"
|
||||
parentIDKey = "x-datadog-parent-id"
|
||||
)
|
||||
|
||||
// UnaryServerInterceptor will trace requests to the given grpc server.
|
||||
func UnaryServerInterceptor(service string, t *tracer.Tracer) grpc.UnaryServerInterceptor {
|
||||
t.SetServiceInfo(service, "grpc-server", ext.AppTypeRPC)
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
if !t.Enabled() {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
span := serverSpan(t, ctx, info.FullMethod, service)
|
||||
resp, err := handler(tracer.ContextWithSpan(ctx, span), req)
|
||||
span.FinishWithErr(err)
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// UnaryClientInterceptor will add tracing to a gprc client.
|
||||
func UnaryClientInterceptor(service string, t *tracer.Tracer) grpc.UnaryClientInterceptor {
|
||||
t.SetServiceInfo(service, "grpc-client", ext.AppTypeRPC)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
|
||||
var child *tracer.Span
|
||||
span, ok := tracer.SpanFromContext(ctx)
|
||||
|
||||
// only trace the request if this is already part of a trace.
|
||||
// does this make sense?
|
||||
if ok && span.Tracer() != nil {
|
||||
t := span.Tracer()
|
||||
child = t.NewChildSpan("grpc.client", span)
|
||||
child.SetMeta("grpc.method", method)
|
||||
ctx = setIDs(child, ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, child)
|
||||
// FIXME[matt] add the host / port information here
|
||||
// https://github.com/grpc/grpc-go/issues/951
|
||||
}
|
||||
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if child != nil {
|
||||
child.SetMeta("grpc.code", grpc.Code(err).String())
|
||||
child.FinishWithErr(err)
|
||||
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func serverSpan(t *tracer.Tracer, ctx context.Context, method, service string) *tracer.Span {
|
||||
span := t.NewRootSpan("grpc.server", service, method)
|
||||
span.SetMeta("gprc.method", method)
|
||||
span.Type = "go"
|
||||
|
||||
traceID, parentID := getIDs(ctx)
|
||||
if traceID != 0 && parentID != 0 {
|
||||
span.TraceID = traceID
|
||||
span.ParentID = parentID
|
||||
}
|
||||
|
||||
return span
|
||||
}
|
||||
|
||||
// setIDs will set the trace ids on the context{
|
||||
func setIDs(span *tracer.Span, ctx context.Context) context.Context {
|
||||
if span == nil || span.TraceID == 0 {
|
||||
return ctx
|
||||
}
|
||||
|
||||
md := metadata.New(map[string]string{
|
||||
traceIDKey: fmt.Sprint(span.TraceID),
|
||||
parentIDKey: fmt.Sprint(span.ParentID),
|
||||
})
|
||||
if existing, ok := metadata.FromContext(ctx); ok {
|
||||
md = metadata.Join(existing, md)
|
||||
}
|
||||
return metadata.NewContext(ctx, md)
|
||||
}
|
||||
|
||||
// getIDs will return ids embededd an ahe context.
|
||||
func getIDs(ctx context.Context) (traceID, parentID uint64) {
|
||||
if md, ok := metadata.FromContext(ctx); ok {
|
||||
if id := getID(md, traceIDKey); id > 0 {
|
||||
traceID = id
|
||||
}
|
||||
if id := getID(md, parentIDKey); id > 0 {
|
||||
parentID = id
|
||||
}
|
||||
}
|
||||
return traceID, parentID
|
||||
}
|
||||
|
||||
// getID parses an id from the metadata.
|
||||
func getID(md metadata.MD, name string) uint64 {
|
||||
for _, str := range md[name] {
|
||||
id, err := strconv.Atoi(str)
|
||||
if err == nil {
|
||||
return uint64(id)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
294
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/grpc_test.go
generated
vendored
Normal file
294
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc.v12/grpc_test.go
generated
vendored
Normal file
|
@ -0,0 +1,294 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
context "golang.org/x/net/context"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
client := rig.client
|
||||
|
||||
span := testTracer.NewRootSpan("a", "b", "c")
|
||||
ctx := tracer.ContextWithSpan(context.Background(), span)
|
||||
resp, err := client.Ping(ctx, &FixtureRequest{Name: "pass"})
|
||||
assert.Nil(err)
|
||||
span.Finish()
|
||||
assert.Equal(resp.Message, "passed")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
|
||||
// A word here about what is going on: this is technically a
|
||||
// distributed trace, while we're in this example in the Go world
|
||||
// and within the same exec, client could know about server details.
|
||||
// But this is not the general cases. So, as we only connect client
|
||||
// and server through their span IDs, they can be flushed as independant
|
||||
// traces. They could also be flushed at once, this is an implementation
|
||||
// detail, what is important is that all of it is flushed, at some point.
|
||||
if len(traces) == 0 {
|
||||
assert.Fail("there should be at least one trace")
|
||||
}
|
||||
var spans []*tracer.Span
|
||||
for _, trace := range traces {
|
||||
for _, span := range trace {
|
||||
spans = append(spans, span)
|
||||
}
|
||||
}
|
||||
assert.Len(spans, 3)
|
||||
|
||||
var sspan, cspan, tspan *tracer.Span
|
||||
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "grpc.server":
|
||||
sspan = s
|
||||
case "grpc.client":
|
||||
cspan = s
|
||||
case "a":
|
||||
tspan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(sspan, "there should be a span with 'grpc.server' as Name")
|
||||
|
||||
assert.NotNil(cspan, "there should be a span with 'grpc.client' as Name")
|
||||
assert.Equal(cspan.GetMeta("grpc.code"), "OK")
|
||||
|
||||
assert.NotNil(tspan, "there should be a span with 'a' as Name")
|
||||
assert.Equal(cspan.TraceID, tspan.TraceID)
|
||||
assert.Equal(sspan.TraceID, tspan.TraceID)
|
||||
}
|
||||
|
||||
func TestDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
testTracer.SetEnabled(false)
|
||||
|
||||
rig, err := newRig(testTracer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "disabled"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "disabled")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Nil(traces)
|
||||
}
|
||||
|
||||
func TestChild(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, false)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "child"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "child")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var sspan, cspan *tracer.Span
|
||||
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "grpc.server":
|
||||
sspan = s
|
||||
case "child":
|
||||
cspan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(cspan, "there should be a span with 'child' as Name")
|
||||
assert.Equal(cspan.Error, int32(0))
|
||||
assert.Equal(cspan.Service, "grpc")
|
||||
assert.Equal(cspan.Resource, "child")
|
||||
assert.True(cspan.Duration > 0)
|
||||
|
||||
assert.NotNil(sspan, "there should be a span with 'grpc.server' as Name")
|
||||
assert.Equal(sspan.Error, int32(0))
|
||||
assert.Equal(sspan.Service, "grpc")
|
||||
assert.Equal(sspan.Resource, "/grpc.Fixture/Ping")
|
||||
assert.True(sspan.Duration > 0)
|
||||
}
|
||||
|
||||
func TestPass(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, false)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "pass"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "passed")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Error, int32(0))
|
||||
assert.Equal(s.Name, "grpc.server")
|
||||
assert.Equal(s.Service, "grpc")
|
||||
assert.Equal(s.Resource, "/grpc.Fixture/Ping")
|
||||
assert.Equal(s.Type, "go")
|
||||
assert.True(s.Duration > 0)
|
||||
}
|
||||
|
||||
// fixtureServer a dummy implemenation of our grpc fixtureServer.
|
||||
type fixtureServer struct{}
|
||||
|
||||
func newFixtureServer() *fixtureServer {
|
||||
return &fixtureServer{}
|
||||
}
|
||||
|
||||
func (s *fixtureServer) Ping(ctx context.Context, in *FixtureRequest) (*FixtureReply, error) {
|
||||
switch {
|
||||
case in.Name == "child":
|
||||
span, ok := tracer.SpanFromContext(ctx)
|
||||
if ok {
|
||||
t := span.Tracer()
|
||||
t.NewChildSpan("child", span).Finish()
|
||||
}
|
||||
return &FixtureReply{Message: "child"}, nil
|
||||
case in.Name == "disabled":
|
||||
_, ok := tracer.SpanFromContext(ctx)
|
||||
if ok {
|
||||
panic("should be disabled")
|
||||
}
|
||||
return &FixtureReply{Message: "disabled"}, nil
|
||||
}
|
||||
|
||||
return &FixtureReply{Message: "passed"}, nil
|
||||
}
|
||||
|
||||
// ensure it's a fixtureServer
|
||||
var _ FixtureServer = &fixtureServer{}
|
||||
|
||||
// rig contains all of the servers and connections we'd need for a
|
||||
// grpc integration test
|
||||
type rig struct {
|
||||
server *grpc.Server
|
||||
listener net.Listener
|
||||
conn *grpc.ClientConn
|
||||
client FixtureClient
|
||||
}
|
||||
|
||||
func (r *rig) Close() {
|
||||
r.server.Stop()
|
||||
r.conn.Close()
|
||||
r.listener.Close()
|
||||
}
|
||||
|
||||
func newRig(t *tracer.Tracer, traceClient bool) (*rig, error) {
|
||||
|
||||
server := grpc.NewServer(grpc.UnaryInterceptor(UnaryServerInterceptor("grpc", t)))
|
||||
|
||||
RegisterFixtureServer(server, newFixtureServer())
|
||||
|
||||
li, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start our test fixtureServer.
|
||||
go server.Serve(li)
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithInsecure(),
|
||||
}
|
||||
|
||||
if traceClient {
|
||||
opts = append(opts, grpc.WithUnaryInterceptor(UnaryClientInterceptor("grpc", t)))
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(li.Addr().String(), opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error dialing: %s", err)
|
||||
}
|
||||
|
||||
r := &rig{
|
||||
listener: li,
|
||||
server: server,
|
||||
conn: conn,
|
||||
client: NewFixtureClient(conn),
|
||||
}
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
11
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/compile.sh
generated
vendored
Executable file
11
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/compile.sh
generated
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compiles test fixtures
|
||||
set -e
|
||||
protoc -I . fixtures.proto --go_out=plugins=grpc:.
|
||||
|
||||
# FIXME[matt] hacks to move the fixtures into the testing package
|
||||
# and make it pass our lint rules. This is cheesy but very simple.
|
||||
mv fixtures.pb.go fixtures_test.go
|
||||
sed -i 's/_Fixture_Ping_Handler/fixturePingHandler/' fixtures_test.go
|
||||
sed -i 's/_Fixture_serviceDesc/fixtureServiceDesc/' fixtures_test.go
|
22
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/fixtures.proto
generated
vendored
Normal file
22
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/fixtures.proto
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.grpc.examples.testgrpc";
|
||||
option java_outer_classname = "TestGRPCProto";
|
||||
|
||||
package grpc;
|
||||
|
||||
service Fixture {
|
||||
|
||||
rpc Ping (FixtureRequest) returns (FixtureReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message FixtureRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message FixtureReply {
|
||||
string message = 1;
|
||||
}
|
164
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/fixtures_test.go
generated
vendored
Normal file
164
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/fixtures_test.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// source: fixtures.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package grpc is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
fixtures.proto
|
||||
|
||||
It has these top-level messages:
|
||||
FixtureRequest
|
||||
FixtureReply
|
||||
*/
|
||||
package grpc
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// The request message containing the user's name.
|
||||
type FixtureRequest struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FixtureRequest) Reset() { *m = FixtureRequest{} }
|
||||
func (m *FixtureRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*FixtureRequest) ProtoMessage() {}
|
||||
func (*FixtureRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *FixtureRequest) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
type FixtureReply struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (m *FixtureReply) Reset() { *m = FixtureReply{} }
|
||||
func (m *FixtureReply) String() string { return proto.CompactTextString(m) }
|
||||
func (*FixtureReply) ProtoMessage() {}
|
||||
func (*FixtureReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *FixtureReply) GetMessage() string {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*FixtureRequest)(nil), "grpc.FixtureRequest")
|
||||
proto.RegisterType((*FixtureReply)(nil), "grpc.FixtureReply")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for Fixture service
|
||||
|
||||
type FixtureClient interface {
|
||||
Ping(ctx context.Context, in *FixtureRequest, opts ...grpc.CallOption) (*FixtureReply, error)
|
||||
}
|
||||
|
||||
type fixtureClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewFixtureClient(cc *grpc.ClientConn) FixtureClient {
|
||||
return &fixtureClient{cc}
|
||||
}
|
||||
|
||||
func (c *fixtureClient) Ping(ctx context.Context, in *FixtureRequest, opts ...grpc.CallOption) (*FixtureReply, error) {
|
||||
out := new(FixtureReply)
|
||||
err := grpc.Invoke(ctx, "/grpc.Fixture/Ping", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Fixture service
|
||||
|
||||
type FixtureServer interface {
|
||||
Ping(context.Context, *FixtureRequest) (*FixtureReply, error)
|
||||
}
|
||||
|
||||
func RegisterFixtureServer(s *grpc.Server, srv FixtureServer) {
|
||||
s.RegisterService(&fixtureServiceDesc, srv)
|
||||
}
|
||||
|
||||
func fixturePingHandler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FixtureRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(FixtureServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/grpc.Fixture/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(FixtureServer).Ping(ctx, req.(*FixtureRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var fixtureServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.Fixture",
|
||||
HandlerType: (*FixtureServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: fixturePingHandler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "fixtures.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("fixtures.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 180 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcb, 0xac, 0x28,
|
||||
0x29, 0x2d, 0x4a, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2c, 0x29, 0x4a, 0x4c,
|
||||
0x4e, 0x4d, 0x2f, 0x2a, 0x48, 0x56, 0x52, 0xe1, 0xe2, 0x73, 0x83, 0x48, 0x06, 0xa5, 0x16, 0x96,
|
||||
0xa6, 0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a,
|
||||
0x70, 0x06, 0x81, 0xd9, 0x4a, 0x1a, 0x5c, 0x3c, 0x70, 0x55, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c,
|
||||
0xec, 0xb9, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x65, 0x30, 0xae, 0x91, 0x3b, 0x17, 0x3b, 0x54,
|
||||
0xa5, 0x90, 0x0d, 0x17, 0x4b, 0x40, 0x66, 0x5e, 0xba, 0x90, 0xa4, 0x1e, 0xdc, 0x3a, 0x3d, 0x54,
|
||||
0xbb, 0xa4, 0xc4, 0xb1, 0x49, 0x15, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0xe9, 0x70, 0x49, 0x66, 0xe6,
|
||||
0xeb, 0x81, 0x65, 0x52, 0x2b, 0x12, 0x73, 0x0b, 0x72, 0x52, 0x8b, 0xf5, 0x4a, 0x52, 0x8b, 0x4b,
|
||||
0x40, 0x22, 0x4e, 0xbc, 0x21, 0xa9, 0xc5, 0x25, 0xee, 0x41, 0x01, 0xce, 0x01, 0x20, 0xff, 0x04,
|
||||
0x30, 0x26, 0xb1, 0x81, 0x3d, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x68, 0x5d, 0x74, 0x4a,
|
||||
0xea, 0x00, 0x00, 0x00,
|
||||
}
|
118
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/grpc.go
generated
vendored
Normal file
118
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/grpc.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
|
||||
context "golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// pass trace ids with these headers
|
||||
const (
|
||||
traceIDKey = "x-datadog-trace-id"
|
||||
parentIDKey = "x-datadog-parent-id"
|
||||
)
|
||||
|
||||
// UnaryServerInterceptor will trace requests to the given grpc server.
|
||||
func UnaryServerInterceptor(service string, t *tracer.Tracer) grpc.UnaryServerInterceptor {
|
||||
t.SetServiceInfo(service, "grpc-server", ext.AppTypeRPC)
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
if !t.Enabled() {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
span := serverSpan(t, ctx, info.FullMethod, service)
|
||||
resp, err := handler(tracer.ContextWithSpan(ctx, span), req)
|
||||
span.FinishWithErr(err)
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// UnaryClientInterceptor will add tracing to a gprc client.
|
||||
func UnaryClientInterceptor(service string, t *tracer.Tracer) grpc.UnaryClientInterceptor {
|
||||
t.SetServiceInfo(service, "grpc-client", ext.AppTypeRPC)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
|
||||
var child *tracer.Span
|
||||
span, ok := tracer.SpanFromContext(ctx)
|
||||
|
||||
// only trace the request if this is already part of a trace.
|
||||
// does this make sense?
|
||||
if ok && span.Tracer() != nil {
|
||||
t := span.Tracer()
|
||||
child = t.NewChildSpan("grpc.client", span)
|
||||
child.SetMeta("grpc.method", method)
|
||||
ctx = setIDs(child, ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, child)
|
||||
// FIXME[matt] add the host / port information here
|
||||
// https://github.com/grpc/grpc-go/issues/951
|
||||
}
|
||||
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if child != nil {
|
||||
child.SetMeta("grpc.code", grpc.Code(err).String())
|
||||
child.FinishWithErr(err)
|
||||
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func serverSpan(t *tracer.Tracer, ctx context.Context, method, service string) *tracer.Span {
|
||||
span := t.NewRootSpan("grpc.server", service, method)
|
||||
span.SetMeta("gprc.method", method)
|
||||
span.Type = "go"
|
||||
|
||||
traceID, parentID := getIDs(ctx)
|
||||
if traceID != 0 && parentID != 0 {
|
||||
span.TraceID = traceID
|
||||
span.ParentID = parentID
|
||||
}
|
||||
|
||||
return span
|
||||
}
|
||||
|
||||
// setIDs will set the trace ids on the context{
|
||||
func setIDs(span *tracer.Span, ctx context.Context) context.Context {
|
||||
if span == nil || span.TraceID == 0 {
|
||||
return ctx
|
||||
}
|
||||
|
||||
md := metadata.New(map[string]string{
|
||||
traceIDKey: fmt.Sprint(span.TraceID),
|
||||
parentIDKey: fmt.Sprint(span.ParentID),
|
||||
})
|
||||
if existing, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
md = metadata.Join(existing, md)
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
// getIDs will return ids embededd an ahe context.
|
||||
func getIDs(ctx context.Context) (traceID, parentID uint64) {
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
if id := getID(md, traceIDKey); id > 0 {
|
||||
traceID = id
|
||||
}
|
||||
if id := getID(md, parentIDKey); id > 0 {
|
||||
parentID = id
|
||||
}
|
||||
}
|
||||
return traceID, parentID
|
||||
}
|
||||
|
||||
// getID parses an id from the metadata.
|
||||
func getID(md metadata.MD, name string) uint64 {
|
||||
for _, str := range md[name] {
|
||||
id, err := strconv.Atoi(str)
|
||||
if err == nil {
|
||||
return uint64(id)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
294
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/grpc_test.go
generated
vendored
Normal file
294
vendor/github.com/DataDog/dd-trace-go/contrib/google.golang.org/grpc/grpc_test.go
generated
vendored
Normal file
|
@ -0,0 +1,294 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
context "golang.org/x/net/context"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
client := rig.client
|
||||
|
||||
span := testTracer.NewRootSpan("a", "b", "c")
|
||||
ctx := tracer.ContextWithSpan(context.Background(), span)
|
||||
resp, err := client.Ping(ctx, &FixtureRequest{Name: "pass"})
|
||||
assert.Nil(err)
|
||||
span.Finish()
|
||||
assert.Equal(resp.Message, "passed")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
|
||||
// A word here about what is going on: this is technically a
|
||||
// distributed trace, while we're in this example in the Go world
|
||||
// and within the same exec, client could know about server details.
|
||||
// But this is not the general cases. So, as we only connect client
|
||||
// and server through their span IDs, they can be flushed as independant
|
||||
// traces. They could also be flushed at once, this is an implementation
|
||||
// detail, what is important is that all of it is flushed, at some point.
|
||||
if len(traces) == 0 {
|
||||
assert.Fail("there should be at least one trace")
|
||||
}
|
||||
var spans []*tracer.Span
|
||||
for _, trace := range traces {
|
||||
for _, span := range trace {
|
||||
spans = append(spans, span)
|
||||
}
|
||||
}
|
||||
assert.Len(spans, 3)
|
||||
|
||||
var sspan, cspan, tspan *tracer.Span
|
||||
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "grpc.server":
|
||||
sspan = s
|
||||
case "grpc.client":
|
||||
cspan = s
|
||||
case "a":
|
||||
tspan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(sspan, "there should be a span with 'grpc.server' as Name")
|
||||
|
||||
assert.NotNil(cspan, "there should be a span with 'grpc.client' as Name")
|
||||
assert.Equal(cspan.GetMeta("grpc.code"), "OK")
|
||||
|
||||
assert.NotNil(tspan, "there should be a span with 'a' as Name")
|
||||
assert.Equal(cspan.TraceID, tspan.TraceID)
|
||||
assert.Equal(sspan.TraceID, tspan.TraceID)
|
||||
}
|
||||
|
||||
func TestDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
testTracer.SetEnabled(false)
|
||||
|
||||
rig, err := newRig(testTracer, true)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "disabled"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "disabled")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Nil(traces)
|
||||
}
|
||||
|
||||
func TestChild(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, false)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "child"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "child")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var sspan, cspan *tracer.Span
|
||||
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "grpc.server":
|
||||
sspan = s
|
||||
case "child":
|
||||
cspan = s
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(cspan, "there should be a span with 'child' as Name")
|
||||
assert.Equal(cspan.Error, int32(0))
|
||||
assert.Equal(cspan.Service, "grpc")
|
||||
assert.Equal(cspan.Resource, "child")
|
||||
assert.True(cspan.Duration > 0)
|
||||
|
||||
assert.NotNil(sspan, "there should be a span with 'grpc.server' as Name")
|
||||
assert.Equal(sspan.Error, int32(0))
|
||||
assert.Equal(sspan.Service, "grpc")
|
||||
assert.Equal(sspan.Resource, "/grpc.Fixture/Ping")
|
||||
assert.True(sspan.Duration > 0)
|
||||
}
|
||||
|
||||
func TestPass(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
rig, err := newRig(testTracer, false)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up rig: %s", err)
|
||||
}
|
||||
defer rig.Close()
|
||||
|
||||
client := rig.client
|
||||
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "pass"})
|
||||
assert.Nil(err)
|
||||
assert.Equal(resp.Message, "passed")
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Error, int32(0))
|
||||
assert.Equal(s.Name, "grpc.server")
|
||||
assert.Equal(s.Service, "grpc")
|
||||
assert.Equal(s.Resource, "/grpc.Fixture/Ping")
|
||||
assert.Equal(s.Type, "go")
|
||||
assert.True(s.Duration > 0)
|
||||
}
|
||||
|
||||
// fixtureServer a dummy implemenation of our grpc fixtureServer.
|
||||
type fixtureServer struct{}
|
||||
|
||||
func newFixtureServer() *fixtureServer {
|
||||
return &fixtureServer{}
|
||||
}
|
||||
|
||||
func (s *fixtureServer) Ping(ctx context.Context, in *FixtureRequest) (*FixtureReply, error) {
|
||||
switch {
|
||||
case in.Name == "child":
|
||||
span, ok := tracer.SpanFromContext(ctx)
|
||||
if ok {
|
||||
t := span.Tracer()
|
||||
t.NewChildSpan("child", span).Finish()
|
||||
}
|
||||
return &FixtureReply{Message: "child"}, nil
|
||||
case in.Name == "disabled":
|
||||
_, ok := tracer.SpanFromContext(ctx)
|
||||
if ok {
|
||||
panic("should be disabled")
|
||||
}
|
||||
return &FixtureReply{Message: "disabled"}, nil
|
||||
}
|
||||
|
||||
return &FixtureReply{Message: "passed"}, nil
|
||||
}
|
||||
|
||||
// ensure it's a fixtureServer
|
||||
var _ FixtureServer = &fixtureServer{}
|
||||
|
||||
// rig contains all of the servers and connections we'd need for a
|
||||
// grpc integration test
|
||||
type rig struct {
|
||||
server *grpc.Server
|
||||
listener net.Listener
|
||||
conn *grpc.ClientConn
|
||||
client FixtureClient
|
||||
}
|
||||
|
||||
func (r *rig) Close() {
|
||||
r.server.Stop()
|
||||
r.conn.Close()
|
||||
r.listener.Close()
|
||||
}
|
||||
|
||||
func newRig(t *tracer.Tracer, traceClient bool) (*rig, error) {
|
||||
|
||||
server := grpc.NewServer(grpc.UnaryInterceptor(UnaryServerInterceptor("grpc", t)))
|
||||
|
||||
RegisterFixtureServer(server, newFixtureServer())
|
||||
|
||||
li, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start our test fixtureServer.
|
||||
go server.Serve(li)
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithInsecure(),
|
||||
}
|
||||
|
||||
if traceClient {
|
||||
opts = append(opts, grpc.WithUnaryInterceptor(UnaryClientInterceptor("grpc", t)))
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(li.Addr().String(), opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error dialing: %s", err)
|
||||
}
|
||||
|
||||
r := &rig{
|
||||
listener: li,
|
||||
server: server,
|
||||
conn: conn,
|
||||
client: NewFixtureClient(conn),
|
||||
}
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
31
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/example_test.go
generated
vendored
Normal file
31
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package mux_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
muxtrace "github.com/DataDog/dd-trace-go/contrib/gorilla/mux"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// handler is a simple handlerFunc that logs some data from the span
|
||||
// that is injected into the requests' context.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
fmt.Printf("tracing service:%s resource:%s", span.Service, span.Resource)
|
||||
w.Write([]byte("hello world"))
|
||||
}
|
||||
|
||||
func Example() {
|
||||
router := mux.NewRouter()
|
||||
muxTracer := muxtrace.NewMuxTracer("my-web-app", tracer.DefaultTracer)
|
||||
|
||||
// Add traced routes directly.
|
||||
muxTracer.HandleFunc(router, "/users", handler)
|
||||
|
||||
// and subroutes as well.
|
||||
subrouter := router.PathPrefix("/user").Subrouter()
|
||||
muxTracer.HandleFunc(subrouter, "/view", handler)
|
||||
muxTracer.HandleFunc(subrouter, "/create", handler)
|
||||
}
|
127
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/mux.go
generated
vendored
Normal file
127
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/mux.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Package mux provides tracing functions for the Gorilla Mux framework.
|
||||
package mux
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// MuxTracer is used to trace requests in a mux server.
|
||||
type MuxTracer struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
}
|
||||
|
||||
// NewMuxTracer creates a MuxTracer for the given service and tracer.
|
||||
func NewMuxTracer(service string, t *tracer.Tracer) *MuxTracer {
|
||||
t.SetServiceInfo(service, "gorilla", ext.AppTypeWeb)
|
||||
return &MuxTracer{
|
||||
tracer: t,
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// TraceHandleFunc will return a HandlerFunc that will wrap tracing around the
|
||||
// given handler func.
|
||||
func (m *MuxTracer) TraceHandleFunc(handler http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
return func(writer http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// bail our if tracing isn't enabled.
|
||||
if !m.tracer.Enabled() {
|
||||
handler(writer, req)
|
||||
return
|
||||
}
|
||||
|
||||
// trace the request
|
||||
tracedRequest, span := m.trace(req)
|
||||
defer span.Finish()
|
||||
|
||||
// trace the response
|
||||
tracedWriter := newTracedResponseWriter(span, writer)
|
||||
|
||||
// run the request
|
||||
handler(tracedWriter, tracedRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleFunc will add a traced version of the given handler to the router.
|
||||
func (m *MuxTracer) HandleFunc(router *mux.Router, pattern string, handler http.HandlerFunc) *mux.Route {
|
||||
return router.HandleFunc(pattern, m.TraceHandleFunc(handler))
|
||||
}
|
||||
|
||||
// span will create a span for the given request.
|
||||
func (m *MuxTracer) trace(req *http.Request) (*http.Request, *tracer.Span) {
|
||||
route := mux.CurrentRoute(req)
|
||||
path, err := route.GetPathTemplate()
|
||||
if err != nil {
|
||||
// when route doesn't define a path
|
||||
path = "unknown"
|
||||
}
|
||||
|
||||
resource := req.Method + " " + path
|
||||
|
||||
span := m.tracer.NewRootSpan("mux.request", m.service, resource)
|
||||
span.Type = ext.HTTPType
|
||||
span.SetMeta(ext.HTTPMethod, req.Method)
|
||||
span.SetMeta(ext.HTTPURL, path)
|
||||
|
||||
// patch the span onto the request context.
|
||||
treq := SetRequestSpan(req, span)
|
||||
return treq, span
|
||||
}
|
||||
|
||||
// tracedResponseWriter is a small wrapper around an http response writer that will
|
||||
// intercept and store the status of a request.
|
||||
type tracedResponseWriter struct {
|
||||
span *tracer.Span
|
||||
w http.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
func newTracedResponseWriter(span *tracer.Span, w http.ResponseWriter) *tracedResponseWriter {
|
||||
return &tracedResponseWriter{
|
||||
span: span,
|
||||
w: w}
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) Header() http.Header {
|
||||
return t.w.Header()
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) Write(b []byte) (int, error) {
|
||||
if t.status == 0 {
|
||||
t.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return t.w.Write(b)
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) WriteHeader(status int) {
|
||||
t.w.WriteHeader(status)
|
||||
t.status = status
|
||||
t.span.SetMeta(ext.HTTPCode, strconv.Itoa(status))
|
||||
if status >= 500 && status < 600 {
|
||||
t.span.Error = 1
|
||||
}
|
||||
}
|
||||
|
||||
// SetRequestSpan sets the span on the request's context.
|
||||
func SetRequestSpan(r *http.Request, span *tracer.Span) *http.Request {
|
||||
if r == nil || span == nil {
|
||||
return r
|
||||
}
|
||||
|
||||
ctx := tracer.ContextWithSpan(r.Context(), span)
|
||||
return r.WithContext(ctx)
|
||||
}
|
||||
|
||||
// GetRequestSpan will return the span associated with the given request. It
|
||||
// will return nil/false if it doesn't exist.
|
||||
func GetRequestSpan(r *http.Request) (*tracer.Span, bool) {
|
||||
span, ok := tracer.SpanFromContext(r.Context())
|
||||
return span, ok
|
||||
}
|
206
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/mux_test.go
generated
vendored
Normal file
206
vendor/github.com/DataDog/dd-trace-go/contrib/gorilla/mux/mux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
package mux
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMuxTracerDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testTracer, testTransport, muxTracer := getTestTracer("disabled-service")
|
||||
router := mux.NewRouter()
|
||||
muxTracer.HandleFunc(router, "/disabled", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("disabled!"))
|
||||
assert.Nil(err)
|
||||
// Ensure we have no tracing context.
|
||||
span, ok := tracer.SpanFromContext(r.Context())
|
||||
assert.Nil(span)
|
||||
assert.False(ok)
|
||||
})
|
||||
testTracer.SetEnabled(false) // the key line in this test.
|
||||
|
||||
// make the request
|
||||
req := httptest.NewRequest("GET", "/disabled", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "disabled!")
|
||||
|
||||
// assert nothing was traced.
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 0)
|
||||
}
|
||||
|
||||
func TestMuxTracerSubrequest(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Send and verify a 200 request
|
||||
for _, url := range []string{"/sub/child1", "/sub/child2"} {
|
||||
tracer, transport, router := setup(t)
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "200!")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMuxTracer200(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// setup
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// Send and verify a 200 request
|
||||
url := "/200"
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "200!")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(0))
|
||||
}
|
||||
|
||||
func TestMuxTracer500(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// setup
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// SEnd and verify a 200 request
|
||||
url := "/500"
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 500)
|
||||
assert.Equal(writer.Body.String(), "500!\n")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "500")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(1))
|
||||
}
|
||||
|
||||
// test handlers
|
||||
|
||||
func handler200(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("200!"))
|
||||
assert.Nil(err)
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Duration, int64(0))
|
||||
}
|
||||
}
|
||||
|
||||
func handler500(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "500!", http.StatusInternalServerError)
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Duration, int64(0))
|
||||
}
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (*tracer.Tracer, *dummyTransport, *mux.Router) {
|
||||
tracer, transport, mt := getTestTracer("my-service")
|
||||
r := mux.NewRouter()
|
||||
|
||||
h200 := handler200(t)
|
||||
h500 := handler500(t)
|
||||
|
||||
// Ensure we can use HandleFunc and it returns a route
|
||||
mt.HandleFunc(r, "/200", h200).Methods("Get")
|
||||
// And we can allso handle a bare func
|
||||
r.HandleFunc("/500", mt.TraceHandleFunc(h500))
|
||||
|
||||
// do a subrouter (one in each way)
|
||||
sub := r.PathPrefix("/sub").Subrouter()
|
||||
sub.HandleFunc("/child1", mt.TraceHandleFunc(h200))
|
||||
mt.HandleFunc(sub, "/child2", h200)
|
||||
|
||||
return tracer, transport, r
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer(service string) (*tracer.Tracer, *dummyTransport, *MuxTracer) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
muxTracer := NewMuxTracer(service, tracer)
|
||||
return tracer, transport, muxTracer
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
23
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/example_test.go
generated
vendored
Normal file
23
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package sqlx_test
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
|
||||
sqlxtrace "github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// The API to trace sqlx calls is the same as sqltraced.
|
||||
// See https://godoc.org/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced for more information on how to use it.
|
||||
func Example() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sqlx.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, _ := sqlxtrace.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
|
||||
// All calls through sqlx API will then be traced.
|
||||
query, args, _ := sqlx.In("SELECT * FROM users WHERE level IN (?);", []int{4, 6, 7})
|
||||
query = db.Rebind(query)
|
||||
rows, _ := db.Query(query, args...)
|
||||
defer rows.Close()
|
||||
}
|
41
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/mysql_test.go
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/mysql_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sqlx
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/sqltest"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func TestMySQL(t *testing.T) {
|
||||
trc, transport := tracertest.GetTestTracer()
|
||||
dbx, err := OpenTraced(&mysql.MySQLDriver{}, "test:test@tcp(127.0.0.1:53306)/test", "mysql-test", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer dbx.Close()
|
||||
|
||||
testDB := &sqltest.DB{
|
||||
DB: dbx.DB,
|
||||
Tracer: trc,
|
||||
Transport: transport,
|
||||
DriverName: "mysql",
|
||||
}
|
||||
|
||||
expectedSpan := &tracer.Span{
|
||||
Name: "mysql.query",
|
||||
Service: "mysql-test",
|
||||
Type: "sql",
|
||||
}
|
||||
expectedSpan.Meta = map[string]string{
|
||||
"db.user": "test",
|
||||
"out.host": "127.0.0.1",
|
||||
"out.port": "53306",
|
||||
"db.name": "test",
|
||||
}
|
||||
|
||||
sqltest.AllSQLTests(t, testDB, expectedSpan)
|
||||
}
|
41
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/pq_test.go
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/pq_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sqlx
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/sqltest"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func TestPostgres(t *testing.T) {
|
||||
trc, transport := tracertest.GetTestTracer()
|
||||
dbx, err := OpenTraced(&pq.Driver{}, "postgres://postgres:postgres@127.0.0.1:55432/postgres?sslmode=disable", "postgres-test", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer dbx.Close()
|
||||
|
||||
testDB := &sqltest.DB{
|
||||
DB: dbx.DB,
|
||||
Tracer: trc,
|
||||
Transport: transport,
|
||||
DriverName: "postgres",
|
||||
}
|
||||
|
||||
expectedSpan := &tracer.Span{
|
||||
Name: "postgres.query",
|
||||
Service: "postgres-test",
|
||||
Type: "sql",
|
||||
}
|
||||
expectedSpan.Meta = map[string]string{
|
||||
"db.user": "postgres",
|
||||
"out.host": "127.0.0.1",
|
||||
"out.port": "55432",
|
||||
"db.name": "postgres",
|
||||
}
|
||||
|
||||
sqltest.AllSQLTests(t, testDB, expectedSpan)
|
||||
}
|
35
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/sql.go
generated
vendored
Normal file
35
vendor/github.com/DataDog/dd-trace-go/contrib/jmoiron/sqlx/sql.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Package sqlxtraced provides a traced version of the "jmoiron/sqlx" package
|
||||
// For more information about the API, see https://godoc.org/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced.
|
||||
package sqlx
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/sqlutils"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// OpenTraced will first register the traced version of the `driver` if not yet registered and will then open a connection with it.
|
||||
// This is usually the only function to use when there is no need for the granularity offered by Register and Open.
|
||||
// The last argument is optional and allows you to pass a custom tracer.
|
||||
func OpenTraced(driver driver.Driver, dataSourceName, service string, trcv ...*tracer.Tracer) (*sqlx.DB, error) {
|
||||
driverName := sqlutils.GetDriverName(driver)
|
||||
Register(driverName, driver, trcv...)
|
||||
return Open(driverName, dataSourceName, service)
|
||||
}
|
||||
|
||||
// Register registers a traced version of `driver`.
|
||||
func Register(driverName string, driver driver.Driver, trcv ...*tracer.Tracer) {
|
||||
sqltraced.Register(driverName, driver, trcv...)
|
||||
}
|
||||
|
||||
// Open returns a traced version of *sqlx.DB.
|
||||
func Open(driverName, dataSourceName, service string) (*sqlx.DB, error) {
|
||||
db, err := sqltraced.Open(driverName, dataSourceName, service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sqlx.NewDb(db, driverName), err
|
||||
}
|
17
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/example_test.go
generated
vendored
Normal file
17
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package http_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
httptrace "github.com/DataDog/dd-trace-go/contrib/net/http"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("Hello World!\n"))
|
||||
}
|
||||
|
||||
func Example() {
|
||||
mux := httptrace.NewServeMux("web-service", nil)
|
||||
mux.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8080", mux)
|
||||
}
|
93
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/http.go
generated
vendored
Normal file
93
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/http.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
)
|
||||
|
||||
// ServeMux is an HTTP request multiplexer that traces all the incoming requests.
|
||||
type ServeMux struct {
|
||||
*http.ServeMux
|
||||
*tracer.Tracer
|
||||
service string
|
||||
}
|
||||
|
||||
// NewServeMux allocates and returns a new ServeMux.
|
||||
func NewServeMux(service string, t *tracer.Tracer) *ServeMux {
|
||||
if t == nil {
|
||||
t = tracer.DefaultTracer
|
||||
}
|
||||
t.SetServiceInfo(service, "net/http", ext.AppTypeWeb)
|
||||
return &ServeMux{http.NewServeMux(), t, service}
|
||||
}
|
||||
|
||||
// ServeHTTP dispatches the request to the handler whose
|
||||
// pattern most closely matches the request URL.
|
||||
// We only needed to rewrite this method to be able to trace the multiplexer.
|
||||
func (mux *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// bail out if tracing isn't enabled
|
||||
if !mux.Tracer.Enabled() {
|
||||
mux.ServeMux.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// get the route associated to this request
|
||||
_, route := mux.Handler(r)
|
||||
|
||||
// create a new span
|
||||
resource := r.Method + " " + route
|
||||
span := mux.Tracer.NewRootSpan("http.request", mux.service, resource)
|
||||
defer span.Finish()
|
||||
|
||||
span.Type = ext.HTTPType
|
||||
span.SetMeta(ext.HTTPMethod, r.Method)
|
||||
span.SetMeta(ext.HTTPURL, r.URL.Path)
|
||||
|
||||
// pass the span through the request context
|
||||
ctx := span.Context(r.Context())
|
||||
traceRequest := r.WithContext(ctx)
|
||||
|
||||
// trace the response to get the status code
|
||||
traceWriter := NewResponseWriter(w, span)
|
||||
|
||||
// serve the request to the underlying multiplexer
|
||||
mux.ServeMux.ServeHTTP(traceWriter, traceRequest)
|
||||
}
|
||||
|
||||
// ResponseWriter is a small wrapper around an http response writer that will
|
||||
// intercept and store the status of a request.
|
||||
// It implements the ResponseWriter interface.
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
span *tracer.Span
|
||||
status int
|
||||
}
|
||||
|
||||
// New ResponseWriter allocateds and returns a new ResponseWriter.
|
||||
func NewResponseWriter(w http.ResponseWriter, span *tracer.Span) *ResponseWriter {
|
||||
return &ResponseWriter{w, span, 0}
|
||||
}
|
||||
|
||||
// Write writes the data to the connection as part of an HTTP reply.
|
||||
// We explicitely call WriteHeader with the 200 status code
|
||||
// in order to get it reported into the span.
|
||||
func (w *ResponseWriter) Write(b []byte) (int, error) {
|
||||
if w.status == 0 {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
// WriteHeader sends an HTTP response header with status code.
|
||||
// It also sets the status code to the span.
|
||||
func (w *ResponseWriter) WriteHeader(status int) {
|
||||
w.ResponseWriter.WriteHeader(status)
|
||||
w.status = status
|
||||
w.span.SetMeta(ext.HTTPCode, strconv.Itoa(status))
|
||||
if status >= 500 && status < 600 {
|
||||
w.span.Error = 1
|
||||
}
|
||||
}
|
134
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/http_test.go
generated
vendored
Normal file
134
vendor/github.com/DataDog/dd-trace-go/contrib/net/http/http_test.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHttpTracerDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testTracer, testTransport := tracertest.GetTestTracer()
|
||||
testTracer.SetEnabled(false)
|
||||
|
||||
mux := NewServeMux("my-service", testTracer)
|
||||
mux.HandleFunc("/disabled", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("disabled!"))
|
||||
assert.Nil(err)
|
||||
|
||||
// Ensure we have no tracing context
|
||||
span, ok := tracer.SpanFromContext(r.Context())
|
||||
assert.Nil(span)
|
||||
assert.False(ok)
|
||||
})
|
||||
|
||||
// Make the request
|
||||
r := httptest.NewRequest("GET", "/disabled", nil)
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, r)
|
||||
assert.Equal(200, w.Code)
|
||||
assert.Equal("disabled!", w.Body.String())
|
||||
|
||||
// Assert nothing was traced
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Equal(0, len(traces))
|
||||
}
|
||||
|
||||
func TestHttpTracer200(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// Send and verify a 200 request
|
||||
url := "/200"
|
||||
r := httptest.NewRequest("GET", url, nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
assert.Equal(200, w.Code)
|
||||
assert.Equal("200!\n", w.Body.String())
|
||||
|
||||
// Ensure the request is properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Equal(1, len(traces))
|
||||
spans := traces[0]
|
||||
assert.Equal(1, len(spans))
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal("http.request", s.Name)
|
||||
assert.Equal("my-service", s.Service)
|
||||
assert.Equal("GET "+url, s.Resource)
|
||||
assert.Equal("200", s.GetMeta("http.status_code"))
|
||||
assert.Equal("GET", s.GetMeta("http.method"))
|
||||
assert.Equal(url, s.GetMeta("http.url"))
|
||||
assert.Equal(int32(0), s.Error)
|
||||
}
|
||||
|
||||
func TestHttpTracer500(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// Send and verify a 500 request
|
||||
url := "/500"
|
||||
r := httptest.NewRequest("GET", url, nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
assert.Equal(500, w.Code)
|
||||
assert.Equal("500!\n", w.Body.String())
|
||||
|
||||
// Ensure the request is properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Equal(1, len(traces))
|
||||
spans := traces[0]
|
||||
assert.Equal(1, len(spans))
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal("http.request", s.Name)
|
||||
assert.Equal("my-service", s.Service)
|
||||
assert.Equal("GET "+url, s.Resource)
|
||||
assert.Equal("500", s.GetMeta("http.status_code"))
|
||||
assert.Equal("GET", s.GetMeta("http.method"))
|
||||
assert.Equal(url, s.GetMeta("http.url"))
|
||||
assert.Equal(int32(1), s.Error)
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (*tracer.Tracer, *tracertest.DummyTransport, http.Handler) {
|
||||
h200 := handler200(t)
|
||||
h500 := handler500(t)
|
||||
tracer, transport := tracertest.GetTestTracer()
|
||||
|
||||
mux := NewServeMux("my-service", tracer)
|
||||
mux.HandleFunc("/200", h200)
|
||||
mux.HandleFunc("/500", h500)
|
||||
|
||||
return tracer, transport, mux
|
||||
}
|
||||
|
||||
func handler200(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("200!\n"))
|
||||
assert.Nil(err)
|
||||
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
assert.Equal("my-service", span.Service)
|
||||
assert.Equal(int64(0), span.Duration)
|
||||
}
|
||||
}
|
||||
|
||||
func handler500(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "500!", http.StatusInternalServerError)
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
|
||||
assert.Equal("my-service", span.Service)
|
||||
assert.Equal(int64(0), span.Duration)
|
||||
}
|
||||
}
|
86
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/elastictrace.go
generated
vendored
Normal file
86
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/elastictrace.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Package elastictrace provides tracing for the Elastic Elasticsearch client.
|
||||
// Supports v3 (gopkg.in/olivere/elastic.v3), v5 (gopkg.in/olivere/elastic.v5)
|
||||
// but with v3 you must use `DoC` on all requests to capture the request context.
|
||||
package elastictrace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
)
|
||||
|
||||
// MaxContentLength is the maximum content length for which we'll read and capture
|
||||
// the contents of the request body. Anything larger will still be traced but the
|
||||
// body will not be captured as trace metadata.
|
||||
const MaxContentLength = 500 * 1024
|
||||
|
||||
// TracedTransport is a traced HTTP transport that captures Elasticsearch spans.
|
||||
type TracedTransport struct {
|
||||
service string
|
||||
tracer *tracer.Tracer
|
||||
*http.Transport
|
||||
}
|
||||
|
||||
// RoundTrip satisfies the RoundTripper interface, wraps the sub Transport and
|
||||
// captures a span of the Elasticsearch request.
|
||||
func (t *TracedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
span := t.tracer.NewChildSpanFromContext("elasticsearch.query", req.Context())
|
||||
span.Service = t.service
|
||||
span.Type = ext.AppTypeDB
|
||||
defer span.Finish()
|
||||
span.SetMeta("elasticsearch.method", req.Method)
|
||||
span.SetMeta("elasticsearch.url", req.URL.Path)
|
||||
span.SetMeta("elasticsearch.params", req.URL.Query().Encode())
|
||||
|
||||
contentLength, _ := strconv.Atoi(req.Header.Get("Content-Length"))
|
||||
if req.Body != nil && contentLength < MaxContentLength {
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
span.SetMeta("elasticsearch.body", string(buf))
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||
}
|
||||
|
||||
// Run the request using the standard transport.
|
||||
res, err := t.Transport.RoundTrip(req)
|
||||
if res != nil {
|
||||
span.SetMeta(ext.HTTPCode, strconv.Itoa(res.StatusCode))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
} else if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
buf, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
// Status text is best we can do if if we can't read the body.
|
||||
span.SetError(errors.New(http.StatusText(res.StatusCode)))
|
||||
} else {
|
||||
span.SetError(errors.New(string(buf)))
|
||||
}
|
||||
res.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||
}
|
||||
Quantize(span)
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// NewTracedHTTPClient returns a new TracedTransport that traces HTTP requests.
|
||||
func NewTracedHTTPClient(service string, tracer *tracer.Tracer) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &TracedTransport{service, tracer, &http.Transport{}},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTracedHTTPClientWithTransport returns a new TracedTransport that traces HTTP requests
|
||||
// and takes in a Transport to use something other than the default.
|
||||
func NewTracedHTTPClientWithTransport(service string, tracer *tracer.Tracer, transport *http.Transport) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &TracedTransport{service, tracer, transport},
|
||||
}
|
||||
}
|
193
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/elastictrace_test.go
generated
vendored
Normal file
193
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/elastictrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
|||
package elastictrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
elasticv3 "gopkg.in/olivere/elastic.v3"
|
||||
elasticv5 "gopkg.in/olivere/elastic.v5"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClientV5(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv5.NewClient(
|
||||
elasticv5.SetURL("http://127.0.0.1:59200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
elasticv5.SetSniff(false),
|
||||
elasticv5.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkPUTTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("twitter").Type("tweet").
|
||||
Id("1").Do(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkGETTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("not-real-index").
|
||||
Id("1").Do(context.TODO())
|
||||
assert.Error(err)
|
||||
checkErrTrace(assert, testTracer, testTransport)
|
||||
}
|
||||
|
||||
func TestClientV3(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv3.NewClient(
|
||||
elasticv3.SetURL("http://127.0.0.1:59201"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
elasticv3.SetSniff(false),
|
||||
elasticv3.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkPUTTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("twitter").Type("tweet").
|
||||
Id("1").DoC(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkGETTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("not-real-index").
|
||||
Id("1").DoC(context.TODO())
|
||||
assert.Error(err)
|
||||
checkErrTrace(assert, testTracer, testTransport)
|
||||
}
|
||||
|
||||
func TestClientV3Failure(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv3.NewClient(
|
||||
// not existing service, it must fail
|
||||
elasticv3.SetURL("http://127.0.0.1:29201"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
elasticv3.SetSniff(false),
|
||||
elasticv3.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.TODO())
|
||||
assert.Error(err)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*net.OpError", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
func TestClientV5Failure(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv5.NewClient(
|
||||
// not existing service, it must fail
|
||||
elasticv5.SetURL("http://127.0.0.1:29200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
elasticv5.SetSniff(false),
|
||||
elasticv5.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.TODO())
|
||||
assert.Error(err)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*net.OpError", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
func checkPUTTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
}
|
||||
|
||||
func checkGETTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("GET /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("GET", spans[0].GetMeta("elasticsearch.method"))
|
||||
}
|
||||
|
||||
func checkErrTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("GET /not-real-index/_all/?", spans[0].Resource)
|
||||
assert.Equal("/not-real-index/_all/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*errors.errorString", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *tracertest.DummyTransport) {
|
||||
transport := &tracertest.DummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
57
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/example_test.go
generated
vendored
Normal file
57
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package elastictrace_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
elastictrace "github.com/DataDog/dd-trace-go/contrib/olivere/elastic"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
elasticv3 "gopkg.in/olivere/elastic.v3"
|
||||
elasticv5 "gopkg.in/olivere/elastic.v5"
|
||||
)
|
||||
|
||||
// To start tracing elastic.v5 requests, create a new TracedHTTPClient that you will
|
||||
// use when initializing the elastic.Client.
|
||||
func Example_v5() {
|
||||
tc := elastictrace.NewTracedHTTPClient("my-elasticsearch-service", tracer.DefaultTracer)
|
||||
client, _ := elasticv5.NewClient(
|
||||
elasticv5.SetURL("http://127.0.0.1:9200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
)
|
||||
|
||||
// Spans are emitted for all
|
||||
client.Index().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.Background())
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/tweet/1")
|
||||
ctx := root.Context(context.Background())
|
||||
client.Get().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
Do(ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
// To trace elastic.v3 you create a TracedHTTPClient in the same way but all requests must use
|
||||
// the DoC() call to pass the request context.
|
||||
func Example_v3() {
|
||||
tc := elastictrace.NewTracedHTTPClient("my-elasticsearch-service", tracer.DefaultTracer)
|
||||
client, _ := elasticv3.NewClient(
|
||||
elasticv3.SetURL("http://127.0.0.1:9200"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
)
|
||||
|
||||
// Spans are emitted for all
|
||||
client.Index().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.Background())
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/tweet/1")
|
||||
ctx := root.Context(context.Background())
|
||||
client.Get().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
DoC(ctx)
|
||||
root.Finish()
|
||||
}
|
26
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/quantize.go
generated
vendored
Normal file
26
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/quantize.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package elastictrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
IdRegexp = regexp.MustCompile("/([0-9]+)([/\\?]|$)")
|
||||
IdPlaceholder = []byte("/?$2")
|
||||
IndexRegexp = regexp.MustCompile("[0-9]{2,}")
|
||||
IndexPlaceholder = []byte("?")
|
||||
)
|
||||
|
||||
// Quantize quantizes an Elasticsearch to extract a meaningful resource from the request.
|
||||
// We quantize based on the method+url with some cleanup applied to the URL.
|
||||
// URLs with an ID will be generalized as will (potential) timestamped indices.
|
||||
func Quantize(span *tracer.Span) {
|
||||
url := span.GetMeta("elasticsearch.url")
|
||||
method := span.GetMeta("elasticsearch.method")
|
||||
|
||||
quantizedURL := IdRegexp.ReplaceAll([]byte(url), IdPlaceholder)
|
||||
quantizedURL = IndexRegexp.ReplaceAll(quantizedURL, IndexPlaceholder)
|
||||
span.Resource = fmt.Sprintf("%s %s", method, quantizedURL)
|
||||
}
|
43
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/quantize_test.go
generated
vendored
Normal file
43
vendor/github.com/DataDog/dd-trace-go/contrib/olivere/elastic/quantize_test.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package elastictrace
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestQuantize(t *testing.T) {
|
||||
tr := tracer.NewTracer()
|
||||
for _, tc := range []struct {
|
||||
url, method string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
url: "/twitter/tweets",
|
||||
method: "POST",
|
||||
expected: "POST /twitter/tweets",
|
||||
},
|
||||
{
|
||||
url: "/logs_2016_05/event/_search",
|
||||
method: "GET",
|
||||
expected: "GET /logs_?_?/event/_search",
|
||||
},
|
||||
{
|
||||
url: "/twitter/tweets/123",
|
||||
method: "GET",
|
||||
expected: "GET /twitter/tweets/?",
|
||||
},
|
||||
{
|
||||
url: "/logs_2016_05/event/123",
|
||||
method: "PUT",
|
||||
expected: "PUT /logs_?_?/event/?",
|
||||
},
|
||||
} {
|
||||
span := tracer.NewSpan("name", "elasticsearch", "", 0, 0, 0, tr)
|
||||
span.SetMeta("elasticsearch.url", tc.url)
|
||||
span.SetMeta("elasticsearch.method", tc.method)
|
||||
Quantize(span)
|
||||
assert.Equal(t, tc.expected, span.Resource)
|
||||
}
|
||||
}
|
33
vendor/github.com/DataDog/dd-trace-go/tasks/benchmarks.rb
generated
vendored
Normal file
33
vendor/github.com/DataDog/dd-trace-go/tasks/benchmarks.rb
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
require_relative 'common'
|
||||
|
||||
namespace :collect do
|
||||
desc 'Run client benchmarks'
|
||||
task :benchmarks do
|
||||
# TODO: benchmarks must be done for different Tracer versions
|
||||
# so that we can retrieve the diff and return an exit code != 0
|
||||
Tasks::Common.get_go_packages.each do |pkg|
|
||||
sh "go test -run=NONE -bench=. #{pkg}"
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Run pprof to collect profiles'
|
||||
task :profiles do
|
||||
# initializes the folder to collect profiles
|
||||
FileUtils.mkdir_p 'profiles'
|
||||
filename = "#{Tasks::Common::PROFILES}/tracer"
|
||||
|
||||
# generate a profile for the Tracer based on benchmarks
|
||||
sh %{
|
||||
go test -run=NONE -bench=.
|
||||
-cpuprofile=#{filename}-cpu.out
|
||||
-memprofile=#{filename}-mem.out
|
||||
-blockprofile=#{filename}-block.out
|
||||
#{Tasks::Common::TRACER_PACKAGE}
|
||||
}.gsub(/\s+/, ' ').strip
|
||||
|
||||
# export profiles
|
||||
sh "go tool pprof -text -nodecount=10 -cum ./tracer.test #{filename}-cpu.out"
|
||||
sh "go tool pprof -text -nodecount=10 -cum -inuse_space ./tracer.test #{filename}-mem.out"
|
||||
sh "go tool pprof -text -nodecount=10 -cum ./tracer.test #{filename}-block.out"
|
||||
end
|
||||
end
|
12
vendor/github.com/DataDog/dd-trace-go/tasks/common.rb
generated
vendored
Normal file
12
vendor/github.com/DataDog/dd-trace-go/tasks/common.rb
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
module Tasks
|
||||
module Common
|
||||
PROFILES = './profiles'
|
||||
TRACER_PACKAGE = 'github.com/DataDog/dd-trace-go/tracer'
|
||||
COVERAGE_FILE = 'code.cov'
|
||||
|
||||
# returns a list of Go packages
|
||||
def self.get_go_packages
|
||||
`go list ./opentracing ./tracer ./contrib/...`.split("\n")
|
||||
end
|
||||
end
|
||||
end
|
41
vendor/github.com/DataDog/dd-trace-go/tasks/testing.rb
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/tasks/testing.rb
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
require 'tempfile'
|
||||
require_relative 'common'
|
||||
|
||||
namespace :test do
|
||||
desc 'Run linting on the repository'
|
||||
task :lint do
|
||||
# enable-gc is required because with a full linting process we may finish workers memory
|
||||
# fast is used temporarily for a faster CI
|
||||
sh 'gometalinter --deadline 60s --fast --enable-gc --errors --vendor ./opentracing ./tracer ./contrib/...'
|
||||
end
|
||||
|
||||
desc 'Test all packages'
|
||||
task :all do
|
||||
sh 'go test ./opentracing ./tracer ./contrib/...'
|
||||
end
|
||||
|
||||
desc 'Test all packages with -race flag'
|
||||
task :race do
|
||||
sh 'go test -race ./opentracing ./tracer ./contrib/...'
|
||||
end
|
||||
|
||||
desc 'Run test coverage'
|
||||
task :coverage do
|
||||
# collect global profiles in this file
|
||||
sh "echo \"mode: count\" > #{Tasks::Common::COVERAGE_FILE}"
|
||||
|
||||
# for each package collect and append the profile
|
||||
Tasks::Common.get_go_packages.each do |pkg|
|
||||
begin
|
||||
f = Tempfile.new('profile')
|
||||
# run code coverage
|
||||
sh "go test -short -covermode=count -coverprofile=#{f.path} #{pkg}"
|
||||
sh "cat #{f.path} | tail -n +2 >> #{Tasks::Common::COVERAGE_FILE}"
|
||||
ensure
|
||||
File.delete(f)
|
||||
end
|
||||
end
|
||||
|
||||
sh "go tool cover -func #{Tasks::Common::COVERAGE_FILE}"
|
||||
end
|
||||
end
|
23
vendor/github.com/DataDog/dd-trace-go/tasks/vendors.rb
generated
vendored
Normal file
23
vendor/github.com/DataDog/dd-trace-go/tasks/vendors.rb
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
desc 'Initialize the development environment'
|
||||
task :init do
|
||||
sh 'go get -u github.com/golang/dep/cmd/dep'
|
||||
sh 'go get -u github.com/alecthomas/gometalinter'
|
||||
sh 'gometalinter --install'
|
||||
|
||||
# TODO:bertrand remove this
|
||||
# It is only a short-term workaround, we should find a proper way to handle
|
||||
# multiple versions of the same dependency
|
||||
sh 'go get -d google.golang.org/grpc'
|
||||
gopath = ENV["GOPATH"].split(":")[0]
|
||||
sh "cd #{gopath}/src/google.golang.org/grpc/ && git checkout v1.5.2 && cd -"
|
||||
sh "go get -t -v ./contrib/..."
|
||||
sh "go get -v github.com/opentracing/opentracing-go"
|
||||
end
|
||||
|
||||
namespace :vendors do
|
||||
desc "Update the vendors list"
|
||||
task :update do
|
||||
# download and update our vendors
|
||||
sh 'dep ensure'
|
||||
end
|
||||
end
|
3
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/README.md
generated
vendored
Normal file
3
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# [DEPRECATED] Libraries supported for tracing
|
||||
|
||||
This folder will be dropped on the medium-term. It's now located at [`/contrib`](https://github.com/DataDog/dd-trace-go/tree/master/contrib).
|
86
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/elastictraced.go
generated
vendored
Normal file
86
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/elastictraced.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Package elastictraced provides tracing for the Elastic Elasticsearch client.
|
||||
// Supports v3 (gopkg.in/olivere/elastic.v3), v5 (gopkg.in/olivere/elastic.v5)
|
||||
// but with v3 you must use `DoC` on all requests to capture the request context.
|
||||
package elastictraced
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
)
|
||||
|
||||
// MaxContentLength is the maximum content length for which we'll read and capture
|
||||
// the contents of the request body. Anything larger will still be traced but the
|
||||
// body will not be captured as trace metadata.
|
||||
const MaxContentLength = 500 * 1024
|
||||
|
||||
// TracedTransport is a traced HTTP transport that captures Elasticsearch spans.
|
||||
type TracedTransport struct {
|
||||
service string
|
||||
tracer *tracer.Tracer
|
||||
*http.Transport
|
||||
}
|
||||
|
||||
// RoundTrip satisfies the RoundTripper interface, wraps the sub Transport and
|
||||
// captures a span of the Elasticsearch request.
|
||||
func (t *TracedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
span := t.tracer.NewChildSpanFromContext("elasticsearch.query", req.Context())
|
||||
span.Service = t.service
|
||||
span.Type = ext.AppTypeDB
|
||||
defer span.Finish()
|
||||
span.SetMeta("elasticsearch.method", req.Method)
|
||||
span.SetMeta("elasticsearch.url", req.URL.Path)
|
||||
span.SetMeta("elasticsearch.params", req.URL.Query().Encode())
|
||||
|
||||
contentLength, _ := strconv.Atoi(req.Header.Get("Content-Length"))
|
||||
if req.Body != nil && contentLength < MaxContentLength {
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
span.SetMeta("elasticsearch.body", string(buf))
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||
}
|
||||
|
||||
// Run the request using the standard transport.
|
||||
res, err := t.Transport.RoundTrip(req)
|
||||
if res != nil {
|
||||
span.SetMeta(ext.HTTPCode, strconv.Itoa(res.StatusCode))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
} else if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
buf, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
// Status text is best we can do if if we can't read the body.
|
||||
span.SetError(errors.New(http.StatusText(res.StatusCode)))
|
||||
} else {
|
||||
span.SetError(errors.New(string(buf)))
|
||||
}
|
||||
res.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||
}
|
||||
Quantize(span)
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// NewTracedHTTPClient returns a new TracedTransport that traces HTTP requests.
|
||||
func NewTracedHTTPClient(service string, tracer *tracer.Tracer) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &TracedTransport{service, tracer, &http.Transport{}},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTracedHTTPClientWithTransport returns a new TracedTransport that traces HTTP requests
|
||||
// and takes in a Transport to use something other than the default.
|
||||
func NewTracedHTTPClientWithTransport(service string, tracer *tracer.Tracer, transport *http.Transport) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &TracedTransport{service, tracer, transport},
|
||||
}
|
||||
}
|
193
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/elastictraced_test.go
generated
vendored
Normal file
193
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/elastictraced_test.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
|||
package elastictraced
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
elasticv3 "gopkg.in/olivere/elastic.v3"
|
||||
elasticv5 "gopkg.in/olivere/elastic.v5"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClientV5(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv5.NewClient(
|
||||
elasticv5.SetURL("http://127.0.0.1:59200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
elasticv5.SetSniff(false),
|
||||
elasticv5.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkPUTTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("twitter").Type("tweet").
|
||||
Id("1").Do(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkGETTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("not-real-index").
|
||||
Id("1").Do(context.TODO())
|
||||
assert.Error(err)
|
||||
checkErrTrace(assert, testTracer, testTransport)
|
||||
}
|
||||
|
||||
func TestClientV3(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv3.NewClient(
|
||||
elasticv3.SetURL("http://127.0.0.1:59201"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
elasticv3.SetSniff(false),
|
||||
elasticv3.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkPUTTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("twitter").Type("tweet").
|
||||
Id("1").DoC(context.TODO())
|
||||
assert.NoError(err)
|
||||
checkGETTrace(assert, testTracer, testTransport)
|
||||
|
||||
_, err = client.Get().Index("not-real-index").
|
||||
Id("1").DoC(context.TODO())
|
||||
assert.Error(err)
|
||||
checkErrTrace(assert, testTracer, testTransport)
|
||||
}
|
||||
|
||||
func TestClientV3Failure(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv3.NewClient(
|
||||
// not existing service, it must fail
|
||||
elasticv3.SetURL("http://127.0.0.1:29201"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
elasticv3.SetSniff(false),
|
||||
elasticv3.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.TODO())
|
||||
assert.Error(err)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*net.OpError", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
func TestClientV5Failure(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
tc := NewTracedHTTPClient("my-es-service", testTracer)
|
||||
client, err := elasticv5.NewClient(
|
||||
// not existing service, it must fail
|
||||
elasticv5.SetURL("http://127.0.0.1:29200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
elasticv5.SetSniff(false),
|
||||
elasticv5.SetHealthcheck(false),
|
||||
)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = client.Index().
|
||||
Index("twitter").Id("1").
|
||||
Type("tweet").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.TODO())
|
||||
assert.Error(err)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*net.OpError", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
func checkPUTTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("PUT /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("PUT", spans[0].GetMeta("elasticsearch.method"))
|
||||
}
|
||||
|
||||
func checkGETTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("GET /twitter/tweet/?", spans[0].Resource)
|
||||
assert.Equal("/twitter/tweet/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.Equal("GET", spans[0].GetMeta("elasticsearch.method"))
|
||||
}
|
||||
|
||||
func checkErrTrace(assert *assert.Assertions, tracer *tracer.Tracer, transport *tracertest.DummyTransport) {
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Equal("my-es-service", spans[0].Service)
|
||||
assert.Equal("GET /not-real-index/_all/?", spans[0].Resource)
|
||||
assert.Equal("/not-real-index/_all/1", spans[0].GetMeta("elasticsearch.url"))
|
||||
assert.NotEmpty(spans[0].GetMeta("error.msg"))
|
||||
assert.Equal("*errors.errorString", spans[0].GetMeta("error.type"))
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *tracertest.DummyTransport) {
|
||||
transport := &tracertest.DummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
57
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/example_test.go
generated
vendored
Normal file
57
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package elastictraced_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced"
|
||||
elasticv3 "gopkg.in/olivere/elastic.v3"
|
||||
elasticv5 "gopkg.in/olivere/elastic.v5"
|
||||
)
|
||||
|
||||
// To start tracing elastic.v5 requests, create a new TracedHTTPClient that you will
|
||||
// use when initializing the elastic.Client.
|
||||
func Example_v5() {
|
||||
tc := elastictraced.NewTracedHTTPClient("my-elasticsearch-service", tracer.DefaultTracer)
|
||||
client, _ := elasticv5.NewClient(
|
||||
elasticv5.SetURL("http://127.0.0.1:9200"),
|
||||
elasticv5.SetHttpClient(tc),
|
||||
)
|
||||
|
||||
// Spans are emitted for all
|
||||
client.Index().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
Do(context.Background())
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/tweet/1")
|
||||
ctx := root.Context(context.Background())
|
||||
client.Get().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
Do(ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
// To trace elastic.v3 you create a TracedHTTPClient in the same way but all requests must use
|
||||
// the DoC() call to pass the request context.
|
||||
func Example_v3() {
|
||||
tc := elastictraced.NewTracedHTTPClient("my-elasticsearch-service", tracer.DefaultTracer)
|
||||
client, _ := elasticv3.NewClient(
|
||||
elasticv3.SetURL("http://127.0.0.1:9200"),
|
||||
elasticv3.SetHttpClient(tc),
|
||||
)
|
||||
|
||||
// Spans are emitted for all
|
||||
client.Index().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
BodyString(`{"user": "test", "message": "hello"}`).
|
||||
DoC(context.Background())
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/tweet/1")
|
||||
ctx := root.Context(context.Background())
|
||||
client.Get().
|
||||
Index("twitter").Type("tweet").Index("1").
|
||||
DoC(ctx)
|
||||
root.Finish()
|
||||
}
|
26
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/quantize.go
generated
vendored
Normal file
26
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/quantize.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package elastictraced
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
IdRegexp = regexp.MustCompile("/([0-9]+)([/\\?]|$)")
|
||||
IdPlaceholder = []byte("/?$2")
|
||||
IndexRegexp = regexp.MustCompile("[0-9]{2,}")
|
||||
IndexPlaceholder = []byte("?")
|
||||
)
|
||||
|
||||
// Quantize quantizes an Elasticsearch to extract a meaningful resource from the request.
|
||||
// We quantize based on the method+url with some cleanup applied to the URL.
|
||||
// URLs with an ID will be generalized as will (potential) timestamped indices.
|
||||
func Quantize(span *tracer.Span) {
|
||||
url := span.GetMeta("elasticsearch.url")
|
||||
method := span.GetMeta("elasticsearch.method")
|
||||
|
||||
quantizedURL := IdRegexp.ReplaceAll([]byte(url), IdPlaceholder)
|
||||
quantizedURL = IndexRegexp.ReplaceAll(quantizedURL, IndexPlaceholder)
|
||||
span.Resource = fmt.Sprintf("%s %s", method, quantizedURL)
|
||||
}
|
43
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/quantize_test.go
generated
vendored
Normal file
43
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/elastictraced/quantize_test.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package elastictraced
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestQuantize(t *testing.T) {
|
||||
tr := tracer.NewTracer()
|
||||
for _, tc := range []struct {
|
||||
url, method string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
url: "/twitter/tweets",
|
||||
method: "POST",
|
||||
expected: "POST /twitter/tweets",
|
||||
},
|
||||
{
|
||||
url: "/logs_2016_05/event/_search",
|
||||
method: "GET",
|
||||
expected: "GET /logs_?_?/event/_search",
|
||||
},
|
||||
{
|
||||
url: "/twitter/tweets/123",
|
||||
method: "GET",
|
||||
expected: "GET /twitter/tweets/?",
|
||||
},
|
||||
{
|
||||
url: "/logs_2016_05/event/123",
|
||||
method: "PUT",
|
||||
expected: "PUT /logs_?_?/event/?",
|
||||
},
|
||||
} {
|
||||
span := tracer.NewSpan("name", "elasticsearch", "", 0, 0, 0, tr)
|
||||
span.SetMeta("elasticsearch.url", tc.url)
|
||||
span.SetMeta("elasticsearch.method", tc.method)
|
||||
Quantize(span)
|
||||
assert.Equal(t, tc.expected, span.Resource)
|
||||
}
|
||||
}
|
59
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/example_test.go
generated
vendored
Normal file
59
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package gintrace_test
|
||||
|
||||
import (
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// To start tracing requests, add the trace middleware to your Gin router.
|
||||
func Example() {
|
||||
|
||||
// Create your router and use the middleware.
|
||||
r := gin.New()
|
||||
r.Use(gintrace.Middleware("my-web-app"))
|
||||
|
||||
r.GET("/hello", func(c *gin.Context) {
|
||||
c.String(200, "hello world!")
|
||||
})
|
||||
|
||||
// Profit!
|
||||
r.Run(":8080")
|
||||
}
|
||||
|
||||
func ExampleHTML() {
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("my-web-app"))
|
||||
r.LoadHTMLGlob("templates/*")
|
||||
|
||||
r.GET("/index", func(c *gin.Context) {
|
||||
// This will render the html and trace the execution time.
|
||||
gintrace.HTML(c, 200, "index.tmpl", gin.H{
|
||||
"title": "Main website",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func ExampleSpanDefault() {
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("image-encoder"))
|
||||
|
||||
r.GET("/image/encode", func(c *gin.Context) {
|
||||
// The middleware patches a span to the request. Let's add some metadata,
|
||||
// and create a child span.
|
||||
span := gintrace.SpanDefault(c)
|
||||
span.SetMeta("user.handle", "admin")
|
||||
span.SetMeta("user.id", "1234")
|
||||
|
||||
encodeSpan := tracer.NewChildSpan("image.encode", span)
|
||||
// encode a image
|
||||
encodeSpan.Finish()
|
||||
|
||||
uploadSpan := tracer.NewChildSpan("image.upload", span)
|
||||
// upload the image
|
||||
uploadSpan.Finish()
|
||||
|
||||
c.String(200, "ok!")
|
||||
})
|
||||
|
||||
}
|
143
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/gintrace.go
generated
vendored
Normal file
143
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/gintrace.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Package gintrace provides tracing middleware for the Gin web framework.
|
||||
package gintrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// key is the string that we'll use to store spans in the tracer.
|
||||
var key = "datadog_trace_span"
|
||||
|
||||
// Middleware returns middleware that will trace requests with the default
|
||||
// tracer.
|
||||
func Middleware(service string) gin.HandlerFunc {
|
||||
return MiddlewareTracer(service, tracer.DefaultTracer)
|
||||
}
|
||||
|
||||
// MiddlewareTracer returns middleware that will trace requests with the given
|
||||
// tracer.
|
||||
func MiddlewareTracer(service string, t *tracer.Tracer) gin.HandlerFunc {
|
||||
t.SetServiceInfo(service, "gin-gonic", ext.AppTypeWeb)
|
||||
mw := newMiddleware(service, t)
|
||||
return mw.Handle
|
||||
}
|
||||
|
||||
// middleware implements gin middleware.
|
||||
type middleware struct {
|
||||
service string
|
||||
trc *tracer.Tracer
|
||||
}
|
||||
|
||||
func newMiddleware(service string, trc *tracer.Tracer) *middleware {
|
||||
return &middleware{
|
||||
service: service,
|
||||
trc: trc,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle is a gin HandlerFunc that will add tracing to the given request.
|
||||
func (m *middleware) Handle(c *gin.Context) {
|
||||
|
||||
// bail if not enabled
|
||||
if !m.trc.Enabled() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME[matt] the handler name is a bit unwieldy and uses reflection
|
||||
// under the hood. might be better to tackle this task and do it right
|
||||
// so we can end up with "user/:user/whatever" instead of
|
||||
// "github.com/foobar/blah"
|
||||
//
|
||||
// See here: https://github.com/gin-gonic/gin/issues/649
|
||||
resource := c.HandlerName()
|
||||
|
||||
// Create our span and patch it to the context for downstream.
|
||||
span := m.trc.NewRootSpan("gin.request", m.service, resource)
|
||||
c.Set(key, span)
|
||||
|
||||
// Pass along the request.
|
||||
c.Next()
|
||||
|
||||
// Set http tags.
|
||||
span.SetMeta(ext.HTTPCode, strconv.Itoa(c.Writer.Status()))
|
||||
span.SetMeta(ext.HTTPMethod, c.Request.Method)
|
||||
span.SetMeta(ext.HTTPURL, c.Request.URL.Path)
|
||||
|
||||
// Set any error information.
|
||||
var err error
|
||||
if len(c.Errors) > 0 {
|
||||
span.SetMeta("gin.errors", c.Errors.String()) // set all errors
|
||||
err = c.Errors[0] // but use the first for standard fields
|
||||
}
|
||||
span.FinishWithErr(err)
|
||||
}
|
||||
|
||||
// Span returns the Span stored in the given Context and true. If it doesn't exist,
|
||||
// it will returns (nil, false)
|
||||
func Span(c *gin.Context) (*tracer.Span, bool) {
|
||||
if c == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
s, ok := c.Get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
switch span := s.(type) {
|
||||
case *tracer.Span:
|
||||
return span, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// SpanDefault returns the span stored in the given Context. If none exists,
|
||||
// it will return an empty span.
|
||||
func SpanDefault(c *gin.Context) *tracer.Span {
|
||||
span, ok := Span(c)
|
||||
if !ok {
|
||||
return &tracer.Span{}
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
// NewChildSpan will create a span that is the child of the span stored in
|
||||
// the context.
|
||||
func NewChildSpan(name string, c *gin.Context) *tracer.Span {
|
||||
span, ok := Span(c)
|
||||
if !ok {
|
||||
return &tracer.Span{}
|
||||
}
|
||||
return span.Tracer().NewChildSpan(name, span)
|
||||
}
|
||||
|
||||
// HTML will trace the rendering of the template as a child of the span in the
|
||||
// given context.
|
||||
func HTML(c *gin.Context, code int, name string, obj interface{}) {
|
||||
span, _ := Span(c)
|
||||
if span == nil {
|
||||
c.HTML(code, name, obj)
|
||||
return
|
||||
}
|
||||
|
||||
child := span.Tracer().NewChildSpan("gin.render.html", span)
|
||||
child.SetMeta("go.template", name)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err := fmt.Errorf("error rendering tmpl:%s: %s", name, r)
|
||||
child.FinishWithErr(err)
|
||||
panic(r)
|
||||
} else {
|
||||
child.Finish()
|
||||
}
|
||||
}()
|
||||
|
||||
// render
|
||||
c.HTML(code, name, obj)
|
||||
}
|
250
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/gintrace_test.go
generated
vendored
Normal file
250
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace/gintrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
package gintrace
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gin.SetMode(gin.ReleaseMode) // silence annoying log msgs
|
||||
}
|
||||
|
||||
func TestChildSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, _ := getTestTracer()
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/user/:id", func(c *gin.Context) {
|
||||
span, ok := tracer.SpanFromContext(c)
|
||||
assert.True(ok)
|
||||
assert.NotNil(span)
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/user/123", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func TestTrace200(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/user/:id", func(c *gin.Context) {
|
||||
// assert we patch the span on the request context.
|
||||
span := SpanDefault(c)
|
||||
span.SetMeta("test.gin", "ginny")
|
||||
assert.Equal(span.Service, "foobar")
|
||||
id := c.Param("id")
|
||||
c.Writer.Write([]byte(id))
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/user/123", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// do and verify the request
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
|
||||
// verify traces look good
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
if len(spans) < 1 {
|
||||
t.Fatalf("no spans")
|
||||
}
|
||||
s := spans[0]
|
||||
assert.Equal(s.Service, "foobar")
|
||||
assert.Equal(s.Name, "gin.request")
|
||||
// FIXME[matt] would be much nicer to have "/user/:id" here
|
||||
assert.True(strings.Contains(s.Resource, "gintrace.TestTrace200"))
|
||||
assert.Equal(s.GetMeta("test.gin"), "ginny")
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), "/user/123")
|
||||
}
|
||||
|
||||
func TestDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetEnabled(false)
|
||||
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
span, ok := Span(c)
|
||||
assert.Nil(span)
|
||||
assert.False(ok)
|
||||
c.Writer.Write([]byte("ok"))
|
||||
})
|
||||
|
||||
r := httptest.NewRequest("GET", "/ping", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// do and verify the request
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
|
||||
// verify traces look good
|
||||
testTracer.ForceFlush()
|
||||
spans := testTransport.Traces()
|
||||
assert.Len(spans, 0)
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
// setup
|
||||
middleware := newMiddleware("foobar", testTracer)
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
|
||||
// a handler with an error and make the requests
|
||||
router.GET("/err", func(c *gin.Context) {
|
||||
c.AbortWithError(500, errors.New("oh no"))
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/err", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 500)
|
||||
|
||||
// verify the errors and status are correct
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
if len(spans) < 1 {
|
||||
t.Fatalf("no spans")
|
||||
}
|
||||
s := spans[0]
|
||||
assert.Equal(s.Service, "foobar")
|
||||
assert.Equal(s.Name, "gin.request")
|
||||
assert.Equal(s.GetMeta("http.status_code"), "500")
|
||||
assert.Equal(s.GetMeta(ext.ErrorMsg), "oh no")
|
||||
assert.Equal(s.Error, int32(1))
|
||||
}
|
||||
|
||||
func TestHTML(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
|
||||
// setup
|
||||
middleware := newMiddleware("tmplservice", testTracer)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(middleware.Handle)
|
||||
|
||||
// add a template
|
||||
tmpl := template.Must(template.New("hello").Parse("hello {{.}}"))
|
||||
router.SetHTMLTemplate(tmpl)
|
||||
|
||||
// a handler with an error and make the requests
|
||||
router.GET("/hello", func(c *gin.Context) {
|
||||
HTML(c, 200, "hello", "world")
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/hello", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
assert.Equal("hello world", w.Body.String())
|
||||
|
||||
// verify the errors and status are correct
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
for _, s := range spans {
|
||||
assert.Equal(s.Service, "tmplservice")
|
||||
}
|
||||
|
||||
var tspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// we need to pick up the span we're searching for, as the
|
||||
// order is not garanteed within the buffer
|
||||
if s.Name == "gin.render.html" {
|
||||
tspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(tspan, "we should have found a span with name gin.render.html")
|
||||
assert.Equal(tspan.GetMeta("go.template"), "hello")
|
||||
fmt.Println(spans)
|
||||
}
|
||||
|
||||
func TestGetSpanNotInstrumented(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
router := gin.New()
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
// Assert we don't have a span on the context.
|
||||
s, ok := Span(c)
|
||||
assert.False(ok)
|
||||
assert.Nil(s)
|
||||
// and the default span is empty
|
||||
s = SpanDefault(c)
|
||||
assert.Equal(s.Service, "")
|
||||
c.Writer.Write([]byte("ok"))
|
||||
})
|
||||
r := httptest.NewRequest("GET", "/ping", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
response := w.Result()
|
||||
assert.Equal(response.StatusCode, 200)
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
81
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/example_test.go
generated
vendored
Normal file
81
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package goredistrace_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/gin-gonic/gintrace"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/go-redis"
|
||||
"github.com/gin-gonic/gin"
|
||||
redis "github.com/go-redis/redis"
|
||||
"time"
|
||||
)
|
||||
|
||||
// To start tracing Redis commands, use the NewTracedClient function to create a traced Redis clienty,
|
||||
// passing in a service name of choice.
|
||||
func Example() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := goredistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Set("test_key", "test_value", 0)
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When set with a context, the traced client will emit a span inheriting from 'parent.request'
|
||||
c.SetContext(ctx)
|
||||
c.Set("food", "cheese", 0)
|
||||
root.Finish()
|
||||
|
||||
// Contexts can be easily passed between Datadog integrations
|
||||
r := gin.Default()
|
||||
r.Use(gintrace.Middleware("web-admin"))
|
||||
client := goredistrace.NewTracedClient(opts, tracer.DefaultTracer, "redis-img-backend")
|
||||
|
||||
r.GET("/user/settings/:id", func(ctx *gin.Context) {
|
||||
// create a span that is a child of your http request
|
||||
client.SetContext(ctx)
|
||||
client.Get(fmt.Sprintf("cached_user_details_%s", ctx.Param("id")))
|
||||
})
|
||||
}
|
||||
|
||||
// You can also trace Redis Pipelines
|
||||
func Example_pipeline() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := goredistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// pipe is a TracedPipeliner
|
||||
pipe := c.Pipeline()
|
||||
pipe.Incr("pipeline_counter")
|
||||
pipe.Expire("pipeline_counter", time.Hour)
|
||||
|
||||
pipe.Exec()
|
||||
}
|
||||
|
||||
func ExampleNewTracedClient() {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
c := goredistrace.NewTracedClient(opts, tracer.DefaultTracer, "my-redis-backend")
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Set("test_key", "test_value", 0)
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When set with a context, the traced client will emit a span inheriting from 'parent.request'
|
||||
c.SetContext(ctx)
|
||||
c.Set("food", "cheese", 0)
|
||||
root.Finish()
|
||||
}
|
151
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/tracedredis.go
generated
vendored
Normal file
151
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/tracedredis.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
// Package goredistrace provides tracing for the go-redis Redis client (https://github.com/go-redis/redis)
|
||||
package goredistrace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/go-redis/redis"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TracedClient is used to trace requests to a redis server.
|
||||
type TracedClient struct {
|
||||
*redis.Client
|
||||
traceParams traceParams
|
||||
}
|
||||
|
||||
// TracedPipeline is used to trace pipelines executed on a redis server.
|
||||
type TracedPipeliner struct {
|
||||
redis.Pipeliner
|
||||
traceParams traceParams
|
||||
}
|
||||
|
||||
type traceParams struct {
|
||||
host string
|
||||
port string
|
||||
db string
|
||||
service string
|
||||
tracer *tracer.Tracer
|
||||
}
|
||||
|
||||
// NewTracedClient takes a Client returned by redis.NewClient and configures it to emit spans under the given service name
|
||||
func NewTracedClient(opt *redis.Options, t *tracer.Tracer, service string) *TracedClient {
|
||||
var host, port string
|
||||
addr := strings.Split(opt.Addr, ":")
|
||||
if len(addr) == 2 && addr[1] != "" {
|
||||
port = addr[1]
|
||||
} else {
|
||||
port = "6379"
|
||||
}
|
||||
host = addr[0]
|
||||
db := strconv.Itoa(opt.DB)
|
||||
|
||||
client := redis.NewClient(opt)
|
||||
t.SetServiceInfo(service, "redis", ext.AppTypeDB)
|
||||
tc := &TracedClient{
|
||||
client,
|
||||
traceParams{
|
||||
host,
|
||||
port,
|
||||
db,
|
||||
service,
|
||||
t},
|
||||
}
|
||||
|
||||
tc.Client.WrapProcess(createWrapperFromClient(tc))
|
||||
return tc
|
||||
}
|
||||
|
||||
// Pipeline creates a TracedPipeline from a TracedClient
|
||||
func (c *TracedClient) Pipeline() *TracedPipeliner {
|
||||
return &TracedPipeliner{
|
||||
c.Client.Pipeline(),
|
||||
c.traceParams,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecWithContext calls Pipeline.Exec(). It ensures that the resulting Redis calls
|
||||
// are traced, and that emitted spans are children of the given Context
|
||||
func (c *TracedPipeliner) ExecWithContext(ctx context.Context) ([]redis.Cmder, error) {
|
||||
span := c.traceParams.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
span.Service = c.traceParams.service
|
||||
|
||||
span.SetMeta("out.host", c.traceParams.host)
|
||||
span.SetMeta("out.port", c.traceParams.port)
|
||||
span.SetMeta("out.db", c.traceParams.db)
|
||||
|
||||
cmds, err := c.Pipeliner.Exec()
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Resource = String(cmds)
|
||||
span.SetMeta("redis.pipeline_length", strconv.Itoa(len(cmds)))
|
||||
span.Finish()
|
||||
return cmds, err
|
||||
}
|
||||
|
||||
// Exec calls Pipeline.Exec() ensuring that the resulting Redis calls are traced
|
||||
func (c *TracedPipeliner) Exec() ([]redis.Cmder, error) {
|
||||
span := c.traceParams.tracer.NewRootSpan("redis.command", c.traceParams.service, "redis")
|
||||
|
||||
span.SetMeta("out.host", c.traceParams.host)
|
||||
span.SetMeta("out.port", c.traceParams.port)
|
||||
span.SetMeta("out.db", c.traceParams.db)
|
||||
|
||||
cmds, err := c.Pipeliner.Exec()
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Resource = String(cmds)
|
||||
span.SetMeta("redis.pipeline_length", strconv.Itoa(len(cmds)))
|
||||
span.Finish()
|
||||
return cmds, err
|
||||
}
|
||||
|
||||
// String returns a string representation of a slice of redis Commands, separated by newlines
|
||||
func String(cmds []redis.Cmder) string {
|
||||
var b bytes.Buffer
|
||||
for _, cmd := range cmds {
|
||||
b.WriteString(cmd.String())
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// SetContext sets a context on a TracedClient. Use it to ensure that emitted spans have the correct parent
|
||||
func (c *TracedClient) SetContext(ctx context.Context) {
|
||||
c.Client = c.Client.WithContext(ctx)
|
||||
}
|
||||
|
||||
// createWrapperFromClient wraps tracing into redis.Process().
|
||||
func createWrapperFromClient(tc *TracedClient) func(oldProcess func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(oldProcess func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(cmd redis.Cmder) error {
|
||||
ctx := tc.Client.Context()
|
||||
|
||||
var resource string
|
||||
resource = strings.Split(cmd.String(), " ")[0]
|
||||
args_length := len(strings.Split(cmd.String(), " ")) - 1
|
||||
span := tc.traceParams.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
|
||||
span.Service = tc.traceParams.service
|
||||
span.Resource = resource
|
||||
|
||||
span.SetMeta("redis.raw_command", cmd.String())
|
||||
span.SetMeta("redis.args_length", strconv.Itoa(args_length))
|
||||
span.SetMeta("out.host", tc.traceParams.host)
|
||||
span.SetMeta("out.port", tc.traceParams.port)
|
||||
span.SetMeta("out.db", tc.traceParams.db)
|
||||
|
||||
err := oldProcess(cmd)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Finish()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
228
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/tracedredis_test.go
generated
vendored
Normal file
228
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/go-redis/tracedredis_test.go
generated
vendored
Normal file
|
@ -0,0 +1,228 @@
|
|||
package goredistrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.Set("test_key", "test_value", 0)
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span := spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "set test_key test_value: ")
|
||||
assert.Equal(span.GetMeta("redis.args_length"), "3")
|
||||
}
|
||||
|
||||
func TestPipeline(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default db
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
pipeline := client.Pipeline()
|
||||
pipeline.Expire("pipeline_counter", time.Hour)
|
||||
|
||||
// Exec with context test
|
||||
pipeline.ExecWithContext(context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span := spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.pipeline_length"), "1")
|
||||
assert.Equal(span.Resource, "expire pipeline_counter 3600: false\n")
|
||||
|
||||
pipeline.Expire("pipeline_counter", time.Hour)
|
||||
pipeline.Expire("pipeline_counter_1", time.Minute)
|
||||
|
||||
// Rewriting Exec
|
||||
pipeline.Exec()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces = testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans = traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
span = spans[0]
|
||||
assert.Equal(span.Service, "my-redis")
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("redis.pipeline_length"), "2")
|
||||
assert.Equal(span.Resource, "expire pipeline_counter 3600: false\nexpire pipeline_counter_1 60: false\n")
|
||||
}
|
||||
|
||||
func TestChildSpan(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parent_span := testTracer.NewChildSpanFromContext("parent_span", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parent_span)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.SetContext(ctx)
|
||||
|
||||
client.Set("test_key", "test_value", 0)
|
||||
parent_span.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var child_span, pspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "redis.command":
|
||||
child_span = s
|
||||
case "parent_span":
|
||||
pspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(child_span, "there should be a child redis.command span")
|
||||
assert.NotNil(child_span, "there should be a parent span")
|
||||
|
||||
assert.Equal(child_span.ParentID, pspan.SpanID)
|
||||
assert.Equal(child_span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(child_span.GetMeta("out.port"), "56379")
|
||||
}
|
||||
|
||||
func TestMultipleCommands(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
client.Set("test_key", "test_value", 0)
|
||||
client.Get("test_key")
|
||||
client.Incr("int_key")
|
||||
client.ClientList()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 4)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
// Checking all commands were recorded
|
||||
var commands [4]string
|
||||
for i := 0; i < 4; i++ {
|
||||
commands[i] = traces[i][0].GetMeta("redis.raw_command")
|
||||
}
|
||||
assert.Contains(commands, "set test_key test_value: ")
|
||||
assert.Contains(commands, "get test_key: ")
|
||||
assert.Contains(commands, "incr int_key: 0")
|
||||
assert.Contains(commands, "client list: ")
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
opts := &redis.Options{
|
||||
Addr: "127.0.0.1:56379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
}
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
client := NewTracedClient(opts, testTracer, "my-redis")
|
||||
err := client.Get("non_existent_key")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Err().Error())
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "get non_existent_key: ")
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
27
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/example_test.go
generated
vendored
Normal file
27
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package gocqltrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
// To trace Cassandra commands, use our query wrapper TraceQuery.
|
||||
func Example() {
|
||||
|
||||
// Initialise a Cassandra session as usual, create a query.
|
||||
cluster := gocql.NewCluster("127.0.0.1")
|
||||
session, _ := cluster.CreateSession()
|
||||
query := session.Query("CREATE KEYSPACE if not exists trace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor': 1}")
|
||||
|
||||
// Use context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// Wrap the query to trace it and pass the context for inheritance
|
||||
tracedQuery := TraceQuery("ServiceName", tracer.DefaultTracer, query)
|
||||
tracedQuery.WithContext(ctx)
|
||||
|
||||
// Execute your query as usual
|
||||
tracedQuery.Exec()
|
||||
}
|
146
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/gocqltrace.go
generated
vendored
Normal file
146
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/gocqltrace.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Package gocqltrace provides tracing for the Cassandra Gocql client (https://github.com/gocql/gocql)
|
||||
package gocqltrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
// TracedQuery inherits from gocql.Query, it keeps the tracer and the context.
|
||||
type TracedQuery struct {
|
||||
*gocql.Query
|
||||
p traceParams
|
||||
traceContext context.Context
|
||||
}
|
||||
|
||||
// TracedIter inherits from gocql.Iter and contains a span.
|
||||
type TracedIter struct {
|
||||
*gocql.Iter
|
||||
span *tracer.Span
|
||||
}
|
||||
|
||||
// traceParams containes fields and metadata useful for command tracing
|
||||
type traceParams struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
keyspace string
|
||||
paginated string
|
||||
consistancy string
|
||||
query string
|
||||
}
|
||||
|
||||
// TraceQuery wraps a gocql.Query into a TracedQuery
|
||||
func TraceQuery(service string, tracer *tracer.Tracer, q *gocql.Query) *TracedQuery {
|
||||
stringQuery := `"` + strings.SplitN(q.String(), "\"", 3)[1] + `"`
|
||||
stringQuery, err := strconv.Unquote(stringQuery)
|
||||
if err != nil {
|
||||
// An invalid string, so that the trace is not dropped
|
||||
// due to having an empty resource
|
||||
stringQuery = "_"
|
||||
}
|
||||
|
||||
tq := &TracedQuery{q, traceParams{tracer, service, "", "false", strconv.Itoa(int(q.GetConsistency())), stringQuery}, context.Background()}
|
||||
tracer.SetServiceInfo(service, ext.CassandraType, ext.AppTypeDB)
|
||||
return tq
|
||||
}
|
||||
|
||||
// WithContext rewrites the original function so that ctx can be used for inheritance
|
||||
func (tq *TracedQuery) WithContext(ctx context.Context) *TracedQuery {
|
||||
tq.traceContext = ctx
|
||||
tq.Query.WithContext(ctx)
|
||||
return tq
|
||||
}
|
||||
|
||||
// PageState rewrites the original function so that spans are aware of the change.
|
||||
func (tq *TracedQuery) PageState(state []byte) *TracedQuery {
|
||||
tq.p.paginated = "true"
|
||||
tq.Query = tq.Query.PageState(state)
|
||||
return tq
|
||||
}
|
||||
|
||||
// NewChildSpan creates a new span from the traceParams and the context.
|
||||
func (tq *TracedQuery) NewChildSpan(ctx context.Context) *tracer.Span {
|
||||
span := tq.p.tracer.NewChildSpanFromContext(ext.CassandraQuery, ctx)
|
||||
span.Type = ext.CassandraType
|
||||
span.Service = tq.p.service
|
||||
span.Resource = tq.p.query
|
||||
span.SetMeta(ext.CassandraPaginated, tq.p.paginated)
|
||||
span.SetMeta(ext.CassandraKeyspace, tq.p.keyspace)
|
||||
return span
|
||||
}
|
||||
|
||||
// Exec is rewritten so that it passes by our custom Iter
|
||||
func (tq *TracedQuery) Exec() error {
|
||||
return tq.Iter().Close()
|
||||
}
|
||||
|
||||
// MapScan wraps in a span query.MapScan call.
|
||||
func (tq *TracedQuery) MapScan(m map[string]interface{}) error {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
err := tq.Query.MapScan(m)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Scan wraps in a span query.Scan call.
|
||||
func (tq *TracedQuery) Scan(dest ...interface{}) error {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
err := tq.Query.Scan(dest...)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ScanCAS wraps in a span query.ScanCAS call.
|
||||
func (tq *TracedQuery) ScanCAS(dest ...interface{}) (applied bool, err error) {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
defer span.Finish()
|
||||
applied, err = tq.Query.ScanCAS(dest...)
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
return applied, err
|
||||
}
|
||||
|
||||
// Iter starts a new span at query.Iter call.
|
||||
func (tq *TracedQuery) Iter() *TracedIter {
|
||||
span := tq.NewChildSpan(tq.traceContext)
|
||||
iter := tq.Query.Iter()
|
||||
span.SetMeta(ext.CassandraRowCount, strconv.Itoa(iter.NumRows()))
|
||||
span.SetMeta(ext.CassandraConsistencyLevel, strconv.Itoa(int(tq.GetConsistency())))
|
||||
|
||||
columns := iter.Columns()
|
||||
if len(columns) > 0 {
|
||||
span.SetMeta(ext.CassandraKeyspace, columns[0].Keyspace)
|
||||
} else {
|
||||
}
|
||||
tIter := &TracedIter{iter, span}
|
||||
if tIter.Host() != nil {
|
||||
tIter.span.SetMeta(ext.TargetHost, tIter.Iter.Host().HostID())
|
||||
tIter.span.SetMeta(ext.TargetPort, strconv.Itoa(tIter.Iter.Host().Port()))
|
||||
tIter.span.SetMeta(ext.CassandraCluster, tIter.Iter.Host().DataCenter())
|
||||
|
||||
}
|
||||
return tIter
|
||||
}
|
||||
|
||||
// Close closes the TracedIter and finish the span created on Iter call.
|
||||
func (tIter *TracedIter) Close() error {
|
||||
err := tIter.Iter.Close()
|
||||
if err != nil {
|
||||
tIter.span.SetError(err)
|
||||
}
|
||||
tIter.span.Finish()
|
||||
return err
|
||||
}
|
132
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/gocqltrace_test.go
generated
vendored
Normal file
132
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gocql/gocqltrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
package gocqltrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
// TestMain sets up the Keyspace and table if they do not exist
|
||||
func TestMain(m *testing.M) {
|
||||
cluster := gocql.NewCluster("127.0.0.1:59042")
|
||||
session, _ := cluster.CreateSession()
|
||||
|
||||
// Ensures test keyspace and table person exists.
|
||||
session.Query("CREATE KEYSPACE if not exists trace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor': 1}").Exec()
|
||||
session.Query("CREATE TABLE if not exists trace.person (name text PRIMARY KEY, age int, description text)").Exec()
|
||||
session.Query("INSERT INTO trace.person (name, age, description) VALUES ('Cassandra', 100, 'A cruel mistress')").Exec()
|
||||
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestErrorWrapper(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
cluster := gocql.NewCluster("127.0.0.1:59042")
|
||||
session, _ := cluster.CreateSession()
|
||||
q := session.Query("CREATE KEYSPACE trace WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 };")
|
||||
err := TraceQuery("ServiceName", testTracer, q).Exec()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Error())
|
||||
assert.Equal(span.Name, ext.CassandraQuery)
|
||||
assert.Equal(span.Resource, "CREATE KEYSPACE trace WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 };")
|
||||
assert.Equal(span.Service, "ServiceName")
|
||||
assert.Equal(span.GetMeta(ext.CassandraConsistencyLevel), "4")
|
||||
assert.Equal(span.GetMeta(ext.CassandraPaginated), "false")
|
||||
|
||||
// Not added in case of an error
|
||||
assert.Equal(span.GetMeta(ext.TargetHost), "")
|
||||
assert.Equal(span.GetMeta(ext.TargetPort), "")
|
||||
assert.Equal(span.GetMeta(ext.CassandraCluster), "")
|
||||
assert.Equal(span.GetMeta(ext.CassandraKeyspace), "")
|
||||
}
|
||||
|
||||
func TestChildWrapperSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parentSpan := testTracer.NewChildSpanFromContext("parentSpan", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parentSpan)
|
||||
|
||||
cluster := gocql.NewCluster("127.0.0.1:59042")
|
||||
session, _ := cluster.CreateSession()
|
||||
q := session.Query("SELECT * from trace.person")
|
||||
tq := TraceQuery("TestServiceName", testTracer, q)
|
||||
tq.WithContext(ctx).Exec()
|
||||
parentSpan.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var childSpan, pSpan *tracer.Span
|
||||
if spans[0].ParentID == spans[1].SpanID {
|
||||
childSpan = spans[0]
|
||||
pSpan = spans[1]
|
||||
} else {
|
||||
childSpan = spans[1]
|
||||
pSpan = spans[0]
|
||||
}
|
||||
assert.Equal(pSpan.Name, "parentSpan")
|
||||
assert.Equal(childSpan.ParentID, pSpan.SpanID)
|
||||
assert.Equal(childSpan.Name, ext.CassandraQuery)
|
||||
assert.Equal(childSpan.Resource, "SELECT * from trace.person")
|
||||
assert.Equal(childSpan.GetMeta(ext.CassandraKeyspace), "trace")
|
||||
assert.Equal(childSpan.GetMeta(ext.TargetPort), "59042")
|
||||
assert.Equal(childSpan.GetMeta(ext.TargetHost), "127.0.0.1")
|
||||
assert.Equal(childSpan.GetMeta(ext.CassandraCluster), "datacenter1")
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
31
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/example_test.go
generated
vendored
Normal file
31
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package muxtrace_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// handler is a simple handlerFunc that logs some data from the span
|
||||
// that is injected into the requests' context.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
fmt.Printf("tracing service:%s resource:%s", span.Service, span.Resource)
|
||||
w.Write([]byte("hello world"))
|
||||
}
|
||||
|
||||
func Example() {
|
||||
router := mux.NewRouter()
|
||||
muxTracer := muxtrace.NewMuxTracer("my-web-app", tracer.DefaultTracer)
|
||||
|
||||
// Add traced routes directly.
|
||||
muxTracer.HandleFunc(router, "/users", handler)
|
||||
|
||||
// and subroutes as well.
|
||||
subrouter := router.PathPrefix("/user").Subrouter()
|
||||
muxTracer.HandleFunc(subrouter, "/view", handler)
|
||||
muxTracer.HandleFunc(subrouter, "/create", handler)
|
||||
}
|
127
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/muxtrace.go
generated
vendored
Normal file
127
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/muxtrace.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Package muxtrace provides tracing functions for the Gorilla Mux framework.
|
||||
package muxtrace
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// MuxTracer is used to trace requests in a mux server.
|
||||
type MuxTracer struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
}
|
||||
|
||||
// NewMuxTracer creates a MuxTracer for the given service and tracer.
|
||||
func NewMuxTracer(service string, t *tracer.Tracer) *MuxTracer {
|
||||
t.SetServiceInfo(service, "gorilla", ext.AppTypeWeb)
|
||||
return &MuxTracer{
|
||||
tracer: t,
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
// TraceHandleFunc will return a HandlerFunc that will wrap tracing around the
|
||||
// given handler func.
|
||||
func (m *MuxTracer) TraceHandleFunc(handler http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
return func(writer http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// bail our if tracing isn't enabled.
|
||||
if !m.tracer.Enabled() {
|
||||
handler(writer, req)
|
||||
return
|
||||
}
|
||||
|
||||
// trace the request
|
||||
tracedRequest, span := m.trace(req)
|
||||
defer span.Finish()
|
||||
|
||||
// trace the response
|
||||
tracedWriter := newTracedResponseWriter(span, writer)
|
||||
|
||||
// run the request
|
||||
handler(tracedWriter, tracedRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleFunc will add a traced version of the given handler to the router.
|
||||
func (m *MuxTracer) HandleFunc(router *mux.Router, pattern string, handler http.HandlerFunc) *mux.Route {
|
||||
return router.HandleFunc(pattern, m.TraceHandleFunc(handler))
|
||||
}
|
||||
|
||||
// span will create a span for the given request.
|
||||
func (m *MuxTracer) trace(req *http.Request) (*http.Request, *tracer.Span) {
|
||||
route := mux.CurrentRoute(req)
|
||||
path, err := route.GetPathTemplate()
|
||||
if err != nil {
|
||||
// when route doesn't define a path
|
||||
path = "unknown"
|
||||
}
|
||||
|
||||
resource := req.Method + " " + path
|
||||
|
||||
span := m.tracer.NewRootSpan("mux.request", m.service, resource)
|
||||
span.Type = ext.HTTPType
|
||||
span.SetMeta(ext.HTTPMethod, req.Method)
|
||||
span.SetMeta(ext.HTTPURL, path)
|
||||
|
||||
// patch the span onto the request context.
|
||||
treq := SetRequestSpan(req, span)
|
||||
return treq, span
|
||||
}
|
||||
|
||||
// tracedResponseWriter is a small wrapper around an http response writer that will
|
||||
// intercept and store the status of a request.
|
||||
type tracedResponseWriter struct {
|
||||
span *tracer.Span
|
||||
w http.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
func newTracedResponseWriter(span *tracer.Span, w http.ResponseWriter) *tracedResponseWriter {
|
||||
return &tracedResponseWriter{
|
||||
span: span,
|
||||
w: w}
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) Header() http.Header {
|
||||
return t.w.Header()
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) Write(b []byte) (int, error) {
|
||||
if t.status == 0 {
|
||||
t.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return t.w.Write(b)
|
||||
}
|
||||
|
||||
func (t *tracedResponseWriter) WriteHeader(status int) {
|
||||
t.w.WriteHeader(status)
|
||||
t.status = status
|
||||
t.span.SetMeta(ext.HTTPCode, strconv.Itoa(status))
|
||||
if status >= 500 && status < 600 {
|
||||
t.span.Error = 1
|
||||
}
|
||||
}
|
||||
|
||||
// SetRequestSpan sets the span on the request's context.
|
||||
func SetRequestSpan(r *http.Request, span *tracer.Span) *http.Request {
|
||||
if r == nil || span == nil {
|
||||
return r
|
||||
}
|
||||
|
||||
ctx := tracer.ContextWithSpan(r.Context(), span)
|
||||
return r.WithContext(ctx)
|
||||
}
|
||||
|
||||
// GetRequestSpan will return the span associated with the given request. It
|
||||
// will return nil/false if it doesn't exist.
|
||||
func GetRequestSpan(r *http.Request) (*tracer.Span, bool) {
|
||||
span, ok := tracer.SpanFromContext(r.Context())
|
||||
return span, ok
|
||||
}
|
206
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/muxtrace_test.go
generated
vendored
Normal file
206
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/gorilla/muxtrace/muxtrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
package muxtrace
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMuxTracerDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testTracer, testTransport, muxTracer := getTestTracer("disabled-service")
|
||||
router := mux.NewRouter()
|
||||
muxTracer.HandleFunc(router, "/disabled", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("disabled!"))
|
||||
assert.Nil(err)
|
||||
// Ensure we have no tracing context.
|
||||
span, ok := tracer.SpanFromContext(r.Context())
|
||||
assert.Nil(span)
|
||||
assert.False(ok)
|
||||
})
|
||||
testTracer.SetEnabled(false) // the key line in this test.
|
||||
|
||||
// make the request
|
||||
req := httptest.NewRequest("GET", "/disabled", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "disabled!")
|
||||
|
||||
// assert nothing was traced.
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 0)
|
||||
}
|
||||
|
||||
func TestMuxTracerSubrequest(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Send and verify a 200 request
|
||||
for _, url := range []string{"/sub/child1", "/sub/child2"} {
|
||||
tracer, transport, router := setup(t)
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "200!")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMuxTracer200(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// setup
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// Send and verify a 200 request
|
||||
url := "/200"
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 200)
|
||||
assert.Equal(writer.Body.String(), "200!")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "200")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(0))
|
||||
}
|
||||
|
||||
func TestMuxTracer500(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// setup
|
||||
tracer, transport, router := setup(t)
|
||||
|
||||
// SEnd and verify a 200 request
|
||||
url := "/500"
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
writer := httptest.NewRecorder()
|
||||
router.ServeHTTP(writer, req)
|
||||
assert.Equal(writer.Code, 500)
|
||||
assert.Equal(writer.Body.String(), "500!\n")
|
||||
|
||||
// ensure properly traced
|
||||
tracer.ForceFlush()
|
||||
traces := transport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
|
||||
s := spans[0]
|
||||
assert.Equal(s.Name, "mux.request")
|
||||
assert.Equal(s.Service, "my-service")
|
||||
assert.Equal(s.Resource, "GET "+url)
|
||||
assert.Equal(s.GetMeta("http.status_code"), "500")
|
||||
assert.Equal(s.GetMeta("http.method"), "GET")
|
||||
assert.Equal(s.GetMeta("http.url"), url)
|
||||
assert.Equal(s.Error, int32(1))
|
||||
}
|
||||
|
||||
// test handlers
|
||||
|
||||
func handler200(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte("200!"))
|
||||
assert.Nil(err)
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Duration, int64(0))
|
||||
}
|
||||
}
|
||||
|
||||
func handler500(t *testing.T) http.HandlerFunc {
|
||||
assert := assert.New(t)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "500!", http.StatusInternalServerError)
|
||||
span := tracer.SpanFromContextDefault(r.Context())
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Duration, int64(0))
|
||||
}
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (*tracer.Tracer, *dummyTransport, *mux.Router) {
|
||||
tracer, transport, mt := getTestTracer("my-service")
|
||||
r := mux.NewRouter()
|
||||
|
||||
h200 := handler200(t)
|
||||
h500 := handler500(t)
|
||||
|
||||
// Ensure we can use HandleFunc and it returns a route
|
||||
mt.HandleFunc(r, "/200", h200).Methods("Get")
|
||||
// And we can allso handle a bare func
|
||||
r.HandleFunc("/500", mt.TraceHandleFunc(h500))
|
||||
|
||||
// do a subrouter (one in each way)
|
||||
sub := r.PathPrefix("/sub").Subrouter()
|
||||
sub.HandleFunc("/child1", mt.TraceHandleFunc(h200))
|
||||
mt.HandleFunc(sub, "/child2", h200)
|
||||
|
||||
return tracer, transport, r
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer(service string) (*tracer.Tracer, *dummyTransport, *MuxTracer) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
muxTracer := NewMuxTracer(service, tracer)
|
||||
return tracer, transport, muxTracer
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
59
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/example_test.go
generated
vendored
Normal file
59
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package redigotrace_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
redigotrace "github.com/DataDog/dd-trace-go/tracer/contrib/redigo"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
// To start tracing Redis commands, use the TracedDial function to create a connection,
|
||||
// passing in a service name of choice.
|
||||
func Example() {
|
||||
c, _ := redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request'
|
||||
c.Do("SET", "food", "cheese", ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
func ExampleTracedConn() {
|
||||
c, _ := redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
|
||||
// Emit spans per command by using your Redis connection as usual
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
|
||||
// Use a context to pass information down the call chain
|
||||
root := tracer.NewRootSpan("parent.request", "web", "/home")
|
||||
ctx := root.Context(context.Background())
|
||||
|
||||
// When passed a context as the final argument, c.Do will emit a span inheriting from 'parent.request'
|
||||
c.Do("SET", "food", "cheese", ctx)
|
||||
root.Finish()
|
||||
}
|
||||
|
||||
// Alternatively, provide a redis URL to the TracedDialURL function
|
||||
func Example_dialURL() {
|
||||
c, _ := redigotrace.TracedDialURL("my-redis-backend", tracer.DefaultTracer, "redis://127.0.0.1:6379")
|
||||
c.Do("SET", "vehicle", "truck")
|
||||
}
|
||||
|
||||
// When using a redigo Pool, set your Dial function to return a traced connection
|
||||
func Example_pool() {
|
||||
pool := &redis.Pool{
|
||||
Dial: func() (redis.Conn, error) {
|
||||
return redigotrace.TracedDial("my-redis-backend", tracer.DefaultTracer, "tcp", "127.0.0.1:6379")
|
||||
},
|
||||
}
|
||||
|
||||
c := pool.Get()
|
||||
|
||||
c.Do("SET", " whiskey", " glass")
|
||||
}
|
131
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/redigotrace.go
generated
vendored
Normal file
131
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/redigotrace.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Package redigotrace provides tracing for the Redigo Redis client (https://github.com/garyburd/redigo)
|
||||
package redigotrace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/ext"
|
||||
redis "github.com/garyburd/redigo/redis"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TracedConn is an implementation of the redis.Conn interface that supports tracing
|
||||
type TracedConn struct {
|
||||
redis.Conn
|
||||
p traceParams
|
||||
}
|
||||
|
||||
// traceParams contains fields and metadata useful for command tracing
|
||||
type traceParams struct {
|
||||
tracer *tracer.Tracer
|
||||
service string
|
||||
network string
|
||||
host string
|
||||
port string
|
||||
}
|
||||
|
||||
// TracedDial takes a Conn returned by redis.Dial and configures it to emit spans with the given service name
|
||||
func TracedDial(service string, tracer *tracer.Tracer, network, address string, options ...redis.DialOption) (redis.Conn, error) {
|
||||
c, err := redis.Dial(network, address, options...)
|
||||
addr := strings.Split(address, ":")
|
||||
var host, port string
|
||||
if len(addr) == 2 && addr[1] != "" {
|
||||
port = addr[1]
|
||||
} else {
|
||||
port = "6379"
|
||||
}
|
||||
host = addr[0]
|
||||
tracer.SetServiceInfo(service, "redis", ext.AppTypeDB)
|
||||
tc := TracedConn{c, traceParams{tracer, service, network, host, port}}
|
||||
return tc, err
|
||||
}
|
||||
|
||||
// TracedDialURL takes a Conn returned by redis.DialURL and configures it to emit spans with the given service name
|
||||
func TracedDialURL(service string, tracer *tracer.Tracer, rawurl string, options ...redis.DialOption) (redis.Conn, error) {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return TracedConn{}, err
|
||||
}
|
||||
|
||||
// Getting host and port, usind code from https://github.com/garyburd/redigo/blob/master/redis/conn.go#L226
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
host = u.Host
|
||||
port = "6379"
|
||||
}
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
// Set in redis.DialUrl source code
|
||||
network := "tcp"
|
||||
c, err := redis.DialURL(rawurl, options...)
|
||||
tc := TracedConn{c, traceParams{tracer, service, network, host, port}}
|
||||
return tc, err
|
||||
}
|
||||
|
||||
// NewChildSpan creates a span inheriting from the given context. It adds to the span useful metadata about the traced Redis connection
|
||||
func (tc TracedConn) NewChildSpan(ctx context.Context) *tracer.Span {
|
||||
span := tc.p.tracer.NewChildSpanFromContext("redis.command", ctx)
|
||||
span.Service = tc.p.service
|
||||
span.SetMeta("out.network", tc.p.network)
|
||||
span.SetMeta("out.port", tc.p.port)
|
||||
span.SetMeta("out.host", tc.p.host)
|
||||
return span
|
||||
}
|
||||
|
||||
// Do wraps redis.Conn.Do. It sends a command to the Redis server and returns the received reply.
|
||||
// In the process it emits a span containing key information about the command sent.
|
||||
// When passed a context.Context as the final argument, Do will ensure that any span created
|
||||
// inherits from this context. The rest of the arguments are passed through to the Redis server unchanged
|
||||
func (tc TracedConn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
var ctx context.Context
|
||||
var ok bool
|
||||
if len(args) > 0 {
|
||||
ctx, ok = args[len(args)-1].(context.Context)
|
||||
if ok {
|
||||
args = args[:len(args)-1]
|
||||
}
|
||||
}
|
||||
|
||||
span := tc.NewChildSpan(ctx)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
span.SetError(err)
|
||||
}
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
span.SetMeta("redis.args_length", strconv.Itoa(len(args)))
|
||||
|
||||
if len(commandName) > 0 {
|
||||
span.Resource = commandName
|
||||
} else {
|
||||
// When the command argument to the Do method is "", then the Do method will flush the output buffer
|
||||
// See https://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining
|
||||
span.Resource = "redigo.Conn.Flush"
|
||||
}
|
||||
var b bytes.Buffer
|
||||
b.WriteString(commandName)
|
||||
for _, arg := range args {
|
||||
b.WriteString(" ")
|
||||
switch arg := arg.(type) {
|
||||
case string:
|
||||
b.WriteString(arg)
|
||||
case int:
|
||||
b.WriteString(strconv.Itoa(arg))
|
||||
case int32:
|
||||
b.WriteString(strconv.FormatInt(int64(arg), 10))
|
||||
case int64:
|
||||
b.WriteString(strconv.FormatInt(arg, 10))
|
||||
case fmt.Stringer:
|
||||
b.WriteString(arg.String())
|
||||
}
|
||||
}
|
||||
span.SetMeta("redis.raw_command", b.String())
|
||||
return tc.Conn.Do(commandName, args...)
|
||||
}
|
214
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/redigotrace_test.go
generated
vendored
Normal file
214
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/redigo/redigotrace_test.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
package redigotrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
c.Do("SET", 1, "truck")
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "SET")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "SET 1 truck")
|
||||
assert.Equal(span.GetMeta("redis.args_length"), "2")
|
||||
}
|
||||
|
||||
func TestCommandError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
_, err := c.Do("NOT_A_COMMAND", context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(int32(span.Error), int32(1))
|
||||
assert.Equal(span.GetMeta("error.msg"), err.Error())
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "NOT_A_COMMAND")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "NOT_A_COMMAND")
|
||||
}
|
||||
|
||||
func TestConnectionError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, _ := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
_, err := TracedDial("redis-service", testTracer, "tcp", "127.0.0.1:1000")
|
||||
|
||||
assert.Contains(err.Error(), "dial tcp 127.0.0.1:1000")
|
||||
}
|
||||
|
||||
func TestInheritance(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
// Parent span
|
||||
ctx := context.Background()
|
||||
parent_span := testTracer.NewChildSpanFromContext("parent_span", ctx)
|
||||
ctx = tracer.ContextWithSpan(ctx, parent_span)
|
||||
client, _ := TracedDial("my_service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
client.Do("SET", "water", "bottle", ctx)
|
||||
parent_span.Finish()
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 2)
|
||||
|
||||
var child_span, pspan *tracer.Span
|
||||
for _, s := range spans {
|
||||
// order of traces in buffer is not garanteed
|
||||
switch s.Name {
|
||||
case "redis.command":
|
||||
child_span = s
|
||||
case "parent_span":
|
||||
pspan = s
|
||||
}
|
||||
}
|
||||
assert.NotNil(child_span, "there should be a child redis.command span")
|
||||
assert.NotNil(child_span, "there should be a parent span")
|
||||
|
||||
assert.Equal(child_span.ParentID, pspan.SpanID)
|
||||
assert.Equal(child_span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(child_span.GetMeta("out.port"), "56379")
|
||||
}
|
||||
|
||||
func TestCommandsToSring(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
stringify_test := TestStruct{Cpython: 57, Cgo: 8}
|
||||
c, _ := TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
c.Do("SADD", "testSet", "a", int(0), int32(1), int64(2), stringify_test, context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
|
||||
assert.Equal(span.Name, "redis.command")
|
||||
assert.Equal(span.Service, "my-service")
|
||||
assert.Equal(span.Resource, "SADD")
|
||||
assert.Equal(span.GetMeta("out.host"), "127.0.0.1")
|
||||
assert.Equal(span.GetMeta("out.port"), "56379")
|
||||
assert.Equal(span.GetMeta("redis.raw_command"), "SADD testSet a 0 1 2 [57, 8]")
|
||||
}
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
|
||||
pool := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 3,
|
||||
IdleTimeout: 23,
|
||||
Wait: true,
|
||||
Dial: func() (redis.Conn, error) {
|
||||
return TracedDial("my-service", testTracer, "tcp", "127.0.0.1:56379")
|
||||
},
|
||||
}
|
||||
|
||||
pc := pool.Get()
|
||||
pc.Do("SET", " whiskey", " glass", context.Background())
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
spans := traces[0]
|
||||
assert.Len(spans, 1)
|
||||
span := spans[0]
|
||||
assert.Equal(span.GetMeta("out.network"), "tcp")
|
||||
}
|
||||
|
||||
func TestTracingDialUrl(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testTracer, testTransport := getTestTracer()
|
||||
testTracer.SetDebugLogging(debug)
|
||||
url := "redis://127.0.0.1:56379"
|
||||
client, _ := TracedDialURL("redis-service", testTracer, url)
|
||||
client.Do("SET", "ONE", " TWO", context.Background())
|
||||
|
||||
testTracer.ForceFlush()
|
||||
traces := testTransport.Traces()
|
||||
assert.Len(traces, 1)
|
||||
}
|
||||
|
||||
// TestStruct implements String interface
|
||||
type TestStruct struct {
|
||||
Cpython int
|
||||
Cgo int
|
||||
}
|
||||
|
||||
func (ts TestStruct) String() string {
|
||||
return fmt.Sprintf("[%d, %d]", ts.Cpython, ts.Cgo)
|
||||
}
|
||||
|
||||
// getTestTracer returns a Tracer with a DummyTransport
|
||||
func getTestTracer() (*tracer.Tracer, *dummyTransport) {
|
||||
transport := &dummyTransport{}
|
||||
tracer := tracer.NewTracerTransport(transport)
|
||||
return tracer, transport
|
||||
}
|
||||
|
||||
// dummyTransport is a transport that just buffers spans and encoding
|
||||
type dummyTransport struct {
|
||||
traces [][]*tracer.Span
|
||||
services map[string]tracer.Service
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendTraces(traces [][]*tracer.Span) (*http.Response, error) {
|
||||
t.traces = append(t.traces, traces...)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) SendServices(services map[string]tracer.Service) (*http.Response, error) {
|
||||
t.services = services
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *dummyTransport) Traces() [][]*tracer.Span {
|
||||
traces := t.traces
|
||||
t.traces = nil
|
||||
return traces
|
||||
}
|
||||
func (t *dummyTransport) SetHeader(key, value string) {}
|
169
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/example_test.go
generated
vendored
Normal file
169
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package sqltraced_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// To trace the sql calls, you just need to open your sql.DB with OpenTraced.
|
||||
// All calls through this sql.DB object will then be traced.
|
||||
func Example() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltraced.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// All calls through the database/sql API will then be traced.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// If you want to link your db calls with existing traces, you need to use
|
||||
// the context version of the database/sql API.
|
||||
// Just make sure you are passing the parent span within the context.
|
||||
func Example_context() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltraced.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// We create a parent span and put it within the context.
|
||||
span := tracer.NewRootSpan("postgres.parent", "web-backend", "query-parent")
|
||||
ctx := tracer.ContextWithSpan(context.Background(), span)
|
||||
|
||||
// We need to use the context version of the database/sql API
|
||||
// in order to link this call with the parent span.
|
||||
db.PingContext(ctx)
|
||||
rows, _ := db.QueryContext(ctx, "SELECT * FROM city LIMIT 5")
|
||||
rows.Close()
|
||||
|
||||
stmt, _ := db.PrepareContext(ctx, "INSERT INTO city(name) VALUES($1)")
|
||||
stmt.Exec("New York")
|
||||
stmt, _ = db.PrepareContext(ctx, "SELECT name FROM city LIMIT $1")
|
||||
rows, _ = stmt.Query(1)
|
||||
rows.Close()
|
||||
stmt.Close()
|
||||
|
||||
tx, _ := db.BeginTx(ctx, nil)
|
||||
tx.ExecContext(ctx, "INSERT INTO city(name) VALUES('New York')")
|
||||
rows, _ = tx.QueryContext(ctx, "SELECT * FROM city LIMIT 5")
|
||||
rows.Close()
|
||||
stmt, _ = tx.PrepareContext(ctx, "SELECT name FROM city LIMIT $1")
|
||||
rows, _ = stmt.Query(1)
|
||||
rows.Close()
|
||||
stmt.Close()
|
||||
tx.Commit()
|
||||
|
||||
// Calling span.Finish() will send the span into the tracer's buffer
|
||||
// and then being processed.
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
// You can trace all drivers implementing the database/sql/driver interface.
|
||||
// For example, you can trace the go-sql-driver/mysql with the following code.
|
||||
func Example_mySQL() {
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
db, err := sqltraced.OpenTraced(&mysql.MySQLDriver{}, "user:password@/dbname", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// All calls through the database/sql API will then be traced.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// OpenTraced will first register a traced version of the driver and then will return the sql.DB object
|
||||
// that holds the connection with the database.
|
||||
func ExampleOpenTraced() {
|
||||
// The first argument is a reference to the driver to trace.
|
||||
// The second argument is the dataSourceName.
|
||||
// The third argument is used to specify the name of the service under which traces will appear in the Datadog app.
|
||||
// The last argument allows you to specify a custom tracer to use for tracing.
|
||||
db, err := sqltraced.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// You can use a custom tracer by passing it through the optional last argument of OpenTraced.
|
||||
func ExampleOpenTraced_tracer() {
|
||||
// Create and customize a new tracer that will forward 50% of generated traces to the agent.
|
||||
// (useful to manage resource usage in high-throughput environments)
|
||||
trc := tracer.NewTracer()
|
||||
trc.SetSampleRate(0.5)
|
||||
|
||||
// Pass your custom tracer through the last argument of OpenTraced to trace your db calls with it.
|
||||
db, err := sqltraced.OpenTraced(&pq.Driver{}, "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// If you need more granularity, you can register the traced driver seperately from the Open call.
|
||||
func ExampleRegister() {
|
||||
// Register a traced version of your driver.
|
||||
sqltraced.Register("postgres", &pq.Driver{})
|
||||
|
||||
// Returns a sql.DB object that holds the traced connection to the database.
|
||||
// Note: the sql.DB object returned by sql.Open will not be traced so make sure to use sqltraced.Open.
|
||||
db, _ := sqltraced.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
defer db.Close()
|
||||
|
||||
// Use the database/sql API as usual and see traces appear in the Datadog app.
|
||||
rows, err := db.Query("SELECT name FROM users WHERE age=?", 27)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
|
||||
// You can use a custom tracer by passing it through the optional last argument of Register.
|
||||
func ExampleRegister_tracer() {
|
||||
// Create and customize a new tracer that will forward 50% of generated traces to the agent.
|
||||
// (useful to manage resource usage in high-throughput environments)
|
||||
trc := tracer.NewTracer()
|
||||
trc.SetSampleRate(0.5)
|
||||
|
||||
// Register a traced version of your driver and specify to use the previous tracer
|
||||
// to send the traces to the agent.
|
||||
sqltraced.Register("postgres", &pq.Driver{}, trc)
|
||||
|
||||
// Returns a sql.DB object that holds the traced connection to the database.
|
||||
// Note: the sql.DB object returned by sql.Open will not be traced so make sure to use sqltraced.Open.
|
||||
db, _ := sqltraced.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=disable", "web-backend")
|
||||
defer db.Close()
|
||||
}
|
41
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/mysql_test.go
generated
vendored
Normal file
41
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/mysql_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sqltraced
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/DataDog/dd-trace-go/tracer"
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/sqltest"
|
||||
"github.com/DataDog/dd-trace-go/tracer/tracertest"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func TestMySQL(t *testing.T) {
|
||||
trc, transport := tracertest.GetTestTracer()
|
||||
db, err := OpenTraced(&mysql.MySQLDriver{}, "test:test@tcp(127.0.0.1:53306)/test", "mysql-test", trc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
testDB := &sqltest.DB{
|
||||
DB: db,
|
||||
Tracer: trc,
|
||||
Transport: transport,
|
||||
DriverName: "mysql",
|
||||
}
|
||||
|
||||
expectedSpan := &tracer.Span{
|
||||
Name: "mysql.query",
|
||||
Service: "mysql-test",
|
||||
Type: "sql",
|
||||
}
|
||||
expectedSpan.Meta = map[string]string{
|
||||
"db.user": "test",
|
||||
"out.host": "127.0.0.1",
|
||||
"out.port": "53306",
|
||||
"db.name": "test",
|
||||
}
|
||||
|
||||
sqltest.AllSQLTests(t, testDB, expectedSpan)
|
||||
}
|
42
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parse.go
generated
vendored
Normal file
42
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package sqltraced
|
||||
|
||||
import (
|
||||
"github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn"
|
||||
)
|
||||
|
||||
// parseDSN returns all information passed through the DSN:
|
||||
func parseDSN(driverName, dsn string) (meta map[string]string, err error) {
|
||||
switch driverName {
|
||||
case "mysql":
|
||||
meta, err = parsedsn.MySQL(dsn)
|
||||
case "postgres":
|
||||
meta, err = parsedsn.Postgres(dsn)
|
||||
}
|
||||
meta = normalize(meta)
|
||||
return meta, err
|
||||
}
|
||||
|
||||
func normalize(meta map[string]string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
if nk, ok := normalizeKey(k); ok {
|
||||
m[nk] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func normalizeKey(k string) (string, bool) {
|
||||
switch k {
|
||||
case "user":
|
||||
return "db.user", true
|
||||
case "application_name":
|
||||
return "db.application", true
|
||||
case "dbname":
|
||||
return "db.name", true
|
||||
case "host", "port":
|
||||
return "out." + k, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
44
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parse_test.go
generated
vendored
Normal file
44
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parse_test.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package sqltraced
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseDSN(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
expected := map[string]string{
|
||||
"db.user": "bob",
|
||||
"out.host": "1.2.3.4",
|
||||
"out.port": "5432",
|
||||
"db.name": "mydb",
|
||||
}
|
||||
m, err := parseDSN("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
|
||||
expected = map[string]string{
|
||||
"db.user": "bob",
|
||||
"out.host": "1.2.3.4",
|
||||
"out.port": "5432",
|
||||
"db.name": "mydb",
|
||||
}
|
||||
m, err = parseDSN("mysql", "bob:secret@tcp(1.2.3.4:5432)/mydb")
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
|
||||
expected = map[string]string{
|
||||
"out.port": "5433",
|
||||
"out.host": "master-db-master-active.postgres.service.consul",
|
||||
"db.name": "dogdatastaging",
|
||||
"db.application": "trace-api",
|
||||
"db.user": "dog",
|
||||
}
|
||||
dsn := "connect_timeout=0 binary_parameters=no password=zMWmQz26GORmgVVKEbEl dbname=dogdatastaging application_name=trace-api port=5433 sslmode=disable host=master-db-master-active.postgres.service.consul user=dog"
|
||||
m, err = parseDSN("postgres", dsn)
|
||||
assert.Equal(nil, err)
|
||||
assert.True(reflect.DeepEqual(expected, m))
|
||||
}
|
25
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/collations.go
generated
vendored
Normal file
25
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/collations.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
const defaultCollation = "utf8_general_ci"
|
||||
|
||||
// A blacklist of collations which is unsafe to interpolate parameters.
|
||||
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
||||
var unsafeCollations = map[string]bool{
|
||||
"big5_chinese_ci": true,
|
||||
"sjis_japanese_ci": true,
|
||||
"gbk_chinese_ci": true,
|
||||
"big5_bin": true,
|
||||
"gb2312_bin": true,
|
||||
"gbk_bin": true,
|
||||
"sjis_bin": true,
|
||||
"cp932_japanese_ci": true,
|
||||
"cp932_bin": true,
|
||||
}
|
148
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/dsn.go
generated
vendored
Normal file
148
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/dsn.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
|
||||
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
|
||||
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
|
||||
errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
|
||||
)
|
||||
|
||||
// Config is a configuration parsed from a DSN string
|
||||
type Config struct {
|
||||
User string // Username
|
||||
Passwd string // Password (requires User)
|
||||
Net string // Network type
|
||||
Addr string // Network address (requires Net)
|
||||
DBName string // Database name
|
||||
Params map[string]string // Connection parameters
|
||||
Collation string // Connection collation
|
||||
Loc *time.Location // Location for time.Time values
|
||||
MaxAllowedPacket int // Max packet size allowed
|
||||
TLSConfig string // TLS configuration name
|
||||
tls *tls.Config // TLS configuration
|
||||
Timeout time.Duration // Dial timeout
|
||||
ReadTimeout time.Duration // I/O read timeout
|
||||
WriteTimeout time.Duration // I/O write timeout
|
||||
|
||||
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
|
||||
AllowCleartextPasswords bool // Allows the cleartext client side plugin
|
||||
AllowNativePasswords bool // Allows the native password authentication method
|
||||
AllowOldPasswords bool // Allows the old insecure password method
|
||||
ClientFoundRows bool // Return number of matching rows instead of rows changed
|
||||
ColumnsWithAlias bool // Prepend table alias to column names
|
||||
InterpolateParams bool // Interpolate placeholders into query string
|
||||
MultiStatements bool // Allow multiple statements in one query
|
||||
ParseTime bool // Parse time values to time.Time
|
||||
Strict bool // Return warnings as errors
|
||||
}
|
||||
|
||||
// ParseDSN parses the DSN string to a Config
|
||||
func ParseDSN(dsn string) (cfg *Config, err error) {
|
||||
// New config with some default values
|
||||
cfg = &Config{
|
||||
Loc: time.UTC,
|
||||
Collation: defaultCollation,
|
||||
}
|
||||
|
||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
||||
foundSlash := false
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
foundSlash = true
|
||||
var j, k int
|
||||
|
||||
// left part is empty if i <= 0
|
||||
if i > 0 {
|
||||
// [username[:password]@][protocol[(address)]]
|
||||
// Find the last '@' in dsn[:i]
|
||||
for j = i; j >= 0; j-- {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
if dsn[k] == ':' {
|
||||
cfg.Passwd = dsn[k+1 : j]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.User = dsn[:k]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// [protocol[(address)]]
|
||||
// Find the first '(' in dsn[j+1:i]
|
||||
for k = j + 1; k < i; k++ {
|
||||
if dsn[k] == '(' {
|
||||
// dsn[i-1] must be == ')' if an address is specified
|
||||
if dsn[i-1] != ')' {
|
||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
||||
return nil, errInvalidDSNUnescaped
|
||||
}
|
||||
return nil, errInvalidDSNAddr
|
||||
}
|
||||
cfg.Addr = dsn[k+1 : i-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.Net = dsn[j+1 : k]
|
||||
}
|
||||
|
||||
// dbname[?param1=value1&...¶mN=valueN]
|
||||
// Find the first '?' in dsn[i+1:]
|
||||
for j = i + 1; j < len(dsn); j++ {
|
||||
if dsn[j] == '?' {
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.DBName = dsn[i+1 : j]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSlash && len(dsn) > 0 {
|
||||
return nil, errInvalidDSNNoSlash
|
||||
}
|
||||
|
||||
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
||||
return nil, errInvalidDSNUnsafeCollation
|
||||
}
|
||||
|
||||
// Set default network if empty
|
||||
if cfg.Net == "" {
|
||||
cfg.Net = "tcp"
|
||||
}
|
||||
|
||||
// Set default address if empty
|
||||
if cfg.Addr == "" {
|
||||
switch cfg.Net {
|
||||
case "tcp":
|
||||
cfg.Addr = "127.0.0.1:3306"
|
||||
case "unix":
|
||||
cfg.Addr = "/tmp/mysql.sock"
|
||||
default:
|
||||
return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
3
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/mysql.go
generated
vendored
Normal file
3
vendor/github.com/DataDog/dd-trace-go/tracer/contrib/sqltraced/parsedsn/mysql/mysql.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Package mysql is the minimal fork of go-sql-driver/mysql so we can use their code
|
||||
// to parse the mysql DSNs
|
||||
package mysql
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue