377 lines
11 KiB
Go
377 lines
11 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
io_prometheus_client "github.com/prometheus/client_model/go"
|
|
"github.com/prometheus/common/expfmt"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opencensus.io/stats"
|
|
"go.opencensus.io/stats/view"
|
|
"go.opentelemetry.io/contrib/config"
|
|
"go.opentelemetry.io/otel/metric"
|
|
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/config/configtelemetry"
|
|
"go.opentelemetry.io/collector/featuregate"
|
|
"go.opentelemetry.io/collector/internal/globalgates"
|
|
"go.opentelemetry.io/collector/internal/testutil"
|
|
semconv "go.opentelemetry.io/collector/semconv/v1.18.0"
|
|
"go.opentelemetry.io/collector/service/internal/proctelemetry"
|
|
"go.opentelemetry.io/collector/service/internal/resource"
|
|
"go.opentelemetry.io/collector/service/telemetry"
|
|
)
|
|
|
|
const (
|
|
metricPrefix = "otelcol_"
|
|
otelPrefix = "otel_sdk_"
|
|
ocPrefix = "oc_sdk_"
|
|
grpcPrefix = "gprc_"
|
|
httpPrefix = "http_"
|
|
counterName = "test_counter"
|
|
)
|
|
|
|
func TestTelemetryInit(t *testing.T) {
|
|
type metricValue struct {
|
|
value float64
|
|
labels map[string]string
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
disableHighCard bool
|
|
disableCensusBridge bool
|
|
expectedMetrics map[string]metricValue
|
|
extendedConfig bool
|
|
cfg *telemetry.Config
|
|
}{
|
|
{
|
|
name: "UseOpenTelemetryForInternalMetrics",
|
|
expectedMetrics: map[string]metricValue{
|
|
metricPrefix + ocPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + otelPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + grpcPrefix + counterName: {
|
|
value: 11,
|
|
labels: map[string]string{
|
|
"net_sock_peer_addr": "",
|
|
"net_sock_peer_name": "",
|
|
"net_sock_peer_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + httpPrefix + counterName: {
|
|
value: 10,
|
|
labels: map[string]string{
|
|
"net_host_name": "",
|
|
"net_host_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
"target_info": {
|
|
value: 0,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "DisableHighCardinalityWithOtel",
|
|
disableHighCard: true,
|
|
expectedMetrics: map[string]metricValue{
|
|
metricPrefix + ocPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + otelPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + grpcPrefix + counterName: {
|
|
value: 11,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + httpPrefix + counterName: {
|
|
value: 10,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
"target_info": {
|
|
value: 0,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "UseOTelWithSDKConfiguration",
|
|
extendedConfig: true,
|
|
cfg: &telemetry.Config{
|
|
Metrics: telemetry.MetricsConfig{
|
|
Level: configtelemetry.LevelDetailed,
|
|
},
|
|
Traces: telemetry.TracesConfig{
|
|
Processors: []config.SpanProcessor{
|
|
{
|
|
Batch: &config.BatchSpanProcessor{
|
|
Exporter: config.SpanExporter{
|
|
Console: config.Console{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Resource: map[string]*string{
|
|
semconv.AttributeServiceInstanceID: &testInstanceID,
|
|
},
|
|
},
|
|
expectedMetrics: map[string]metricValue{
|
|
metricPrefix + ocPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + otelPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + grpcPrefix + counterName: {
|
|
value: 11,
|
|
labels: map[string]string{
|
|
"net_sock_peer_addr": "",
|
|
"net_sock_peer_name": "",
|
|
"net_sock_peer_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + httpPrefix + counterName: {
|
|
value: 10,
|
|
labels: map[string]string{
|
|
"net_host_name": "",
|
|
"net_host_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
"target_info": {
|
|
value: 0,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "DisableOpenCensusBridge",
|
|
expectedMetrics: map[string]metricValue{
|
|
metricPrefix + otelPrefix + counterName: {
|
|
value: 13,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + grpcPrefix + counterName: {
|
|
value: 11,
|
|
labels: map[string]string{
|
|
"net_sock_peer_addr": "",
|
|
"net_sock_peer_name": "",
|
|
"net_sock_peer_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
metricPrefix + httpPrefix + counterName: {
|
|
value: 10,
|
|
labels: map[string]string{
|
|
"net_host_name": "",
|
|
"net_host_port": "",
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
"target_info": {
|
|
value: 0,
|
|
labels: map[string]string{
|
|
"service_name": "otelcol",
|
|
"service_version": "latest",
|
|
"service_instance_id": testInstanceID,
|
|
},
|
|
},
|
|
},
|
|
disableCensusBridge: true,
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
require.NoError(t, featuregate.GlobalRegistry().Set(globalgates.DisableOpenCensusBridge.ID(), tc.disableCensusBridge))
|
|
t.Cleanup(func() {
|
|
require.NoError(t, featuregate.GlobalRegistry().Set(globalgates.DisableOpenCensusBridge.ID(), true))
|
|
})
|
|
if tc.extendedConfig {
|
|
tc.cfg.Metrics.Readers = []config.MetricReader{
|
|
{
|
|
Pull: &config.PullMetricReader{
|
|
Exporter: config.MetricExporter{
|
|
Prometheus: testutil.GetAvailableLocalAddressPrometheus(t),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
if tc.cfg == nil {
|
|
tc.cfg = &telemetry.Config{
|
|
Resource: map[string]*string{
|
|
semconv.AttributeServiceInstanceID: &testInstanceID,
|
|
},
|
|
Metrics: telemetry.MetricsConfig{
|
|
Level: configtelemetry.LevelDetailed,
|
|
Address: testutil.GetAvailableLocalAddress(t),
|
|
},
|
|
}
|
|
}
|
|
set := meterProviderSettings{
|
|
res: resource.New(component.NewDefaultBuildInfo(), tc.cfg.Resource),
|
|
cfg: tc.cfg.Metrics,
|
|
asyncErrorChannel: make(chan error),
|
|
}
|
|
mp, err := newMeterProvider(set, tc.disableHighCard)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
if prov, ok := mp.(interface{ Shutdown(context.Context) error }); ok {
|
|
require.NoError(t, prov.Shutdown(context.Background()))
|
|
}
|
|
}()
|
|
|
|
v := createTestMetrics(t, mp)
|
|
defer func() {
|
|
view.Unregister(v)
|
|
}()
|
|
|
|
metrics := getMetricsFromPrometheus(t, mp.(*meterProvider).servers[0].Handler)
|
|
require.Equal(t, len(tc.expectedMetrics), len(metrics))
|
|
|
|
for metricName, metricValue := range tc.expectedMetrics {
|
|
mf, present := metrics[metricName]
|
|
require.True(t, present, "expected metric %q was not present", metricName)
|
|
require.Len(t, mf.Metric, 1, "only one measure should exist for metric %q", metricName)
|
|
|
|
labels := make(map[string]string)
|
|
for _, pair := range mf.Metric[0].Label {
|
|
labels[pair.GetName()] = pair.GetValue()
|
|
}
|
|
|
|
require.Equal(t, metricValue.labels, labels, "labels for metric %q was different than expected", metricName)
|
|
require.Equal(t, metricValue.value, mf.Metric[0].Counter.GetValue(), "value for metric %q was different than expected", metricName)
|
|
}
|
|
})
|
|
|
|
}
|
|
}
|
|
|
|
func createTestMetrics(t *testing.T, mp metric.MeterProvider) *view.View {
|
|
// Creates a OTel Go counter
|
|
counter, err := mp.Meter("collector_test").Int64Counter(otelPrefix+counterName, metric.WithUnit("ms"))
|
|
require.NoError(t, err)
|
|
counter.Add(context.Background(), 13)
|
|
|
|
grpcExampleCounter, err := mp.Meter(proctelemetry.GRPCInstrumentation).Int64Counter(grpcPrefix + counterName)
|
|
require.NoError(t, err)
|
|
grpcExampleCounter.Add(context.Background(), 11, metric.WithAttributes(proctelemetry.GRPCUnacceptableKeyValues...))
|
|
|
|
httpExampleCounter, err := mp.Meter(proctelemetry.HTTPInstrumentation).Int64Counter(httpPrefix + counterName)
|
|
require.NoError(t, err)
|
|
httpExampleCounter.Add(context.Background(), 10, metric.WithAttributes(proctelemetry.HTTPUnacceptableKeyValues...))
|
|
|
|
// Creates a OpenCensus measure
|
|
ocCounter := stats.Int64(ocPrefix+counterName, counterName, stats.UnitDimensionless)
|
|
v := &view.View{
|
|
Name: ocPrefix + counterName,
|
|
Description: ocCounter.Description(),
|
|
Measure: ocCounter,
|
|
Aggregation: view.Sum(),
|
|
}
|
|
err = view.Register(v)
|
|
require.NoError(t, err)
|
|
|
|
stats.Record(context.Background(), stats.Int64(ocPrefix+counterName, counterName, stats.UnitDimensionless).M(13))
|
|
|
|
// Forces a flush for the view data.
|
|
_, _ = view.RetrieveData(ocPrefix + counterName)
|
|
|
|
return v
|
|
}
|
|
|
|
func getMetricsFromPrometheus(t *testing.T, handler http.Handler) map[string]*io_prometheus_client.MetricFamily {
|
|
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
|
require.NoError(t, err)
|
|
|
|
rr := httptest.NewRecorder()
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
var parser expfmt.TextParser
|
|
parsed, err := parser.TextToMetricFamilies(rr.Body)
|
|
require.NoError(t, err)
|
|
|
|
return parsed
|
|
|
|
}
|