2019-08-03 17:13:38 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2019-11-18 08:30:03 -07:00
|
|
|
package ocagent_test
|
2019-08-03 17:13:38 -06:00
|
|
|
|
|
|
|
import (
|
2019-11-22 11:41:57 -07:00
|
|
|
"bytes"
|
2020-03-10 19:43:28 -06:00
|
|
|
"context"
|
2019-08-03 17:13:38 -06:00
|
|
|
"encoding/json"
|
2019-11-27 09:24:29 -07:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"sync"
|
2019-08-03 17:13:38 -06:00
|
|
|
"testing"
|
2019-11-26 22:19:20 -07:00
|
|
|
"time"
|
2019-08-03 17:13:38 -06:00
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
|
|
|
"golang.org/x/tools/internal/event/core"
|
|
|
|
"golang.org/x/tools/internal/event/export"
|
|
|
|
"golang.org/x/tools/internal/event/export/metric"
|
|
|
|
"golang.org/x/tools/internal/event/export/ocagent"
|
2020-04-20 19:50:02 -06:00
|
|
|
"golang.org/x/tools/internal/event/keys"
|
2020-04-20 13:44:34 -06:00
|
|
|
"golang.org/x/tools/internal/event/label"
|
2019-08-03 17:13:38 -06:00
|
|
|
)
|
|
|
|
|
2019-11-26 22:19:20 -07:00
|
|
|
const testNodeStr = `{
|
|
|
|
"node":{
|
|
|
|
"identifier":{
|
|
|
|
"host_name":"tester",
|
|
|
|
"pid":1,
|
|
|
|
"start_timestamp":"1970-01-01T00:00:00Z"
|
|
|
|
},
|
|
|
|
"library_info":{
|
|
|
|
"language":4,
|
|
|
|
"exporter_version":"0.0.1",
|
|
|
|
"core_library_version":"x/tools"
|
|
|
|
},
|
|
|
|
"service_info":{
|
|
|
|
"name":"ocagent-tests"
|
|
|
|
}
|
|
|
|
},`
|
|
|
|
|
2020-03-13 12:20:13 -06:00
|
|
|
var (
|
2020-04-20 19:50:02 -06:00
|
|
|
keyDB = keys.NewString("db", "the database name")
|
|
|
|
keyMethod = keys.NewString("method", "a metric grouping key")
|
|
|
|
keyRoute = keys.NewString("route", "another metric grouping key")
|
2020-03-10 19:43:28 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
key1DB = keys.NewString("1_db", "A test string key")
|
2020-03-10 19:43:28 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
key2aAge = keys.NewFloat64("2a_age", "A test float64 key")
|
|
|
|
key2bTTL = keys.NewFloat32("2b_ttl", "A test float32 key")
|
|
|
|
key2cExpiryMS = keys.NewFloat64("2c_expiry_ms", "A test float64 key")
|
2020-03-10 19:43:28 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
key3aRetry = keys.NewBoolean("3a_retry", "A test boolean key")
|
|
|
|
key3bStale = keys.NewBoolean("3b_stale", "Another test boolean key")
|
2020-03-10 19:43:28 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
key4aMax = keys.NewInt("4a_max", "A test int key")
|
|
|
|
key4bOpcode = keys.NewInt8("4b_opcode", "A test int8 key")
|
|
|
|
key4cBase = keys.NewInt16("4c_base", "A test int16 key")
|
|
|
|
key4eChecksum = keys.NewInt32("4e_checksum", "A test int32 key")
|
|
|
|
key4fMode = keys.NewInt64("4f_mode", "A test int64 key")
|
2020-03-10 19:43:28 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
key5aMin = keys.NewUInt("5a_min", "A test uint key")
|
|
|
|
key5bMix = keys.NewUInt8("5b_mix", "A test uint8 key")
|
|
|
|
key5cPort = keys.NewUInt16("5c_port", "A test uint16 key")
|
|
|
|
key5dMinHops = keys.NewUInt32("5d_min_hops", "A test uint32 key")
|
|
|
|
key5eMaxHops = keys.NewUInt64("5e_max_hops", "A test uint64 key")
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-04-20 19:50:02 -06:00
|
|
|
recursiveCalls = keys.NewInt64("recursive_calls", "Number of recursive calls")
|
|
|
|
bytesIn = keys.NewInt64("bytes_in", "Number of bytes in") //, unit.Bytes)
|
|
|
|
latencyMs = keys.NewFloat64("latency", "The latency in milliseconds") //, unit.Milliseconds)
|
2020-03-17 14:00:16 -06:00
|
|
|
|
|
|
|
metricLatency = metric.HistogramFloat64{
|
|
|
|
Name: "latency_ms",
|
|
|
|
Description: "The latency of calls in milliseconds",
|
2020-04-20 13:44:34 -06:00
|
|
|
Keys: []label.Key{keyMethod, keyRoute},
|
2020-03-17 14:00:16 -06:00
|
|
|
Buckets: []float64{0, 5, 10, 25, 50},
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
2020-03-17 14:00:16 -06:00
|
|
|
|
|
|
|
metricBytesIn = metric.HistogramInt64{
|
|
|
|
Name: "latency_ms",
|
|
|
|
Description: "The latency of calls in milliseconds",
|
2020-04-20 13:44:34 -06:00
|
|
|
Keys: []label.Key{keyMethod, keyRoute},
|
2020-03-17 14:00:16 -06:00
|
|
|
Buckets: []int64{0, 10, 50, 100, 500, 1000, 2000},
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
2020-03-17 14:00:16 -06:00
|
|
|
|
|
|
|
metricRecursiveCalls = metric.Scalar{
|
|
|
|
Name: "latency_ms",
|
|
|
|
Description: "The latency of calls in milliseconds",
|
2020-04-20 13:44:34 -06:00
|
|
|
Keys: []label.Key{keyMethod, keyRoute},
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
2020-03-13 12:20:13 -06:00
|
|
|
)
|
2019-08-03 17:13:38 -06:00
|
|
|
|
2020-03-10 19:43:28 -06:00
|
|
|
type testExporter struct {
|
|
|
|
ocagent *ocagent.Exporter
|
|
|
|
sent fakeSender
|
|
|
|
}
|
|
|
|
|
|
|
|
func registerExporter() *testExporter {
|
|
|
|
exporter := &testExporter{}
|
2020-03-13 12:20:13 -06:00
|
|
|
cfg := ocagent.Config{
|
|
|
|
Host: "tester",
|
|
|
|
Process: 1,
|
|
|
|
Service: "ocagent-tests",
|
2020-03-10 19:43:28 -06:00
|
|
|
Client: &http.Client{Transport: &exporter.sent},
|
2019-08-03 17:13:38 -06:00
|
|
|
}
|
2020-03-13 12:20:13 -06:00
|
|
|
cfg.Start, _ = time.Parse(time.RFC3339Nano, "1970-01-01T00:00:00Z")
|
2020-03-10 19:43:28 -06:00
|
|
|
exporter.ocagent = ocagent.Connect(&cfg)
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
metrics := metric.Config{}
|
|
|
|
metricLatency.Record(&metrics, latencyMs)
|
|
|
|
metricBytesIn.Record(&metrics, bytesIn)
|
|
|
|
metricRecursiveCalls.SumInt64(&metrics, recursiveCalls)
|
|
|
|
|
|
|
|
e := exporter.ocagent.ProcessEvent
|
|
|
|
e = metrics.Exporter(e)
|
|
|
|
e = spanFixer(e)
|
|
|
|
e = export.Spans(e)
|
|
|
|
e = export.Labels(e)
|
|
|
|
e = timeFixer(e)
|
|
|
|
event.SetExporter(e)
|
2020-03-10 19:43:28 -06:00
|
|
|
return exporter
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func timeFixer(output event.Exporter) event.Exporter {
|
|
|
|
start, _ := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:30Z")
|
|
|
|
at, _ := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:40Z")
|
|
|
|
end, _ := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:50Z")
|
2020-04-20 13:44:34 -06:00
|
|
|
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
|
2020-03-20 06:29:48 -06:00
|
|
|
switch {
|
|
|
|
case ev.IsStartSpan():
|
2020-04-20 21:00:05 -06:00
|
|
|
ev = core.CloneEvent(ev, start)
|
2020-03-20 06:29:48 -06:00
|
|
|
case ev.IsEndSpan():
|
2020-04-20 21:00:05 -06:00
|
|
|
ev = core.CloneEvent(ev, end)
|
2020-03-20 06:29:48 -06:00
|
|
|
default:
|
2020-04-20 21:00:05 -06:00
|
|
|
ev = core.CloneEvent(ev, at)
|
2020-03-20 06:29:48 -06:00
|
|
|
}
|
2020-04-20 13:44:34 -06:00
|
|
|
return output(ctx, ev, lm)
|
2020-03-10 19:43:28 -06:00
|
|
|
}
|
2020-03-20 06:29:48 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func spanFixer(output event.Exporter) event.Exporter {
|
2020-04-20 13:44:34 -06:00
|
|
|
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
|
2020-03-20 06:29:48 -06:00
|
|
|
if ev.IsStartSpan() {
|
|
|
|
span := export.GetSpan(ctx)
|
|
|
|
span.ID = export.SpanContext{}
|
|
|
|
}
|
2020-04-20 13:44:34 -06:00
|
|
|
return output(ctx, ev, lm)
|
2020-03-10 19:43:28 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *testExporter) Output(route string) []byte {
|
|
|
|
e.ocagent.Flush()
|
|
|
|
return e.sent.get(route)
|
2019-08-03 17:13:38 -06:00
|
|
|
}
|
|
|
|
|
2019-11-18 08:30:03 -07:00
|
|
|
func checkJSON(t *testing.T, got, want []byte) {
|
2019-11-22 11:41:57 -07:00
|
|
|
// compare the compact form, to allow for formatting differences
|
|
|
|
g := &bytes.Buffer{}
|
|
|
|
if err := json.Compact(g, got); err != nil {
|
2019-11-18 08:30:03 -07:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2019-11-22 11:41:57 -07:00
|
|
|
w := &bytes.Buffer{}
|
|
|
|
if err := json.Compact(w, want); err != nil {
|
2019-11-18 08:30:03 -07:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2019-11-22 11:41:57 -07:00
|
|
|
if g.String() != w.String() {
|
2019-11-26 22:19:20 -07:00
|
|
|
t.Fatalf("Got:\n%s\nWant:\n%s", g, w)
|
2019-11-18 08:30:03 -07:00
|
|
|
}
|
2019-08-03 17:13:38 -06:00
|
|
|
}
|
2019-11-27 09:24:29 -07:00
|
|
|
|
|
|
|
type fakeSender struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
data map[string][]byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *fakeSender) get(route string) []byte {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
data, found := s.data[route]
|
|
|
|
if found {
|
|
|
|
delete(s.data, route)
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *fakeSender) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
if s.data == nil {
|
|
|
|
s.data = make(map[string][]byte)
|
|
|
|
}
|
|
|
|
data, err := ioutil.ReadAll(req.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
path := req.URL.EscapedPath()
|
|
|
|
if _, found := s.data[path]; found {
|
|
|
|
return nil, fmt.Errorf("duplicate delivery to %v", path)
|
|
|
|
}
|
|
|
|
s.data[path] = data
|
|
|
|
return &http.Response{
|
|
|
|
Status: "200 OK",
|
|
|
|
StatusCode: 200,
|
|
|
|
Proto: "HTTP/1.0",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
|
|
|
}, nil
|
|
|
|
}
|