Skip to content

Commit 56a33cf

Browse files
authored
refactor: Improve the OtelConfig handling (#41)
Signed-off-by: Joonas Bergius <joonas@cosmonic.com>
1 parent f8ed71d commit 56a33cf

4 files changed

Lines changed: 294 additions & 29 deletions

File tree

host.go

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package provider
22

3-
import "encoding/json"
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strings"
7+
)
48

59
type RedactedString string
610

@@ -31,6 +35,118 @@ type OtelConfig struct {
3135
Protocol string `json:"protocol,omitempty"`
3236
}
3337

38+
type otelSignal int
39+
40+
const (
41+
traces otelSignal = iota
42+
metrics
43+
logs
44+
45+
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint
46+
OtelExporterGrpcEndpoint = "http://localhost:4317"
47+
OtelExporterHttpEndpoint = "http://localhost:4318"
48+
49+
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_traces_endpoint
50+
OtelExporterHttpTracesPath = "/v1/traces"
51+
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_metrics_endpoint
52+
OtelExporterHttpMetricsPath = "/v1/metrics"
53+
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_logs_endpoint
54+
OtelExporterHttpLogsPath = "/v1/logs"
55+
)
56+
57+
// OtelProtocol returns the configured OpenTelemetry protocol if one is provided,
58+
// otherwise defaulting to http.
59+
func (config *OtelConfig) OtelProtocol() string {
60+
protocol := OtelProtocolHTTP
61+
if config.Protocol != "" {
62+
protocol = strings.ToLower(config.Protocol)
63+
}
64+
return protocol
65+
}
66+
67+
// TracesURL returns the configured TracesEndpoint as-is if one is provided,
68+
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
69+
// Protocol appropriate path.
70+
func (config *OtelConfig) TracesURL() string {
71+
if config.TracesEndpoint != "" {
72+
return config.TracesEndpoint
73+
}
74+
75+
return config.resolveSignalUrl(traces)
76+
}
77+
78+
// MetricsURL returns the configured MetricsEndpoint as-is if one is provided,
79+
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
80+
// Protocol appropriate path.
81+
func (config *OtelConfig) MetricsURL() string {
82+
if config.MetricsEndpoint != "" {
83+
return config.MetricsEndpoint
84+
}
85+
86+
return config.resolveSignalUrl(metrics)
87+
}
88+
89+
// LogsURL returns the configured LogsEndpoint as-is if one is provided,
90+
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
91+
// Protocol appropriate path.
92+
func (config *OtelConfig) LogsURL() string {
93+
if config.LogsEndpoint != "" {
94+
return config.LogsEndpoint
95+
}
96+
97+
return config.resolveSignalUrl(logs)
98+
}
99+
100+
// TracesEnabled returns whether emitting traces has been enabled.
101+
func (config *OtelConfig) TracesEnabled() bool {
102+
return config.EnableObservability || config.EnableTraces
103+
}
104+
105+
// MetricsEnabled returns whether emitting metrics has been enabled.
106+
func (config *OtelConfig) MetricsEnabled() bool {
107+
return config.EnableObservability || config.EnableMetrics
108+
}
109+
110+
// LogsEnabled returns whether emitting logs has been enabled.
111+
func (config *OtelConfig) LogsEnabled() bool {
112+
return config.EnableObservability || config.EnableLogs
113+
}
114+
115+
func (config *OtelConfig) resolveSignalUrl(signal otelSignal) string {
116+
endpoint := config.defaultEndpoint()
117+
if config.ObservabilityEndpoint != "" {
118+
endpoint = config.ObservabilityEndpoint
119+
}
120+
endpoint = strings.TrimRight(endpoint, "/")
121+
122+
return fmt.Sprintf("%s%s", endpoint, config.defaultSignalPath(signal))
123+
}
124+
125+
func (config *OtelConfig) defaultEndpoint() string {
126+
endpoint := OtelExporterHttpEndpoint
127+
if config.OtelProtocol() == OtelProtocolGRPC {
128+
endpoint = OtelExporterGrpcEndpoint
129+
}
130+
return endpoint
131+
}
132+
133+
func (config *OtelConfig) defaultSignalPath(signal otelSignal) string {
134+
// In case of gRPC, we return empty string gRPC doesn't need a path to be set for it.
135+
if config.OtelProtocol() == OtelProtocolGRPC {
136+
return ""
137+
}
138+
139+
switch signal {
140+
case traces:
141+
return OtelExporterHttpTracesPath
142+
case metrics:
143+
return OtelExporterHttpMetricsPath
144+
case logs:
145+
return OtelExporterHttpLogsPath
146+
}
147+
return ""
148+
}
149+
34150
type HostData struct {
35151
HostID string `json:"host_id,omitempty"`
36152
LatticeRPCPrefix string `json:"lattice_rpc_prefix,omitempty"`

host_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,168 @@ func TestRedactedStringLogging(t *testing.T) {
2828
t.Error("text slog handler should not have contained the secret string")
2929
}
3030
}
31+
32+
func TestOtelConfigProtocol(t *testing.T) {
33+
type test struct {
34+
name string
35+
config OtelConfig
36+
protocol string
37+
}
38+
39+
tests := []test{
40+
{
41+
name: "Defaults to http",
42+
config: OtelConfig{},
43+
protocol: "http",
44+
},
45+
{
46+
name: "Explicit Grpc Rust enum variant",
47+
config: OtelConfig{Protocol: "Grpc"},
48+
protocol: "grpc",
49+
},
50+
{
51+
name: "Explicit Http Rust enum variant",
52+
config: OtelConfig{Protocol: "Http"},
53+
protocol: "http",
54+
},
55+
}
56+
57+
for _, tc := range tests {
58+
if tc.config.OtelProtocol() != tc.protocol {
59+
t.Fatalf("%s / OtelProtocol: expected %q, got: %q", tc.name, tc.protocol, tc.config.OtelProtocol())
60+
}
61+
}
62+
}
63+
64+
func TestOtelConfigURLs(t *testing.T) {
65+
type test struct {
66+
name string
67+
config OtelConfig
68+
tracesURL string
69+
metricsURL string
70+
logsURL string
71+
}
72+
73+
tests := []test{
74+
{
75+
name: "Defaults with HTTP",
76+
config: OtelConfig{},
77+
tracesURL: "http://localhost:4318/v1/traces",
78+
metricsURL: "http://localhost:4318/v1/metrics",
79+
logsURL: "http://localhost:4318/v1/logs",
80+
},
81+
{
82+
name: "Defaults with gRPC",
83+
config: OtelConfig{Protocol: "Grpc"},
84+
tracesURL: "http://localhost:4317",
85+
metricsURL: "http://localhost:4317",
86+
logsURL: "http://localhost:4317",
87+
},
88+
{
89+
name: "Custom ObservabilityEndpoint",
90+
config: OtelConfig{ObservabilityEndpoint: "https://api.opentelemetry.com"},
91+
tracesURL: "https://api.opentelemetry.com/v1/traces",
92+
metricsURL: "https://api.opentelemetry.com/v1/metrics",
93+
logsURL: "https://api.opentelemetry.com/v1/logs",
94+
},
95+
{
96+
name: "Custom ObservabilityEndpoint with gRPC",
97+
config: OtelConfig{Protocol: "grpc", ObservabilityEndpoint: "https://api.opentelemetry.com"},
98+
tracesURL: "https://api.opentelemetry.com",
99+
metricsURL: "https://api.opentelemetry.com",
100+
logsURL: "https://api.opentelemetry.com",
101+
},
102+
{
103+
name: "Custom TracesEndpoint",
104+
config: OtelConfig{TracesEndpoint: "https://api.opentelemetry.com/v1/traces"},
105+
tracesURL: "https://api.opentelemetry.com/v1/traces",
106+
metricsURL: "http://localhost:4318/v1/metrics",
107+
logsURL: "http://localhost:4318/v1/logs",
108+
},
109+
{
110+
name: "Custom MetricsEndpoint",
111+
config: OtelConfig{MetricsEndpoint: "https://api.opentelemetry.com/v1/metrics"},
112+
tracesURL: "http://localhost:4318/v1/traces",
113+
metricsURL: "https://api.opentelemetry.com/v1/metrics",
114+
logsURL: "http://localhost:4318/v1/logs",
115+
},
116+
{
117+
name: "Custom LogsEndpoint",
118+
config: OtelConfig{LogsEndpoint: "https://api.opentelemetry.com/v1/logs"},
119+
tracesURL: "http://localhost:4318/v1/traces",
120+
metricsURL: "http://localhost:4318/v1/metrics",
121+
logsURL: "https://api.opentelemetry.com/v1/logs",
122+
},
123+
}
124+
125+
for _, tc := range tests {
126+
if tc.config.TracesURL() != tc.tracesURL {
127+
t.Fatalf("%s / TracesURL: expected %s, got: %s", tc.name, tc.tracesURL, tc.config.TracesURL())
128+
}
129+
if tc.config.MetricsURL() != tc.metricsURL {
130+
t.Fatalf("%s / MetricsURL: expected %s, got: %s", tc.name, tc.metricsURL, tc.config.MetricsURL())
131+
}
132+
if tc.config.LogsURL() != tc.logsURL {
133+
t.Fatalf("%s / LogsURL: expected %s, got: %s", tc.name, tc.logsURL, tc.config.LogsURL())
134+
}
135+
}
136+
}
137+
138+
func TestOtelConfigBooleans(t *testing.T) {
139+
type test struct {
140+
name string
141+
config OtelConfig
142+
tracesEnabled bool
143+
metricsEnabled bool
144+
logsEnabled bool
145+
}
146+
147+
tests := []test{
148+
{
149+
name: "Defaults",
150+
config: OtelConfig{},
151+
tracesEnabled: false,
152+
metricsEnabled: false,
153+
logsEnabled: false,
154+
},
155+
{
156+
name: "Enable all with EnableObservability",
157+
config: OtelConfig{EnableObservability: true},
158+
tracesEnabled: true,
159+
metricsEnabled: true,
160+
logsEnabled: true,
161+
},
162+
{
163+
name: "Enable just traces",
164+
config: OtelConfig{EnableTraces: true},
165+
tracesEnabled: true,
166+
metricsEnabled: false,
167+
logsEnabled: false,
168+
},
169+
{
170+
name: "Enable just metrics",
171+
config: OtelConfig{EnableMetrics: true},
172+
tracesEnabled: false,
173+
metricsEnabled: true,
174+
logsEnabled: false,
175+
},
176+
{
177+
name: "Enable just logs",
178+
config: OtelConfig{EnableLogs: true},
179+
tracesEnabled: false,
180+
metricsEnabled: false,
181+
logsEnabled: true,
182+
},
183+
}
184+
for _, tc := range tests {
185+
if tc.config.TracesEnabled() != tc.tracesEnabled {
186+
t.Fatalf("%s / TracesEnabled: expected %t, got: %t", tc.name, tc.tracesEnabled, tc.config.TracesEnabled())
187+
}
188+
if tc.config.MetricsEnabled() != tc.metricsEnabled {
189+
t.Fatalf("%s / MetricsEnabled: expected %t, got: %t", tc.name, tc.metricsEnabled, tc.config.MetricsEnabled())
190+
}
191+
if tc.config.LogsEnabled() != tc.logsEnabled {
192+
t.Fatalf("%s / LogsEnabled: expected %t, got: %t", tc.name, tc.logsEnabled, tc.config.LogsEnabled())
193+
}
194+
}
195+
}

observability.go

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8-
"strings"
98
"time"
109

1110
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
@@ -43,16 +42,11 @@ func newTracerProvider(ctx context.Context, config OtelConfig, serviceResource *
4342
var exporter trace.SpanExporter
4443
var err error
4544

46-
endpoint := config.TracesEndpoint
47-
if endpoint == "" {
48-
endpoint = config.ObservabilityEndpoint
49-
}
50-
51-
switch strings.ToLower(config.Protocol) {
45+
switch config.OtelProtocol() {
5246
case OtelProtocolGRPC:
53-
exporter, err = otlptracegrpc.New(ctx, otlptracegrpc.WithEndpointURL(endpoint))
47+
exporter, err = otlptracegrpc.New(ctx, otlptracegrpc.WithEndpointURL(config.TracesURL()))
5448
case OtelProtocolHTTP:
55-
exporter, err = otlptracehttp.New(ctx, otlptracehttp.WithEndpointURL(endpoint))
49+
exporter, err = otlptracehttp.New(ctx, otlptracehttp.WithEndpointURL(config.TracesURL()))
5650
default:
5751
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
5852
}
@@ -82,16 +76,11 @@ func newMeterProvider(ctx context.Context, config OtelConfig, serviceResource *r
8276
var exporter metric.Exporter
8377
var err error
8478

85-
endpoint := config.MetricsEndpoint
86-
if endpoint == "" {
87-
endpoint = config.ObservabilityEndpoint
88-
}
89-
90-
switch strings.ToLower(config.Protocol) {
79+
switch config.OtelProtocol() {
9180
case OtelProtocolGRPC:
92-
exporter, err = otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpointURL(endpoint))
81+
exporter, err = otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpointURL(config.MetricsURL()))
9382
case OtelProtocolHTTP:
94-
exporter, err = otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpointURL(endpoint))
83+
exporter, err = otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpointURL(config.MetricsURL()))
9584
default:
9685
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
9786
}
@@ -113,16 +102,11 @@ func newLoggerProvider(ctx context.Context, config OtelConfig, serviceResource *
113102
var exporter log.Exporter
114103
var err error
115104

116-
endpoint := config.LogsEndpoint
117-
if endpoint == "" {
118-
endpoint = config.ObservabilityEndpoint
119-
}
120-
121-
switch strings.ToLower(config.Protocol) {
105+
switch config.OtelProtocol() {
122106
case OtelProtocolGRPC:
123-
exporter, err = otlploggrpc.New(ctx, otlploggrpc.WithEndpointURL(endpoint))
107+
exporter, err = otlploggrpc.New(ctx, otlploggrpc.WithEndpointURL(config.LogsURL()))
124108
case OtelProtocolHTTP:
125-
exporter, err = otlploghttp.New(ctx, otlploghttp.WithEndpointURL(endpoint))
109+
exporter, err = otlploghttp.New(ctx, otlploghttp.WithEndpointURL(config.LogsURL()))
126110
default:
127111
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
128112
}

provider.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func NewWithHostDataSource(source io.Reader, options ...ProviderHandler) (*Wasmc
125125
return nil, err
126126
}
127127

128-
if hostData.OtelConfig.EnableObservability || hostData.OtelConfig.EnableMetrics {
128+
if hostData.OtelConfig.MetricsEnabled() {
129129
meterProvider, err := newMeterProvider(context.Background(), hostData.OtelConfig, serviceResource)
130130
if err != nil {
131131
return nil, err
@@ -134,7 +134,7 @@ func NewWithHostDataSource(source io.Reader, options ...ProviderHandler) (*Wasmc
134134
internalShutdownFuncs = append(internalShutdownFuncs, func(c context.Context) error { return meterProvider.Shutdown(c) })
135135
}
136136

137-
if hostData.OtelConfig.EnableObservability || hostData.OtelConfig.EnableTraces {
137+
if hostData.OtelConfig.TracesEnabled() {
138138
tracerProvider, err := newTracerProvider(context.Background(), hostData.OtelConfig, serviceResource)
139139
if err != nil {
140140
return nil, err
@@ -143,7 +143,7 @@ func NewWithHostDataSource(source io.Reader, options ...ProviderHandler) (*Wasmc
143143
internalShutdownFuncs = append(internalShutdownFuncs, func(c context.Context) error { return tracerProvider.Shutdown(c) })
144144
}
145145

146-
if hostData.OtelConfig.EnableObservability || hostData.OtelConfig.EnableLogs {
146+
if hostData.OtelConfig.LogsEnabled() {
147147
loggerProvider, err := newLoggerProvider(context.Background(), hostData.OtelConfig, serviceResource)
148148
if err != nil {
149149
return nil, err

0 commit comments

Comments
 (0)