-
Notifications
You must be signed in to change notification settings - Fork 137
/
otel.go
118 lines (93 loc) · 3.33 KB
/
otel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package dtotel
import (
"context"
k8ssecret "github.com/Dynatrace/dynatrace-operator/pkg/util/kubeobjects/secret"
"github.com/Dynatrace/dynatrace-operator/pkg/version"
"github.com/pkg/errors"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric/noop"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type shutdownFn func(ctx context.Context) error
// Start sets up and starts all components needed for creating OpenTelemetry traces and metrics as well as some common auto-instrumentation
// It logs and swallows all errors to not prevent the application from startup.
func Start(ctx context.Context, otelServiceName string, apiReader client.Reader, webhookNamespace string) func() {
endpoint, apiToken, err := getOtelConfig(ctx, apiReader, webhookNamespace)
if err != nil {
log.Info("failed to read OpenTelementry config secret", "err", err.Error())
return setupNoopOTel()
}
shutdown, err := setupOtlpOTel(ctx, otelServiceName, endpoint, apiToken)
if err != nil {
log.Error(err, "failed to setup OTLP OpenTelementry")
return setupNoopOTel()
}
return shutdown
}
func setupOtlpOTel(ctx context.Context, otelServiceName string, endpoint string, apiToken string) (func(), error) {
otelResource, err := newResource(otelServiceName)
if err != nil {
return nil, err
}
_, tracesShutdownFn, err := setupTracesWithOtlp(ctx, otelResource, endpoint, apiToken)
if err != nil {
return nil, err
}
meterProvider, metricsShutdownFn, err := setupMetricsWithOtlp(ctx, otelResource, endpoint, apiToken)
if err != nil {
_ = tracesShutdownFn(ctx)
return nil, err
}
startAutoInstrumentation(meterProvider)
return func() {
_ = tracesShutdownFn(ctx)
_ = metricsShutdownFn(ctx)
}, nil
}
// setupNoopOTel makes sure, that OpenTelementry is properly configured so that no subsequent usage of OpenTelementry leads to panics while keeping
// a minimal impact on runtime. Basically all collected metrics and traces get discarded right away.
func setupNoopOTel() func() {
otel.SetMeterProvider(noop.NewMeterProvider())
otel.SetTracerProvider(trace.NewNoopTracerProvider())
log.Info("use Noop providers for OpenTelemetry")
return func() {}
}
// newResource returns a resource describing this application.
func newResource(otelServiceName string) (*resource.Resource, error) {
r, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(otelServiceName),
semconv.ServiceVersionKey.String(version.Version),
),
)
return r, errors.WithStack(err)
}
func getOtelConfig(ctx context.Context, apiReader client.Reader, namespace string) (string, string, error) {
if apiReader == nil {
return "", "", errors.Errorf("invalid API reader")
}
secretName := types.NamespacedName{
Namespace: namespace,
Name: otelSecretName,
}
query := k8ssecret.NewQuery(ctx, nil, apiReader, log)
secret, err := query.Get(secretName)
if err != nil {
return "", "", errors.WithStack(err)
}
endpoint, err := k8ssecret.ExtractToken(&secret, otelApiEndpointKey)
if err != nil {
return "", "", err
}
token, err := k8ssecret.ExtractToken(&secret, otelAccessTokenKey)
if err != nil {
return "", "", err
}
return endpoint, token, nil
}