Add DD support (#1596)
This commit is contained in:
parent
95342dfaad
commit
87790dd47c
214 changed files with 69817 additions and 18 deletions
12
vendor/github.com/DataDog/dd-trace-go/.gitignore
generated
vendored
Normal file
12
vendor/github.com/DataDog/dd-trace-go/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# go
|
||||
bin/
|
||||
|
||||
# profiling
|
||||
*.test
|
||||
*.out
|
||||
|
||||
# generic
|
||||
.DS_Store
|
||||
*.cov
|
||||
*.lock
|
||||
*.swp
|
30
vendor/github.com/DataDog/dd-trace-go/Gopkg.toml
generated
vendored
Normal file
30
vendor/github.com/DataDog/dd-trace-go/Gopkg.toml
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Gopkg.toml:
|
||||
# this `dep` file is used only to lock Tracer dependencies. It's not meant to be
|
||||
# used by end users so no integrations dependencies must be added here. If you update
|
||||
# or add a new dependency, remember to commit the `vendor` folder. To prepare
|
||||
# your development environment, remember to use `rake init` instead.
|
||||
|
||||
# ignore integrations dependencies
|
||||
ignored = [
|
||||
"github.com/opentracing/*",
|
||||
"github.com/cihub/seelog",
|
||||
"github.com/gin-gonic/gin",
|
||||
"github.com/go-redis/redis",
|
||||
"github.com/go-sql-driver/mysql",
|
||||
"github.com/gocql/gocql",
|
||||
"github.com/gorilla/mux",
|
||||
"github.com/jmoiron/sqlx",
|
||||
"github.com/lib/pq",
|
||||
"google.golang.org/grpc",
|
||||
"gopkg.in/olivere/elastic.v3",
|
||||
"gopkg.in/olivere/elastic.v5",
|
||||
"github.com/stretchr/*",
|
||||
"github.com/garyburd/*",
|
||||
"github.com/golang/*",
|
||||
"google.golang.org/*",
|
||||
"golang.org/x/*",
|
||||
]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ugorji/go"
|
||||
revision = "9c7f9b7a2bc3a520f7c7b30b34b7f85f47fe27b6"
|
24
vendor/github.com/DataDog/dd-trace-go/LICENSE
generated
vendored
Normal file
24
vendor/github.com/DataDog/dd-trace-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2016, Datadog <info@datadoghq.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Datadog nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2
vendor/github.com/DataDog/dd-trace-go/LICENSE-3rdparty.csv
generated
vendored
Normal file
2
vendor/github.com/DataDog/dd-trace-go/LICENSE-3rdparty.csv
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
Component,Origin,License,Copyright
|
||||
import,io.opentracing,Apache-2.0,Copyright 2016-2017 The OpenTracing Authors
|
|
80
vendor/github.com/DataDog/dd-trace-go/README.md
generated
vendored
Normal file
80
vendor/github.com/DataDog/dd-trace-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
[](https://circleci.com/gh/DataDog/dd-trace-go/tree/master)
|
||||
[](https://godoc.org/github.com/DataDog/dd-trace-go/opentracing)
|
||||
|
||||
Datadog APM client that implements an [OpenTracing](http://opentracing.io) Tracer.
|
||||
|
||||
## Initialization
|
||||
|
||||
To start using the Datadog Tracer with the OpenTracing API, you should first initialize the tracer with a proper `Configuration` object:
|
||||
|
||||
```go
|
||||
import (
|
||||
// ddtrace namespace is suggested
|
||||
ddtrace "github.com/DataDog/dd-trace-go/opentracing"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// create a Tracer configuration
|
||||
config := ddtrace.NewConfiguration()
|
||||
config.ServiceName = "api-intake"
|
||||
config.AgentHostname = "ddagent.consul.local"
|
||||
|
||||
// initialize a Tracer and ensure a graceful shutdown
|
||||
// using the `closer.Close()`
|
||||
tracer, closer, err := ddtrace.NewTracer(config)
|
||||
if err != nil {
|
||||
// handle the configuration error
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
// set the Datadog tracer as a GlobalTracer
|
||||
opentracing.SetGlobalTracer(tracer)
|
||||
startWebServer()
|
||||
}
|
||||
```
|
||||
|
||||
Function `NewTracer(config)` returns an `io.Closer` instance that can be used to gracefully shutdown the `tracer`. It's recommended to always call the `closer.Close()`, otherwise internal buffers are not flushed and you may lose some traces.
|
||||
|
||||
## Usage
|
||||
|
||||
See [Opentracing documentation](https://github.com/opentracing/opentracing-go) for some usage patterns. Legacy documentation is available in [GoDoc format](https://godoc.org/github.com/DataDog/dd-trace-go/tracer).
|
||||
|
||||
## Contributing Quick Start
|
||||
|
||||
Requirements:
|
||||
|
||||
* Go 1.7 or later
|
||||
* Docker
|
||||
* Rake
|
||||
* [gometalinter](https://github.com/alecthomas/gometalinter)
|
||||
|
||||
### Run the tests
|
||||
|
||||
Start the containers defined in `docker-compose.yml` so that integrations can be tested:
|
||||
|
||||
```
|
||||
$ docker-compose up -d
|
||||
$ ./wait-for-services.sh # wait that all services are up and running
|
||||
```
|
||||
|
||||
Fetch package's third-party dependencies (integrations and testing utilities):
|
||||
|
||||
```
|
||||
$ rake init
|
||||
```
|
||||
|
||||
This will only work if your working directory is in $GOPATH/src.
|
||||
|
||||
Now, you can run your tests via :
|
||||
|
||||
```
|
||||
$ rake test:lint # linting via gometalinter
|
||||
$ rake test:all # test the tracer and all integrations
|
||||
$ rake test:race # use the -race flag
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
Automatically traced libraries and frameworks: https://godoc.org/github.com/DataDog/dd-trace-go/tracer#pkg-subdirectories
|
||||
Sample code: https://godoc.org/github.com/DataDog/dd-trace-go/tracer#pkg-examples
|
4
vendor/github.com/DataDog/dd-trace-go/Rakefile
generated
vendored
Normal file
4
vendor/github.com/DataDog/dd-trace-go/Rakefile
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
require_relative 'tasks/common'
|
||||
require_relative 'tasks/vendors'
|
||||
require_relative 'tasks/testing'
|
||||
require_relative 'tasks/benchmarks'
|
40
vendor/github.com/DataDog/dd-trace-go/circle.yml
generated
vendored
Normal file
40
vendor/github.com/DataDog/dd-trace-go/circle.yml
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
machine:
|
||||
services:
|
||||
- docker
|
||||
environment:
|
||||
GODIST: "go1.9.linux-amd64.tar.gz"
|
||||
IMPORT_PATH: "/home/ubuntu/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
|
||||
post:
|
||||
- mkdir -p download
|
||||
- test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST
|
||||
- sudo rm -rf /usr/local/go
|
||||
- sudo tar -C /usr/local -xzf download/$GODIST
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
# clean the workspace
|
||||
- rm -Rf /home/ubuntu/.go_workspace/src/*
|
||||
# we should use an old docker-compose because CircleCI supports
|
||||
# only docker-engine==1.9
|
||||
- pip install docker-compose==1.7.1
|
||||
|
||||
override:
|
||||
# put the package in the right $GOPATH
|
||||
- mkdir -p "$IMPORT_PATH"
|
||||
- rsync -azr --delete ./ "$IMPORT_PATH"
|
||||
- cd "$IMPORT_PATH" && rake init
|
||||
|
||||
test:
|
||||
override:
|
||||
# run the agent and backing services
|
||||
- docker-compose up -d | cat
|
||||
# wait for external services and execute tests
|
||||
- cd "$IMPORT_PATH" && ./wait-for-services.sh
|
||||
- cd "$IMPORT_PATH" && rake test:lint
|
||||
- cd "$IMPORT_PATH" && rake test:all
|
||||
- cd "$IMPORT_PATH" && rake test:race
|
||||
- cd "$IMPORT_PATH" && rake test:coverage
|
||||
|
||||
post:
|
||||
# add the coverage HTML report as CircleCI artifact
|
||||
- cd "$IMPORT_PATH" && go tool cover -html=code.cov -o $CIRCLE_ARTIFACTS/coverage.html
|
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)
|
||||
}
|
||||
}
|
43
vendor/github.com/DataDog/dd-trace-go/docker-compose.yml
generated
vendored
Normal file
43
vendor/github.com/DataDog/dd-trace-go/docker-compose.yml
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
# File for development/ testing purposes
|
||||
|
||||
cassandra:
|
||||
image: cassandra:3.7
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_CASSANDRA_PORT}:9042"
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=$TEST_MYSQL_ROOT_PASSWORD
|
||||
- MYSQL_PASSWORD=$TEST_MYSQL_PASSWORD
|
||||
- MYSQL_USER=$TEST_MYSQL_USER
|
||||
- MYSQL_DATABASE=$TEST_MYSQL_DATABASE
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_MYSQL_PORT}:3306"
|
||||
postgres:
|
||||
image: postgres:9.5
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=$TEST_POSTGRES_PASSWORD
|
||||
- POSTGRES_USER=$TEST_POSTGRES_USER
|
||||
- POSTGRES_DB=$TEST_POSTGRES_DB
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_POSTGRES_PORT}:5432"
|
||||
redis:
|
||||
image: redis:3.2
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_REDIS_PORT}:6379"
|
||||
elasticsearch-v5:
|
||||
image: elasticsearch:5
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_ELASTICSEARCH5_PORT}:9200"
|
||||
elasticsearch-v2:
|
||||
image: elasticsearch:2
|
||||
ports:
|
||||
- "127.0.0.1:${TEST_ELASTICSEARCH2_PORT}:9200"
|
||||
ddagent:
|
||||
image: datadog/docker-dd-agent
|
||||
environment:
|
||||
- DD_APM_ENABLED=true
|
||||
- DD_BIND_HOST=0.0.0.0
|
||||
- DD_API_KEY=invalid_key_but_this_is_fine
|
||||
ports:
|
||||
- "127.0.0.1:8126:8126"
|
58
vendor/github.com/DataDog/dd-trace-go/opentracing/config.go
generated
vendored
Normal file
58
vendor/github.com/DataDog/dd-trace-go/opentracing/config.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Configuration struct configures the Datadog tracer. Please use the NewConfiguration
|
||||
// constructor to begin.
|
||||
type Configuration struct {
|
||||
// Enabled, when false, returns a no-op implementation of the Tracer.
|
||||
Enabled bool
|
||||
|
||||
// Debug, when true, writes details to logs.
|
||||
Debug bool
|
||||
|
||||
// ServiceName specifies the name of this application.
|
||||
ServiceName string
|
||||
|
||||
// SampleRate sets the Tracer sample rate (ext/priority.go).
|
||||
SampleRate float64
|
||||
|
||||
// AgentHostname specifies the hostname of the agent where the traces
|
||||
// are sent to.
|
||||
AgentHostname string
|
||||
|
||||
// AgentPort specifies the port that the agent is listening on.
|
||||
AgentPort string
|
||||
|
||||
// GlobalTags holds a set of tags that will be automatically applied to
|
||||
// all spans.
|
||||
GlobalTags map[string]interface{}
|
||||
|
||||
// TextMapPropagator is an injector used for Context propagation.
|
||||
TextMapPropagator Propagator
|
||||
}
|
||||
|
||||
// NewConfiguration creates a `Configuration` object with default values.
|
||||
func NewConfiguration() *Configuration {
|
||||
// default service name is the Go binary name
|
||||
binaryName := filepath.Base(os.Args[0])
|
||||
|
||||
// Configuration struct with default values
|
||||
return &Configuration{
|
||||
Enabled: true,
|
||||
Debug: false,
|
||||
ServiceName: binaryName,
|
||||
SampleRate: 1,
|
||||
AgentHostname: "localhost",
|
||||
AgentPort: "8126",
|
||||
GlobalTags: make(map[string]interface{}),
|
||||
TextMapPropagator: NewTextMapPropagator("", "", ""),
|
||||
}
|
||||
}
|
||||
|
||||
type noopCloser struct{}
|
||||
|
||||
func (c *noopCloser) Close() error { return nil }
|
58
vendor/github.com/DataDog/dd-trace-go/opentracing/config_test.go
generated
vendored
Normal file
58
vendor/github.com/DataDog/dd-trace-go/opentracing/config_test.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConfigurationDefaults(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
assert.Equal(true, config.Enabled)
|
||||
assert.Equal(false, config.Debug)
|
||||
assert.Equal(float64(1), config.SampleRate)
|
||||
assert.Equal("opentracing.test", config.ServiceName)
|
||||
assert.Equal("localhost", config.AgentHostname)
|
||||
assert.Equal("8126", config.AgentPort)
|
||||
}
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
config.SampleRate = 0
|
||||
config.ServiceName = "api-intake"
|
||||
config.AgentHostname = "ddagent.consul.local"
|
||||
config.AgentPort = "58126"
|
||||
tracer, closer, err := NewTracer(config)
|
||||
assert.NotNil(tracer)
|
||||
assert.NotNil(closer)
|
||||
assert.Nil(err)
|
||||
assert.Equal("api-intake", tracer.(*Tracer).config.ServiceName)
|
||||
}
|
||||
|
||||
func TestTracerServiceName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
config.ServiceName = ""
|
||||
tracer, closer, err := NewTracer(config)
|
||||
assert.Nil(tracer)
|
||||
assert.Nil(closer)
|
||||
assert.NotNil(err)
|
||||
assert.Equal("A Datadog Tracer requires a valid `ServiceName` set", err.Error())
|
||||
}
|
||||
|
||||
func TestDisabledTracer(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
config.Enabled = false
|
||||
tracer, closer, err := NewTracer(config)
|
||||
assert.IsType(&ot.NoopTracer{}, tracer)
|
||||
assert.IsType(&noopCloser{}, closer)
|
||||
assert.Nil(err)
|
||||
}
|
46
vendor/github.com/DataDog/dd-trace-go/opentracing/context.go
generated
vendored
Normal file
46
vendor/github.com/DataDog/dd-trace-go/opentracing/context.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package opentracing
|
||||
|
||||
// SpanContext represents Span state that must propagate to descendant Spans
|
||||
// and across process boundaries.
|
||||
type SpanContext struct {
|
||||
traceID uint64
|
||||
spanID uint64
|
||||
parentID uint64
|
||||
sampled bool
|
||||
span *Span
|
||||
baggage map[string]string
|
||||
}
|
||||
|
||||
// ForeachBaggageItem grants access to all baggage items stored in the
|
||||
// SpanContext
|
||||
func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
|
||||
for k, v := range c.baggage {
|
||||
if !handler(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaggageItem returns an entirely new SpanContext with the
|
||||
// given key:value baggage pair set.
|
||||
func (c SpanContext) WithBaggageItem(key, val string) SpanContext {
|
||||
var newBaggage map[string]string
|
||||
if c.baggage == nil {
|
||||
newBaggage = map[string]string{key: val}
|
||||
} else {
|
||||
newBaggage = make(map[string]string, len(c.baggage)+1)
|
||||
for k, v := range c.baggage {
|
||||
newBaggage[k] = v
|
||||
}
|
||||
newBaggage[key] = val
|
||||
}
|
||||
// Use positional parameters so the compiler will help catch new fields.
|
||||
return SpanContext{
|
||||
traceID: c.traceID,
|
||||
spanID: c.spanID,
|
||||
parentID: c.parentID,
|
||||
sampled: c.sampled,
|
||||
span: c.span,
|
||||
baggage: newBaggage,
|
||||
}
|
||||
}
|
29
vendor/github.com/DataDog/dd-trace-go/opentracing/context_test.go
generated
vendored
Normal file
29
vendor/github.com/DataDog/dd-trace-go/opentracing/context_test.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSpanContextBaggage(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ctx := SpanContext{}
|
||||
ctx = ctx.WithBaggageItem("key", "value")
|
||||
assert.Equal("value", ctx.baggage["key"])
|
||||
}
|
||||
|
||||
func TestSpanContextIterator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
baggageIterator := make(map[string]string)
|
||||
ctx := SpanContext{baggage: map[string]string{"key": "value"}}
|
||||
ctx.ForeachBaggageItem(func(k, v string) bool {
|
||||
baggageIterator[k] = v
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Len(baggageIterator, 1)
|
||||
assert.Equal("value", baggageIterator["key"])
|
||||
}
|
4
vendor/github.com/DataDog/dd-trace-go/opentracing/doc.go
generated
vendored
Normal file
4
vendor/github.com/DataDog/dd-trace-go/opentracing/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Package opentracing implements an OpenTracing (http://opentracing.io)
|
||||
// compatible tracer. A Datadog tracer must be initialized through
|
||||
// a Configuration object as you can see in the following examples.
|
||||
package opentracing
|
30
vendor/github.com/DataDog/dd-trace-go/opentracing/example_context_test.go
generated
vendored
Normal file
30
vendor/github.com/DataDog/dd-trace-go/opentracing/example_context_test.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package opentracing_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// You can leverage the Golang `Context` for intra-process propagation of
|
||||
// Spans. In this example we create a root Span, so that it can be reused
|
||||
// in a nested function to create a child Span.
|
||||
func Example_startContext() {
|
||||
// create a new root Span and return a new Context that includes
|
||||
// the Span itself
|
||||
ctx := context.Background()
|
||||
rootSpan, ctx := opentracing.StartSpanFromContext(ctx, "web.request")
|
||||
defer rootSpan.Finish()
|
||||
|
||||
requestHandler(ctx)
|
||||
}
|
||||
|
||||
func requestHandler(ctx context.Context) {
|
||||
// retrieve the previously set root Span
|
||||
span := opentracing.SpanFromContext(ctx)
|
||||
span.SetTag("resource.name", "/")
|
||||
|
||||
// or simply create a new child Span from the previous Context
|
||||
childSpan, _ := opentracing.StartSpanFromContext(ctx, "sql.query")
|
||||
defer childSpan.Finish()
|
||||
}
|
30
vendor/github.com/DataDog/dd-trace-go/opentracing/example_test.go
generated
vendored
Normal file
30
vendor/github.com/DataDog/dd-trace-go/opentracing/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package opentracing_test
|
||||
|
||||
import (
|
||||
// ddtrace namespace is suggested
|
||||
ddtrace "github.com/DataDog/dd-trace-go/opentracing"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
func Example_initialization() {
|
||||
// create a Tracer configuration
|
||||
config := ddtrace.NewConfiguration()
|
||||
config.ServiceName = "api-intake"
|
||||
config.AgentHostname = "ddagent.consul.local"
|
||||
|
||||
// initialize a Tracer and ensure a graceful shutdown
|
||||
// using the `closer.Close()`
|
||||
tracer, closer, err := ddtrace.NewTracer(config)
|
||||
if err != nil {
|
||||
// handle the configuration error
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
// set the Datadog tracer as a GlobalTracer
|
||||
opentracing.SetGlobalTracer(tracer)
|
||||
startWebServer()
|
||||
}
|
||||
|
||||
func startWebServer() {
|
||||
// start a web server
|
||||
}
|
24
vendor/github.com/DataDog/dd-trace-go/opentracing/example_tracing_test.go
generated
vendored
Normal file
24
vendor/github.com/DataDog/dd-trace-go/opentracing/example_tracing_test.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package opentracing_test
|
||||
|
||||
import (
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// You can use the GlobalTracer to create a root Span. If you need to create a hierarchy,
|
||||
// simply use the `ChildOf` reference
|
||||
func Example_startSpan() {
|
||||
// use the GlobalTracer previously set
|
||||
rootSpan := opentracing.StartSpan("web.request")
|
||||
defer rootSpan.Finish()
|
||||
|
||||
// set the reference to create a hierarchy of spans
|
||||
reference := opentracing.ChildOf(rootSpan.Context())
|
||||
childSpan := opentracing.StartSpan("sql.query", reference)
|
||||
defer childSpan.Finish()
|
||||
|
||||
dbQuery()
|
||||
}
|
||||
|
||||
func dbQuery() {
|
||||
// start a database query
|
||||
}
|
125
vendor/github.com/DataDog/dd-trace-go/opentracing/propagators.go
generated
vendored
Normal file
125
vendor/github.com/DataDog/dd-trace-go/opentracing/propagators.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// Propagator implementations should be able to inject and extract
|
||||
// SpanContexts into an implementation specific carrier.
|
||||
type Propagator interface {
|
||||
// Inject takes the SpanContext and injects it into the carrier using
|
||||
// an implementation specific method.
|
||||
Inject(context ot.SpanContext, carrier interface{}) error
|
||||
|
||||
// Extract returns the SpanContext from the given carrier using an
|
||||
// implementation specific method.
|
||||
Extract(carrier interface{}) (ot.SpanContext, error)
|
||||
}
|
||||
|
||||
const (
|
||||
defaultBaggageHeaderPrefix = "ot-baggage-"
|
||||
defaultTraceIDHeader = "x-datadog-trace-id"
|
||||
defaultParentIDHeader = "x-datadog-parent-id"
|
||||
)
|
||||
|
||||
// NewTextMapPropagator returns a new propagator which uses opentracing.TextMap
|
||||
// to inject and extract values. The parameters specify the prefix that will
|
||||
// be used to prefix baggage header keys along with the trace and parent header.
|
||||
// Empty strings may be provided to use the defaults, which are: "ot-baggage-" as
|
||||
// prefix for baggage headers, "x-datadog-trace-id" and "x-datadog-parent-id" for
|
||||
// trace and parent ID headers.
|
||||
func NewTextMapPropagator(baggagePrefix, traceHeader, parentHeader string) *TextMapPropagator {
|
||||
if baggagePrefix == "" {
|
||||
baggagePrefix = defaultBaggageHeaderPrefix
|
||||
}
|
||||
if traceHeader == "" {
|
||||
traceHeader = defaultTraceIDHeader
|
||||
}
|
||||
if parentHeader == "" {
|
||||
parentHeader = defaultParentIDHeader
|
||||
}
|
||||
return &TextMapPropagator{baggagePrefix, traceHeader, parentHeader}
|
||||
}
|
||||
|
||||
// TextMapPropagator implements a propagator which uses opentracing.TextMap
|
||||
// internally.
|
||||
type TextMapPropagator struct {
|
||||
baggagePrefix string
|
||||
traceHeader string
|
||||
parentHeader string
|
||||
}
|
||||
|
||||
// Inject defines the TextMapPropagator to propagate SpanContext data
|
||||
// out of the current process. The implementation propagates the
|
||||
// TraceID and the current active SpanID, as well as the Span baggage.
|
||||
func (p *TextMapPropagator) Inject(context ot.SpanContext, carrier interface{}) error {
|
||||
ctx, ok := context.(SpanContext)
|
||||
if !ok {
|
||||
return ot.ErrInvalidSpanContext
|
||||
}
|
||||
writer, ok := carrier.(ot.TextMapWriter)
|
||||
if !ok {
|
||||
return ot.ErrInvalidCarrier
|
||||
}
|
||||
|
||||
// propagate the TraceID and the current active SpanID
|
||||
writer.Set(p.traceHeader, strconv.FormatUint(ctx.traceID, 10))
|
||||
writer.Set(p.parentHeader, strconv.FormatUint(ctx.spanID, 10))
|
||||
|
||||
// propagate OpenTracing baggage
|
||||
for k, v := range ctx.baggage {
|
||||
writer.Set(p.baggagePrefix+k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract implements Propagator.
|
||||
func (p *TextMapPropagator) Extract(carrier interface{}) (ot.SpanContext, error) {
|
||||
reader, ok := carrier.(ot.TextMapReader)
|
||||
if !ok {
|
||||
return nil, ot.ErrInvalidCarrier
|
||||
}
|
||||
var err error
|
||||
var traceID, parentID uint64
|
||||
decodedBaggage := make(map[string]string)
|
||||
|
||||
// extract SpanContext fields
|
||||
err = reader.ForeachKey(func(k, v string) error {
|
||||
switch strings.ToLower(k) {
|
||||
case p.traceHeader:
|
||||
traceID, err = strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return ot.ErrSpanContextCorrupted
|
||||
}
|
||||
case p.parentHeader:
|
||||
parentID, err = strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return ot.ErrSpanContextCorrupted
|
||||
}
|
||||
default:
|
||||
lowercaseK := strings.ToLower(k)
|
||||
if strings.HasPrefix(lowercaseK, p.baggagePrefix) {
|
||||
decodedBaggage[strings.TrimPrefix(lowercaseK, p.baggagePrefix)] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if traceID == 0 || parentID == 0 {
|
||||
return nil, ot.ErrSpanContextNotFound
|
||||
}
|
||||
|
||||
return SpanContext{
|
||||
traceID: traceID,
|
||||
spanID: parentID,
|
||||
baggage: decodedBaggage,
|
||||
}, nil
|
||||
}
|
81
vendor/github.com/DataDog/dd-trace-go/opentracing/propagators_test.go
generated
vendored
Normal file
81
vendor/github.com/DataDog/dd-trace-go/opentracing/propagators_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTracerPropagationDefaults(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
root := tracer.StartSpan("web.request")
|
||||
ctx := root.Context()
|
||||
headers := http.Header{}
|
||||
|
||||
// inject the SpanContext
|
||||
carrier := opentracing.HTTPHeadersCarrier(headers)
|
||||
err := tracer.Inject(ctx, opentracing.HTTPHeaders, carrier)
|
||||
assert.Nil(err)
|
||||
|
||||
// retrieve the SpanContext
|
||||
propagated, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
|
||||
assert.Nil(err)
|
||||
|
||||
tCtx, ok := ctx.(SpanContext)
|
||||
assert.True(ok)
|
||||
tPropagated, ok := propagated.(SpanContext)
|
||||
assert.True(ok)
|
||||
|
||||
// compare if there is a Context match
|
||||
assert.Equal(tCtx.traceID, tPropagated.traceID)
|
||||
assert.Equal(tCtx.spanID, tPropagated.spanID)
|
||||
|
||||
// ensure a child can be created
|
||||
child := tracer.StartSpan("db.query", opentracing.ChildOf(propagated))
|
||||
tRoot, ok := root.(*Span)
|
||||
assert.True(ok)
|
||||
tChild, ok := child.(*Span)
|
||||
assert.True(ok)
|
||||
|
||||
assert.NotEqual(uint64(0), tChild.Span.TraceID)
|
||||
assert.NotEqual(uint64(0), tChild.Span.SpanID)
|
||||
assert.Equal(tRoot.Span.SpanID, tChild.Span.ParentID)
|
||||
assert.Equal(tRoot.Span.TraceID, tChild.Span.ParentID)
|
||||
|
||||
tid := strconv.FormatUint(tRoot.Span.TraceID, 10)
|
||||
pid := strconv.FormatUint(tRoot.Span.SpanID, 10)
|
||||
|
||||
// hardcode header names to fail test if defaults are changed
|
||||
assert.Equal(headers.Get("x-datadog-trace-id"), tid)
|
||||
assert.Equal(headers.Get("x-datadog-parent-id"), pid)
|
||||
}
|
||||
|
||||
func TestTracerTextMapPropagationHeader(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
config.TextMapPropagator = NewTextMapPropagator("bg-", "tid", "pid")
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
root := tracer.StartSpan("web.request").SetBaggageItem("item", "x").(*Span)
|
||||
ctx := root.Context()
|
||||
headers := http.Header{}
|
||||
|
||||
carrier := opentracing.HTTPHeadersCarrier(headers)
|
||||
err := tracer.Inject(ctx, opentracing.HTTPHeaders, carrier)
|
||||
assert.Nil(err)
|
||||
|
||||
tid := strconv.FormatUint(root.Span.TraceID, 10)
|
||||
pid := strconv.FormatUint(root.Span.SpanID, 10)
|
||||
|
||||
assert.Equal(headers.Get("tid"), tid)
|
||||
assert.Equal(headers.Get("pid"), pid)
|
||||
assert.Equal(headers.Get("bg-item"), "x")
|
||||
}
|
151
vendor/github.com/DataDog/dd-trace-go/opentracing/span.go
generated
vendored
Normal file
151
vendor/github.com/DataDog/dd-trace-go/opentracing/span.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
ddtrace "github.com/DataDog/dd-trace-go/tracer"
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
// Span represents an active, un-finished span in the OpenTracing system.
|
||||
// Spans are created by the Tracer interface.
|
||||
type Span struct {
|
||||
*ddtrace.Span
|
||||
context SpanContext
|
||||
tracer *Tracer
|
||||
}
|
||||
|
||||
// Tracer provides access to the `Tracer`` that created this Span.
|
||||
func (s *Span) Tracer() ot.Tracer {
|
||||
return s.tracer
|
||||
}
|
||||
|
||||
// Context yields the SpanContext for this Span. Note that the return
|
||||
// value of Context() is still valid after a call to Span.Finish(), as is
|
||||
// a call to Span.Context() after a call to Span.Finish().
|
||||
func (s *Span) Context() ot.SpanContext {
|
||||
return s.context
|
||||
}
|
||||
|
||||
// SetBaggageItem sets a key:value pair on this Span and its SpanContext
|
||||
// that also propagates to descendants of this Span.
|
||||
func (s *Span) SetBaggageItem(key, val string) ot.Span {
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
|
||||
s.context = s.context.WithBaggageItem(key, val)
|
||||
return s
|
||||
}
|
||||
|
||||
// BaggageItem gets the value for a baggage item given its key. Returns the empty string
|
||||
// if the value isn't found in this Span.
|
||||
func (s *Span) BaggageItem(key string) string {
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
|
||||
return s.context.baggage[key]
|
||||
}
|
||||
|
||||
// SetTag adds a tag to the span, overwriting pre-existing values for
|
||||
// the given `key`.
|
||||
func (s *Span) SetTag(key string, value interface{}) ot.Span {
|
||||
switch key {
|
||||
case ServiceName:
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
s.Span.Service = fmt.Sprint(value)
|
||||
case ResourceName:
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
s.Span.Resource = fmt.Sprint(value)
|
||||
case SpanType:
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
s.Span.Type = fmt.Sprint(value)
|
||||
case Error:
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
// no error
|
||||
case error:
|
||||
s.Span.SetError(v)
|
||||
default:
|
||||
s.Span.SetError(fmt.Errorf("%v", v))
|
||||
}
|
||||
default:
|
||||
// NOTE: locking is not required because the `SetMeta` is
|
||||
// already thread-safe
|
||||
s.Span.SetMeta(key, fmt.Sprint(value))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FinishWithOptions is like Finish() but with explicit control over
|
||||
// timestamps and log data.
|
||||
func (s *Span) FinishWithOptions(options ot.FinishOptions) {
|
||||
if options.FinishTime.IsZero() {
|
||||
options.FinishTime = time.Now().UTC()
|
||||
}
|
||||
|
||||
s.Span.FinishWithTime(options.FinishTime.UnixNano())
|
||||
}
|
||||
|
||||
// SetOperationName sets or changes the operation name.
|
||||
func (s *Span) SetOperationName(operationName string) ot.Span {
|
||||
s.Span.Lock()
|
||||
defer s.Span.Unlock()
|
||||
|
||||
s.Span.Name = operationName
|
||||
return s
|
||||
}
|
||||
|
||||
// LogFields is an efficient and type-checked way to record key:value
|
||||
// logging data about a Span, though the programming interface is a little
|
||||
// more verbose than LogKV().
|
||||
func (s *Span) LogFields(fields ...log.Field) {
|
||||
// TODO: implementation missing
|
||||
}
|
||||
|
||||
// LogKV is a concise, readable way to record key:value logging data about
|
||||
// a Span, though unfortunately this also makes it less efficient and less
|
||||
// type-safe than LogFields().
|
||||
func (s *Span) LogKV(keyVals ...interface{}) {
|
||||
// TODO: implementation missing
|
||||
}
|
||||
|
||||
// LogEvent is deprecated: use LogFields or LogKV
|
||||
func (s *Span) LogEvent(event string) {
|
||||
// TODO: implementation missing
|
||||
}
|
||||
|
||||
// LogEventWithPayload deprecated: use LogFields or LogKV
|
||||
func (s *Span) LogEventWithPayload(event string, payload interface{}) {
|
||||
// TODO: implementation missing
|
||||
}
|
||||
|
||||
// Log is deprecated: use LogFields or LogKV
|
||||
func (s *Span) Log(data ot.LogData) {
|
||||
// TODO: implementation missing
|
||||
}
|
||||
|
||||
// NewSpan is the OpenTracing Span constructor
|
||||
func NewSpan(operationName string) *Span {
|
||||
span := &ddtrace.Span{
|
||||
Name: operationName,
|
||||
}
|
||||
|
||||
otSpan := &Span{
|
||||
Span: span,
|
||||
context: SpanContext{
|
||||
traceID: span.TraceID,
|
||||
spanID: span.SpanID,
|
||||
parentID: span.ParentID,
|
||||
sampled: span.Sampled,
|
||||
},
|
||||
}
|
||||
|
||||
// SpanContext is propagated and used to create children
|
||||
otSpan.context.span = otSpan
|
||||
return otSpan
|
||||
}
|
129
vendor/github.com/DataDog/dd-trace-go/opentracing/span_test.go
generated
vendored
Normal file
129
vendor/github.com/DataDog/dd-trace-go/opentracing/span_test.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSpanBaggage(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
span.SetBaggageItem("key", "value")
|
||||
assert.Equal("value", span.BaggageItem("key"))
|
||||
}
|
||||
|
||||
func TestSpanContext(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
assert.NotNil(span.Context())
|
||||
}
|
||||
|
||||
func TestSpanOperationName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
span.SetOperationName("http.request")
|
||||
assert.Equal("http.request", span.Span.Name)
|
||||
}
|
||||
|
||||
func TestSpanFinish(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
span.Finish()
|
||||
|
||||
assert.True(span.Span.Duration > 0)
|
||||
}
|
||||
|
||||
func TestSpanFinishWithTime(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
finishTime := time.Now().Add(10 * time.Second)
|
||||
span := NewSpan("web.request")
|
||||
span.FinishWithOptions(opentracing.FinishOptions{FinishTime: finishTime})
|
||||
|
||||
duration := finishTime.UnixNano() - span.Span.Start
|
||||
assert.Equal(duration, span.Span.Duration)
|
||||
}
|
||||
|
||||
func TestSpanSetTag(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
span.SetTag("component", "tracer")
|
||||
assert.Equal("tracer", span.Meta["component"])
|
||||
|
||||
span.SetTag("tagInt", 1234)
|
||||
assert.Equal("1234", span.Meta["tagInt"])
|
||||
}
|
||||
|
||||
func TestSpanSetDatadogTags(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
span := NewSpan("web.request")
|
||||
span.SetTag("span.type", "http")
|
||||
span.SetTag("service.name", "db-cluster")
|
||||
span.SetTag("resource.name", "SELECT * FROM users;")
|
||||
|
||||
assert.Equal("http", span.Span.Type)
|
||||
assert.Equal("db-cluster", span.Span.Service)
|
||||
assert.Equal("SELECT * FROM users;", span.Span.Resource)
|
||||
}
|
||||
|
||||
func TestSpanSetErrorTag(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string // span name
|
||||
val interface{} // tag value
|
||||
msg string // error message
|
||||
typ string // error type
|
||||
}{
|
||||
{
|
||||
name: "error.error",
|
||||
val: errors.New("some error"),
|
||||
msg: "some error",
|
||||
typ: "*errors.errorString",
|
||||
},
|
||||
{
|
||||
name: "error.string",
|
||||
val: "some string error",
|
||||
msg: "some string error",
|
||||
typ: "*errors.errorString",
|
||||
},
|
||||
{
|
||||
name: "error.struct",
|
||||
val: struct{ N int }{5},
|
||||
msg: "{5}",
|
||||
typ: "*errors.errorString",
|
||||
},
|
||||
{
|
||||
name: "error.other",
|
||||
val: 1,
|
||||
msg: "1",
|
||||
typ: "*errors.errorString",
|
||||
},
|
||||
{
|
||||
name: "error.nil",
|
||||
val: nil,
|
||||
msg: "",
|
||||
typ: "",
|
||||
},
|
||||
} {
|
||||
span := NewSpan(tt.name)
|
||||
span.SetTag(Error, tt.val)
|
||||
|
||||
assert.Equal(span.Meta["error.msg"], tt.msg)
|
||||
assert.Equal(span.Meta["error.type"], tt.typ)
|
||||
|
||||
if tt.val != nil {
|
||||
assert.NotEqual(span.Meta["error.stack"], "")
|
||||
}
|
||||
}
|
||||
}
|
12
vendor/github.com/DataDog/dd-trace-go/opentracing/tags.go
generated
vendored
Normal file
12
vendor/github.com/DataDog/dd-trace-go/opentracing/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package opentracing
|
||||
|
||||
const (
|
||||
// SpanType defines the Span type (web, db, cache)
|
||||
SpanType = "span.type"
|
||||
// ServiceName defines the Service name for this Span
|
||||
ServiceName = "service.name"
|
||||
// ResourceName defines the Resource name for the Span
|
||||
ResourceName = "resource.name"
|
||||
// Error defines an error.
|
||||
Error = "error.error"
|
||||
)
|
178
vendor/github.com/DataDog/dd-trace-go/opentracing/tracer.go
generated
vendored
Normal file
178
vendor/github.com/DataDog/dd-trace-go/opentracing/tracer.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
ddtrace "github.com/DataDog/dd-trace-go/tracer"
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// Tracer is a simple, thin interface for Span creation and SpanContext
|
||||
// propagation. In the current state, this Tracer is a compatibility layer
|
||||
// that wraps the Datadog Tracer implementation.
|
||||
type Tracer struct {
|
||||
// impl is the Datadog Tracer implementation.
|
||||
impl *ddtrace.Tracer
|
||||
|
||||
// config holds the Configuration used to create the Tracer.
|
||||
config *Configuration
|
||||
}
|
||||
|
||||
// StartSpan creates, starts, and returns a new Span with the given `operationName`
|
||||
// A Span with no SpanReference options (e.g., opentracing.ChildOf() or
|
||||
// opentracing.FollowsFrom()) becomes the root of its own trace.
|
||||
func (t *Tracer) StartSpan(operationName string, options ...ot.StartSpanOption) ot.Span {
|
||||
sso := ot.StartSpanOptions{}
|
||||
for _, o := range options {
|
||||
o.Apply(&sso)
|
||||
}
|
||||
|
||||
return t.startSpanWithOptions(operationName, sso)
|
||||
}
|
||||
|
||||
func (t *Tracer) startSpanWithOptions(operationName string, options ot.StartSpanOptions) ot.Span {
|
||||
if options.StartTime.IsZero() {
|
||||
options.StartTime = time.Now().UTC()
|
||||
}
|
||||
|
||||
var context SpanContext
|
||||
var hasParent bool
|
||||
var parent *Span
|
||||
var span *ddtrace.Span
|
||||
|
||||
for _, ref := range options.References {
|
||||
ctx, ok := ref.ReferencedContext.(SpanContext)
|
||||
if !ok {
|
||||
// ignore the SpanContext since it's not valid
|
||||
continue
|
||||
}
|
||||
|
||||
// if we have parenting define it
|
||||
if ref.Type == ot.ChildOfRef {
|
||||
hasParent = true
|
||||
context = ctx
|
||||
parent = ctx.span
|
||||
}
|
||||
}
|
||||
|
||||
if parent == nil {
|
||||
// create a root Span with the default service name and resource
|
||||
span = t.impl.NewRootSpan(operationName, t.config.ServiceName, operationName)
|
||||
|
||||
if hasParent {
|
||||
// the Context doesn't have a Span reference because it
|
||||
// has been propagated from another process, so we set these
|
||||
// values manually
|
||||
span.TraceID = context.traceID
|
||||
span.ParentID = context.spanID
|
||||
t.impl.Sample(span)
|
||||
}
|
||||
} else {
|
||||
// create a child Span that inherits from a parent
|
||||
span = t.impl.NewChildSpan(operationName, parent.Span)
|
||||
}
|
||||
|
||||
// create an OpenTracing compatible Span; the SpanContext has a
|
||||
// back-reference that is used for parent-child hierarchy
|
||||
otSpan := &Span{
|
||||
Span: span,
|
||||
context: SpanContext{
|
||||
traceID: span.TraceID,
|
||||
spanID: span.SpanID,
|
||||
parentID: span.ParentID,
|
||||
sampled: span.Sampled,
|
||||
},
|
||||
tracer: t,
|
||||
}
|
||||
otSpan.context.span = otSpan
|
||||
|
||||
// set start time
|
||||
otSpan.Span.Start = options.StartTime.UnixNano()
|
||||
|
||||
if parent != nil {
|
||||
// propagate baggage items
|
||||
if l := len(parent.context.baggage); l > 0 {
|
||||
otSpan.context.baggage = make(map[string]string, len(parent.context.baggage))
|
||||
for k, v := range parent.context.baggage {
|
||||
otSpan.context.baggage[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add tags from options
|
||||
for k, v := range options.Tags {
|
||||
otSpan.SetTag(k, v)
|
||||
}
|
||||
|
||||
// add global tags
|
||||
for k, v := range t.config.GlobalTags {
|
||||
otSpan.SetTag(k, v)
|
||||
}
|
||||
|
||||
return otSpan
|
||||
}
|
||||
|
||||
// Inject takes the `sm` SpanContext instance and injects it for
|
||||
// propagation within `carrier`. The actual type of `carrier` depends on
|
||||
// the value of `format`. Currently supported Injectors are:
|
||||
// * `TextMap`
|
||||
// * `HTTPHeaders`
|
||||
func (t *Tracer) Inject(ctx ot.SpanContext, format interface{}, carrier interface{}) error {
|
||||
switch format {
|
||||
case ot.TextMap, ot.HTTPHeaders:
|
||||
return t.config.TextMapPropagator.Inject(ctx, carrier)
|
||||
}
|
||||
return ot.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
// Extract returns a SpanContext instance given `format` and `carrier`.
|
||||
func (t *Tracer) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) {
|
||||
switch format {
|
||||
case ot.TextMap, ot.HTTPHeaders:
|
||||
return t.config.TextMapPropagator.Extract(carrier)
|
||||
}
|
||||
return nil, ot.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
// Close method implements `io.Closer` interface to graceful shutdown the Datadog
|
||||
// Tracer. Note that this is a blocking operation that waits for the flushing Go
|
||||
// routine.
|
||||
func (t *Tracer) Close() error {
|
||||
t.impl.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTracer uses a Configuration object to initialize a Datadog Tracer.
|
||||
// The initialization returns a `io.Closer` that can be used to graceful
|
||||
// shutdown the tracer. If the configuration object defines a disabled
|
||||
// Tracer, a no-op implementation is returned.
|
||||
func NewTracer(config *Configuration) (ot.Tracer, io.Closer, error) {
|
||||
if config.ServiceName == "" {
|
||||
// abort initialization if a `ServiceName` is not defined
|
||||
return nil, nil, errors.New("A Datadog Tracer requires a valid `ServiceName` set")
|
||||
}
|
||||
|
||||
if config.Enabled == false {
|
||||
// return a no-op implementation so Datadog provides the minimum overhead
|
||||
return &ot.NoopTracer{}, &noopCloser{}, nil
|
||||
}
|
||||
|
||||
// configure a Datadog Tracer
|
||||
transport := ddtrace.NewTransport(config.AgentHostname, config.AgentPort)
|
||||
tracer := &Tracer{
|
||||
impl: ddtrace.NewTracerTransport(transport),
|
||||
config: config,
|
||||
}
|
||||
tracer.impl.SetDebugLogging(config.Debug)
|
||||
tracer.impl.SetSampleRate(config.SampleRate)
|
||||
|
||||
// set the new Datadog Tracer as a `DefaultTracer` so it can be
|
||||
// used in integrations. NOTE: this is a temporary implementation
|
||||
// that can be removed once all integrations have been migrated
|
||||
// to the OpenTracing API.
|
||||
ddtrace.DefaultTracer = tracer.impl
|
||||
|
||||
return tracer, tracer, nil
|
||||
}
|
131
vendor/github.com/DataDog/dd-trace-go/opentracing/tracer_test.go
generated
vendored
Normal file
131
vendor/github.com/DataDog/dd-trace-go/opentracing/tracer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ddtrace "github.com/DataDog/dd-trace-go/tracer"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultTracer(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
tTracer, ok := tracer.(*Tracer)
|
||||
assert.True(ok)
|
||||
|
||||
assert.Equal(tTracer.impl, ddtrace.DefaultTracer)
|
||||
}
|
||||
|
||||
func TestTracerStartSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
span, ok := tracer.StartSpan("web.request").(*Span)
|
||||
assert.True(ok)
|
||||
|
||||
assert.NotEqual(uint64(0), span.Span.TraceID)
|
||||
assert.NotEqual(uint64(0), span.Span.SpanID)
|
||||
assert.Equal(uint64(0), span.Span.ParentID)
|
||||
assert.Equal("web.request", span.Span.Name)
|
||||
assert.Equal("opentracing.test", span.Span.Service)
|
||||
assert.NotNil(span.Span.Tracer())
|
||||
}
|
||||
|
||||
func TestTracerStartChildSpan(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
root := tracer.StartSpan("web.request")
|
||||
child := tracer.StartSpan("db.query", opentracing.ChildOf(root.Context()))
|
||||
tRoot, ok := root.(*Span)
|
||||
assert.True(ok)
|
||||
tChild, ok := child.(*Span)
|
||||
assert.True(ok)
|
||||
|
||||
assert.NotEqual(uint64(0), tChild.Span.TraceID)
|
||||
assert.NotEqual(uint64(0), tChild.Span.SpanID)
|
||||
assert.Equal(tRoot.Span.SpanID, tChild.Span.ParentID)
|
||||
assert.Equal(tRoot.Span.TraceID, tChild.Span.ParentID)
|
||||
}
|
||||
|
||||
func TestTracerBaggagePropagation(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
root := tracer.StartSpan("web.request")
|
||||
root.SetBaggageItem("key", "value")
|
||||
child := tracer.StartSpan("db.query", opentracing.ChildOf(root.Context()))
|
||||
context, ok := child.Context().(SpanContext)
|
||||
assert.True(ok)
|
||||
|
||||
assert.Equal("value", context.baggage["key"])
|
||||
}
|
||||
|
||||
func TestTracerBaggageImmutability(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
root := tracer.StartSpan("web.request")
|
||||
root.SetBaggageItem("key", "value")
|
||||
child := tracer.StartSpan("db.query", opentracing.ChildOf(root.Context()))
|
||||
child.SetBaggageItem("key", "changed!")
|
||||
parentContext, ok := root.Context().(SpanContext)
|
||||
assert.True(ok)
|
||||
childContext, ok := child.Context().(SpanContext)
|
||||
assert.True(ok)
|
||||
|
||||
assert.Equal("value", parentContext.baggage["key"])
|
||||
assert.Equal("changed!", childContext.baggage["key"])
|
||||
}
|
||||
|
||||
func TestTracerSpanTags(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
tag := opentracing.Tag{Key: "key", Value: "value"}
|
||||
span, ok := tracer.StartSpan("web.request", tag).(*Span)
|
||||
assert.True(ok)
|
||||
|
||||
assert.Equal("value", span.Span.Meta["key"])
|
||||
}
|
||||
|
||||
func TestTracerSpanGlobalTags(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
config.GlobalTags["key"] = "value"
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
span := tracer.StartSpan("web.request").(*Span)
|
||||
assert.Equal("value", span.Span.Meta["key"])
|
||||
|
||||
child := tracer.StartSpan("db.query", opentracing.ChildOf(span.Context())).(*Span)
|
||||
assert.Equal("value", child.Span.Meta["key"])
|
||||
}
|
||||
|
||||
func TestTracerSpanStartTime(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
config := NewConfiguration()
|
||||
tracer, _, _ := NewTracer(config)
|
||||
|
||||
startTime := time.Now().Add(-10 * time.Second)
|
||||
span, ok := tracer.StartSpan("web.request", opentracing.StartTime(startTime)).(*Span)
|
||||
assert.True(ok)
|
||||
|
||||
assert.Equal(startTime.UnixNano(), span.Span.Start)
|
||||
}
|
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
|
127
vendor/github.com/DataDog/dd-trace-go/tracer/buffer.go
generated
vendored
Normal file
127
vendor/github.com/DataDog/dd-trace-go/tracer/buffer.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package tracer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// spanBufferDefaultInitSize is the initial size of our trace buffer,
|
||||
// by default we allocate for a handful of spans within the trace,
|
||||
// reasonable as span is actually way bigger, and avoids re-allocating
|
||||
// over and over. Could be fine-tuned at runtime.
|
||||
spanBufferDefaultInitSize = 10
|
||||
// spanBufferDefaultMaxSize is the maximum number of spans we keep in memory.
|
||||
// This is to avoid memory leaks, if above that value, spans are randomly
|
||||
// dropped and ignore, resulting in corrupted tracing data, but ensuring
|
||||
// original program continues to work as expected.
|
||||
spanBufferDefaultMaxSize = 1e5
|
||||
)
|
||||
|
||||
type spanBuffer struct {
|
||||
channels tracerChans
|
||||
|
||||
spans []*Span
|
||||
finishedSpans int
|
||||
|
||||
initSize int
|
||||
maxSize int
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func newSpanBuffer(channels tracerChans, initSize, maxSize int) *spanBuffer {
|
||||
if initSize <= 0 {
|
||||
initSize = spanBufferDefaultInitSize
|
||||
}
|
||||
if maxSize <= 0 {
|
||||
maxSize = spanBufferDefaultMaxSize
|
||||
}
|
||||
return &spanBuffer{
|
||||
channels: channels,
|
||||
initSize: initSize,
|
||||
maxSize: maxSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) Push(span *Span) {
|
||||
if tb == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tb.Lock()
|
||||
defer tb.Unlock()
|
||||
|
||||
if len(tb.spans) > 0 {
|
||||
// if spanBuffer is full, forget span
|
||||
if len(tb.spans) >= tb.maxSize {
|
||||
tb.channels.pushErr(&errorSpanBufFull{Len: len(tb.spans)})
|
||||
return
|
||||
}
|
||||
// if there's a trace ID mismatch, ignore span
|
||||
if tb.spans[0].TraceID != span.TraceID {
|
||||
tb.channels.pushErr(&errorTraceIDMismatch{Expected: tb.spans[0].TraceID, Actual: span.TraceID})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if tb.spans == nil {
|
||||
tb.spans = make([]*Span, 0, tb.initSize)
|
||||
}
|
||||
|
||||
tb.spans = append(tb.spans, span)
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) flushable() bool {
|
||||
tb.RLock()
|
||||
defer tb.RUnlock()
|
||||
|
||||
if len(tb.spans) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return tb.finishedSpans == len(tb.spans)
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) ack() {
|
||||
tb.Lock()
|
||||
defer tb.Unlock()
|
||||
|
||||
tb.finishedSpans++
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) doFlush() {
|
||||
if !tb.flushable() {
|
||||
return
|
||||
}
|
||||
|
||||
tb.Lock()
|
||||
defer tb.Unlock()
|
||||
|
||||
tb.channels.pushTrace(tb.spans)
|
||||
tb.spans = nil
|
||||
tb.finishedSpans = 0 // important, because a buffer can be used for several flushes
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) Flush() {
|
||||
if tb == nil {
|
||||
return
|
||||
}
|
||||
tb.doFlush()
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) AckFinish() {
|
||||
if tb == nil {
|
||||
return
|
||||
}
|
||||
tb.ack()
|
||||
tb.doFlush()
|
||||
}
|
||||
|
||||
func (tb *spanBuffer) Len() int {
|
||||
if tb == nil {
|
||||
return 0
|
||||
}
|
||||
tb.RLock()
|
||||
defer tb.RUnlock()
|
||||
return len(tb.spans)
|
||||
}
|
105
vendor/github.com/DataDog/dd-trace-go/tracer/buffer_test.go
generated
vendored
Normal file
105
vendor/github.com/DataDog/dd-trace-go/tracer/buffer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
package tracer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testInitSize = 2
|
||||
testMaxSize = 5
|
||||
)
|
||||
|
||||
func TestSpanBufferPushOne(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
buffer := newSpanBuffer(newTracerChans(), testInitSize, testMaxSize)
|
||||
assert.NotNil(buffer)
|
||||
assert.Len(buffer.spans, 0)
|
||||
|
||||
traceID := NextSpanID()
|
||||
root := NewSpan("name1", "a-service", "a-resource", traceID, traceID, 0, nil)
|
||||
root.buffer = buffer
|
||||
|
||||
buffer.Push(root)
|
||||
assert.Len(buffer.spans, 1, "there is one span in the buffer")
|
||||
assert.Equal(root, buffer.spans[0], "the span is the one pushed before")
|
||||
|
||||
root.Finish()
|
||||
|
||||
select {
|
||||
case trace := <-buffer.channels.trace:
|
||||
assert.Len(trace, 1, "there was a trace in the channel")
|
||||
assert.Equal(root, trace[0], "the trace in the channel is the one pushed before")
|
||||
assert.Equal(0, buffer.Len(), "no more spans in the buffer")
|
||||
case err := <-buffer.channels.err:
|
||||
assert.Fail("unexpected error:", err.Error())
|
||||
t.Logf("buffer: %v", buffer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanBufferPushNoFinish(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
buffer := newSpanBuffer(newTracerChans(), testInitSize, testMaxSize)
|
||||
assert.NotNil(buffer)
|
||||
assert.Len(buffer.spans, 0)
|
||||
|
||||
traceID := NextSpanID()
|
||||
root := NewSpan("name1", "a-service", "a-resource", traceID, traceID, 0, nil)
|
||||
root.buffer = buffer
|
||||
|
||||
buffer.Push(root)
|
||||
assert.Len(buffer.spans, 1, "there is one span in the buffer")
|
||||
assert.Equal(root, buffer.spans[0], "the span is the one pushed before")
|
||||
|
||||
select {
|
||||
case <-buffer.channels.trace:
|
||||
assert.Fail("span was not finished, should not be flushed")
|
||||
t.Logf("buffer: %v", buffer)
|
||||
case err := <-buffer.channels.err:
|
||||
assert.Fail("unexpected error:", err.Error())
|
||||
t.Logf("buffer: %v", buffer)
|
||||
case <-time.After(time.Second / 10):
|
||||
t.Logf("expected timeout, nothing should show up in buffer as the trace is not finished")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanBufferPushSeveral(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
buffer := newSpanBuffer(newTracerChans(), testInitSize, testMaxSize)
|
||||
assert.NotNil(buffer)
|
||||
assert.Len(buffer.spans, 0)
|
||||
|
||||
traceID := NextSpanID()
|
||||
root := NewSpan("name1", "a-service", "a-resource", traceID, traceID, 0, nil)
|
||||
span2 := NewSpan("name2", "a-service", "a-resource", NextSpanID(), traceID, root.SpanID, nil)
|
||||
span3 := NewSpan("name3", "a-service", "a-resource", NextSpanID(), traceID, root.SpanID, nil)
|
||||
span3a := NewSpan("name3", "a-service", "a-resource", NextSpanID(), traceID, span3.SpanID, nil)
|
||||
|
||||
spans := []*Span{root, span2, span3, span3a}
|
||||
|
||||
for i, span := range spans {
|
||||
span.buffer = buffer
|
||||
buffer.Push(span)
|
||||
assert.Len(buffer.spans, i+1, "there is one more span in the buffer")
|
||||
assert.Equal(span, buffer.spans[i], "the span is the one pushed before")
|
||||
}
|
||||
|
||||
for _, span := range spans {
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
select {
|
||||
case trace := <-buffer.channels.trace:
|
||||
assert.Len(trace, 4, "there was one trace with the right number of spans in the channel")
|
||||
for _, span := range spans {
|
||||
assert.Contains(trace, span, "the trace contains the spans")
|
||||
}
|
||||
case err := <-buffer.channels.err:
|
||||
assert.Fail("unexpected error:", err.Error())
|
||||
}
|
||||
}
|
88
vendor/github.com/DataDog/dd-trace-go/tracer/channels.go
generated
vendored
Normal file
88
vendor/github.com/DataDog/dd-trace-go/tracer/channels.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package tracer
|
||||
|
||||
const (
|
||||
// traceChanLen is the capacity of the trace channel. This channels is emptied
|
||||
// on a regular basis (worker thread) or when it reaches 50% of its capacity.
|
||||
// If it's full, then data is simply dropped and ignored, with a log message.
|
||||
// This only happens under heavy load,
|
||||
traceChanLen = 1000
|
||||
// serviceChanLen is the length of the service channel. As for the trace channel,
|
||||
// it's emptied by worker thread or when it reaches 50%. Note that there should
|
||||
// be much less data here, as service data does not be to be updated that often.
|
||||
serviceChanLen = 50
|
||||
// errChanLen is the number of errors we keep in the error channel. When this
|
||||
// one is full, errors are just ignored, dropped, nothing left. At some point,
|
||||
// there's already a whole lot of errors in the backlog, there's no real point
|
||||
// in keeping millions of errors, a representative sample is enough. And we
|
||||
// don't want to block user code and/or bloat memory or log files with redundant data.
|
||||
errChanLen = 200
|
||||
)
|
||||
|
||||
// traceChans holds most tracer channels together, it's mostly used to
|
||||
// pass them together to the span buffer/context. It's obviously safe
|
||||
// to access it concurrently as it contains channels only. And it's convenient
|
||||
// to have it isolated from tracer, for the sake of unit testing.
|
||||
type tracerChans struct {
|
||||
trace chan []*Span
|
||||
service chan Service
|
||||
err chan error
|
||||
traceFlush chan struct{}
|
||||
serviceFlush chan struct{}
|
||||
errFlush chan struct{}
|
||||
}
|
||||
|
||||
func newTracerChans() tracerChans {
|
||||
return tracerChans{
|
||||
trace: make(chan []*Span, traceChanLen),
|
||||
service: make(chan Service, serviceChanLen),
|
||||
err: make(chan error, errChanLen),
|
||||
traceFlush: make(chan struct{}, 1),
|
||||
serviceFlush: make(chan struct{}, 1),
|
||||
errFlush: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *tracerChans) pushTrace(trace []*Span) {
|
||||
if len(tc.trace) >= cap(tc.trace)/2 { // starts being full, anticipate, try and flush soon
|
||||
select {
|
||||
case tc.traceFlush <- struct{}{}:
|
||||
default: // a flush was already requested, skip
|
||||
}
|
||||
}
|
||||
select {
|
||||
case tc.trace <- trace:
|
||||
default: // never block user code
|
||||
tc.pushErr(&errorTraceChanFull{Len: len(tc.trace)})
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *tracerChans) pushService(service Service) {
|
||||
if len(tc.service) >= cap(tc.service)/2 { // starts being full, anticipate, try and flush soon
|
||||
select {
|
||||
case tc.serviceFlush <- struct{}{}:
|
||||
default: // a flush was already requested, skip
|
||||
}
|
||||
}
|
||||
select {
|
||||
case tc.service <- service:
|
||||
default: // never block user code
|
||||
tc.pushErr(&errorServiceChanFull{Len: len(tc.service)})
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *tracerChans) pushErr(err error) {
|
||||
if len(tc.err) >= cap(tc.err)/2 { // starts being full, anticipate, try and flush soon
|
||||
select {
|
||||
case tc.errFlush <- struct{}{}:
|
||||
default: // a flush was already requested, skip
|
||||
}
|
||||
}
|
||||
select {
|
||||
case tc.err <- err:
|
||||
default:
|
||||
// OK, if we get this, our error error buffer is full,
|
||||
// we can assume it is filled with meaningful messages which
|
||||
// are going to be logged and hopefully read, nothing better
|
||||
// we can do, blocking would make things worse.
|
||||
}
|
||||
}
|
117
vendor/github.com/DataDog/dd-trace-go/tracer/channels_test.go
generated
vendored
Normal file
117
vendor/github.com/DataDog/dd-trace-go/tracer/channels_test.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package tracer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPushTrace(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
channels := newTracerChans()
|
||||
|
||||
trace := []*Span{
|
||||
&Span{
|
||||
Name: "pylons.request",
|
||||
Service: "pylons",
|
||||
Resource: "/",
|
||||
},
|
||||
&Span{
|
||||
Name: "pylons.request",
|
||||
Service: "pylons",
|
||||
Resource: "/foo",
|
||||
},
|
||||
}
|
||||
channels.pushTrace(trace)
|
||||
|
||||
assert.Len(channels.trace, 1, "there should be data in channel")
|
||||
assert.Len(channels.traceFlush, 0, "no flush requested yet")
|
||||
|
||||
pushed := <-channels.trace
|
||||
assert.Equal(trace, pushed)
|
||||
|
||||
many := traceChanLen/2 + 1
|
||||
for i := 0; i < many; i++ {
|
||||
channels.pushTrace(make([]*Span, i))
|
||||
}
|
||||
assert.Len(channels.trace, many, "all traces should be in the channel, not yet blocking")
|
||||
assert.Len(channels.traceFlush, 1, "a trace flush should have been requested")
|
||||
|
||||
for i := 0; i < cap(channels.trace); i++ {
|
||||
channels.pushTrace(make([]*Span, i))
|
||||
}
|
||||
assert.Len(channels.trace, traceChanLen, "buffer should be full")
|
||||
assert.NotEqual(0, len(channels.err), "there should be an error logged")
|
||||
err := <-channels.err
|
||||
assert.Equal(&errorTraceChanFull{Len: traceChanLen}, err)
|
||||
}
|
||||
|
||||
func TestPushService(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
channels := newTracerChans()
|
||||
|
||||
service := Service{
|
||||
Name: "redis-master",
|
||||
App: "redis",
|
||||
AppType: "db",
|
||||
}
|
||||
channels.pushService(service)
|
||||
|
||||
assert.Len(channels.service, 1, "there should be data in channel")
|
||||
assert.Len(channels.serviceFlush, 0, "no flush requested yet")
|
||||
|
||||
pushed := <-channels.service
|
||||
assert.Equal(service, pushed)
|
||||
|
||||
many := serviceChanLen/2 + 1
|
||||
for i := 0; i < many; i++ {
|
||||
channels.pushService(Service{
|
||||
Name: fmt.Sprintf("service%d", i),
|
||||
App: "custom",
|
||||
AppType: "web",
|
||||
})
|
||||
}
|
||||
assert.Len(channels.service, many, "all services should be in the channel, not yet blocking")
|
||||
assert.Len(channels.serviceFlush, 1, "a service flush should have been requested")
|
||||
|
||||
for i := 0; i < cap(channels.service); i++ {
|
||||
channels.pushService(Service{
|
||||
Name: fmt.Sprintf("service%d", i),
|
||||
App: "custom",
|
||||
AppType: "web",
|
||||
})
|
||||
}
|
||||
assert.Len(channels.service, serviceChanLen, "buffer should be full")
|
||||
assert.NotEqual(0, len(channels.err), "there should be an error logged")
|
||||
err := <-channels.err
|
||||
assert.Equal(&errorServiceChanFull{Len: serviceChanLen}, err)
|
||||
}
|
||||
|
||||
func TestPushErr(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
channels := newTracerChans()
|
||||
|
||||
err := fmt.Errorf("ooops")
|
||||
channels.pushErr(err)
|
||||
|
||||
assert.Len(channels.err, 1, "there should be data in channel")
|
||||
assert.Len(channels.errFlush, 0, "no flush requested yet")
|
||||
|
||||
pushed := <-channels.err
|
||||
assert.Equal(err, pushed)
|
||||
|
||||
many := errChanLen/2 + 1
|
||||
for i := 0; i < many; i++ {
|
||||
channels.pushErr(fmt.Errorf("err %d", i))
|
||||
}
|
||||
assert.Len(channels.err, many, "all errs should be in the channel, not yet blocking")
|
||||
assert.Len(channels.errFlush, 1, "a err flush should have been requested")
|
||||
for i := 0; i < cap(channels.err); i++ {
|
||||
channels.pushErr(fmt.Errorf("err %d", i))
|
||||
}
|
||||
// if we reach this, means pushErr is not blocking, which is what we want to double-check
|
||||
}
|
42
vendor/github.com/DataDog/dd-trace-go/tracer/context.go
generated
vendored
Normal file
42
vendor/github.com/DataDog/dd-trace-go/tracer/context.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package tracer
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
var spanKey = "datadog_trace_span"
|
||||
|
||||
// ContextWithSpan will return a new context that includes the given span.
|
||||
// DEPRECATED: use span.Context(ctx) instead.
|
||||
func ContextWithSpan(ctx context.Context, span *Span) context.Context {
|
||||
if span == nil {
|
||||
return ctx
|
||||
}
|
||||
return span.Context(ctx)
|
||||
}
|
||||
|
||||
// SpanFromContext returns the stored *Span from the Context if it's available.
|
||||
// This helper returns also the ok value that is true if the span is present.
|
||||
func SpanFromContext(ctx context.Context) (*Span, bool) {
|
||||
if ctx == nil {
|
||||
return nil, false
|
||||
}
|
||||
span, ok := ctx.Value(spanKey).(*Span)
|
||||
return span, ok
|
||||
}
|
||||
|
||||
// SpanFromContextDefault returns the stored *Span from the Context. If not, it
|
||||
// will return an empty span that will do nothing.
|
||||
func SpanFromContextDefault(ctx context.Context) *Span {
|
||||
|
||||
// FIXME[matt] is it better to return a singleton empty span?
|
||||
if ctx == nil {
|
||||
return &Span{}
|
||||
}
|
||||
|
||||
span, ok := SpanFromContext(ctx)
|
||||
if !ok {
|
||||
return &Span{}
|
||||
}
|
||||
return span
|
||||
}
|
69
vendor/github.com/DataDog/dd-trace-go/tracer/context_test.go
generated
vendored
Normal file
69
vendor/github.com/DataDog/dd-trace-go/tracer/context_test.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package tracer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContextWithSpanDefault(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// create a new context with a span
|
||||
span := SpanFromContextDefault(nil)
|
||||
assert.NotNil(span)
|
||||
|
||||
ctx := context.Background()
|
||||
assert.NotNil(SpanFromContextDefault(ctx))
|
||||
}
|
||||
|
||||
func TestSpanFromContext(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// create a new context with a span
|
||||
ctx := context.Background()
|
||||
tracer := NewTracer()
|
||||
expectedSpan := tracer.NewRootSpan("pylons.request", "pylons", "/")
|
||||
ctx = ContextWithSpan(ctx, expectedSpan)
|
||||
|
||||
span, ok := SpanFromContext(ctx)
|
||||
assert.True(ok)
|
||||
assert.Equal(expectedSpan, span)
|
||||
}
|
||||
|
||||
func TestSpanFromContextNil(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// create a context without a span
|
||||
ctx := context.Background()
|
||||
|
||||
span, ok := SpanFromContext(ctx)
|
||||
assert.False(ok)
|
||||
assert.Nil(span)
|
||||
|
||||
span, ok = SpanFromContext(nil)
|
||||
assert.False(ok)
|
||||
assert.Nil(span)
|
||||
|
||||
}
|
||||
|
||||
func TestSpanMissingParent(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
tracer := NewTracer()
|
||||
|
||||
// assuming we're in an inner function and we
|
||||
// forget the nil or ok checks
|
||||
ctx := context.Background()
|
||||
span, _ := SpanFromContext(ctx)
|
||||
|
||||
// span is nil according to the API
|
||||
child := tracer.NewChildSpan("redis.command", span)
|
||||
child.Finish()
|
||||
|
||||
// the child is finished but it's not recorded in
|
||||
// the tracer buffer because the service is missing
|
||||
assert.True(child.Duration > 0)
|
||||
assert.Equal(1, len(tracer.channels.trace))
|
||||
}
|
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)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue