1// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package rpcmetrics
16
17import (
18	"fmt"
19	"testing"
20	"time"
21
22	opentracing "github.com/opentracing/opentracing-go"
23	"github.com/stretchr/testify/assert"
24	u "github.com/uber/jaeger-lib/metrics/metricstest"
25
26	"github.com/opentracing/opentracing-go/ext"
27	jaeger "github.com/uber/jaeger-client-go"
28)
29
30func ExampleObserver() {
31	metricsFactory := u.NewFactory(0)
32	metricsObserver := NewObserver(
33		metricsFactory,
34		DefaultNameNormalizer,
35	)
36	tracer, closer := jaeger.NewTracer(
37		"serviceName",
38		jaeger.NewConstSampler(true),
39		jaeger.NewInMemoryReporter(),
40		jaeger.TracerOptions.Observer(metricsObserver),
41	)
42	defer closer.Close()
43
44	span := tracer.StartSpan("test", ext.SpanKindRPCServer)
45	span.Finish()
46
47	c, _ := metricsFactory.Snapshot()
48	fmt.Printf("requests (success): %d\n", c["requests|endpoint=test|error=false"])
49	fmt.Printf("requests (failure): %d\n", c["requests|endpoint=test|error=true"])
50	// Output:
51	// requests (success): 1
52	// requests (failure): 0
53}
54
55type testTracer struct {
56	metrics *u.Factory
57	tracer  opentracing.Tracer
58}
59
60func withTestTracer(runTest func(tt *testTracer)) {
61	sampler := jaeger.NewConstSampler(true)
62	reporter := jaeger.NewInMemoryReporter()
63	metrics := u.NewFactory(time.Minute)
64	observer := NewObserver(metrics, DefaultNameNormalizer)
65	tracer, closer := jaeger.NewTracer(
66		"test",
67		sampler,
68		reporter,
69		jaeger.TracerOptions.Observer(observer))
70	defer closer.Close()
71	runTest(&testTracer{
72		metrics: metrics,
73		tracer:  tracer,
74	})
75}
76
77func TestObserver(t *testing.T) {
78	withTestTracer(func(testTracer *testTracer) {
79		ts := time.Now()
80		finishOptions := opentracing.FinishOptions{
81			FinishTime: ts.Add(50 * time.Millisecond),
82		}
83
84		testCases := []struct {
85			name           string
86			tag            opentracing.Tag
87			opNameOverride string
88			err            bool
89		}{
90			{name: "local-span", tag: opentracing.Tag{Key: "x", Value: "y"}},
91			{name: "get-user", tag: ext.SpanKindRPCServer},
92			{name: "get-user", tag: ext.SpanKindRPCServer, opNameOverride: "get-user-override"},
93			{name: "get-user", tag: ext.SpanKindRPCServer, err: true},
94			{name: "get-user-client", tag: ext.SpanKindRPCClient},
95		}
96
97		for _, testCase := range testCases {
98			span := testTracer.tracer.StartSpan(
99				testCase.name,
100				testCase.tag,
101				opentracing.StartTime(ts),
102			)
103			if testCase.opNameOverride != "" {
104				span.SetOperationName(testCase.opNameOverride)
105			}
106			if testCase.err {
107				ext.Error.Set(span, true)
108			}
109			span.FinishWithOptions(finishOptions)
110		}
111
112		testTracer.metrics.AssertCounterMetrics(t,
113			u.ExpectedMetric{Name: "requests", Tags: endpointTags("local-span", "error", "false"), Value: 0},
114			u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "false"), Value: 1},
115			u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "true"), Value: 1},
116			u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-override", "error", "false"), Value: 1},
117			u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-client", "error", "false"), Value: 0},
118		)
119		// TODO something wrong with string generation, .P99 should not be appended to the tag
120		// as a result we cannot use u.AssertGaugeMetrics
121		_, g := testTracer.metrics.Snapshot()
122		assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=false.P99"])
123		assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=true.P99"])
124	})
125}
126
127func TestTags(t *testing.T) {
128	type tagTestCase struct {
129		key     string
130		value   interface{}
131		metrics []u.ExpectedMetric
132	}
133
134	testCases := []tagTestCase{
135		{key: "something", value: 42, metrics: []u.ExpectedMetric{
136			{Name: "requests", Value: 1, Tags: tags("error", "false")},
137		}},
138		{key: "error", value: true, metrics: []u.ExpectedMetric{
139			{Name: "requests", Value: 1, Tags: tags("error", "true")},
140		}},
141		{key: "error", value: "true", metrics: []u.ExpectedMetric{
142			{Name: "requests", Value: 1, Tags: tags("error", "true")},
143		}},
144	}
145
146	for i := 2; i <= 5; i++ {
147		values := []interface{}{
148			i * 100,
149			uint16(i * 100),
150			fmt.Sprintf("%d00", i),
151		}
152		for _, v := range values {
153			testCases = append(testCases, tagTestCase{
154				key: "http.status_code", value: v, metrics: []u.ExpectedMetric{
155					{Name: "http_requests", Value: 1, Tags: tags("status_code", fmt.Sprintf("%dxx", i))},
156				},
157			})
158		}
159	}
160
161	for _, tc := range testCases {
162		testCase := tc // capture loop var
163		for i := range testCase.metrics {
164			testCase.metrics[i].Tags["endpoint"] = "span"
165		}
166		t.Run(fmt.Sprintf("%s-%v", testCase.key, testCase.value), func(t *testing.T) {
167			withTestTracer(func(testTracer *testTracer) {
168				span := testTracer.tracer.StartSpan("span", ext.SpanKindRPCServer)
169				span.SetTag(testCase.key, testCase.value)
170				span.Finish()
171				testTracer.metrics.AssertCounterMetrics(t, testCase.metrics...)
172			})
173		})
174	}
175}
176