Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Improve the OtelConfig handling #41

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion host.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package provider

import "encoding/json"
import (
"encoding/json"
"fmt"
"strings"
)

type RedactedString string

Expand Down Expand Up @@ -31,6 +35,118 @@ type OtelConfig struct {
Protocol string `json:"protocol,omitempty"`
}

type otelSignal int

const (
traces otelSignal = iota
metrics
logs

// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint
OtelExporterGrpcEndpoint = "http://localhost:4317"
OtelExporterHttpEndpoint = "http://localhost:4318"

// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_traces_endpoint
OtelExporterHttpTracesPath = "/v1/traces"
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_metrics_endpoint
OtelExporterHttpMetricsPath = "/v1/metrics"
// https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_logs_endpoint
OtelExporterHttpLogsPath = "/v1/logs"
)

// OtelProtocol returns the configured OpenTelemetry protocol if one is provided,
// otherwise defaulting to http.
func (config *OtelConfig) OtelProtocol() string {
protocol := OtelProtocolHTTP
if config.Protocol != "" {
protocol = strings.ToLower(config.Protocol)
}
return protocol
}

// TracesURL returns the configured TracesEndpoint as-is if one is provided,
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
// Protocol appropriate path.
func (config *OtelConfig) TracesURL() string {
if config.TracesEndpoint != "" {
return config.TracesEndpoint
}

return config.resolveSignalUrl(traces)
}

// MetricsURL returns the configured MetricsEndpoint as-is if one is provided,
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
// Protocol appropriate path.
func (config *OtelConfig) MetricsURL() string {
if config.MetricsEndpoint != "" {
return config.MetricsEndpoint
}

return config.resolveSignalUrl(metrics)
}

// LogsURL returns the configured LogsEndpoint as-is if one is provided,
// otherwise it resolves the URL based on ObservabilityEndpoint value and the
// Protocol appropriate path.
func (config *OtelConfig) LogsURL() string {
if config.LogsEndpoint != "" {
return config.LogsEndpoint
}

return config.resolveSignalUrl(logs)
}

// TracesEnabled returns whether emitting traces has been enabled.
func (config *OtelConfig) TracesEnabled() bool {
return config.EnableObservability || config.EnableTraces
}

// MetricsEnabled returns whether emitting metrics has been enabled.
func (config *OtelConfig) MetricsEnabled() bool {
return config.EnableObservability || config.EnableMetrics
}

// LogsEnabled returns whether emitting logs has been enabled.
func (config *OtelConfig) LogsEnabled() bool {
return config.EnableObservability || config.EnableLogs
}

func (config *OtelConfig) resolveSignalUrl(signal otelSignal) string {
endpoint := config.defaultEndpoint()
if config.ObservabilityEndpoint != "" {
endpoint = config.ObservabilityEndpoint
}
endpoint = strings.TrimRight(endpoint, "/")

return fmt.Sprintf("%s%s", endpoint, config.defaultSignalPath(signal))
}

func (config *OtelConfig) defaultEndpoint() string {
endpoint := OtelExporterHttpEndpoint
if config.OtelProtocol() == OtelProtocolGRPC {
endpoint = OtelExporterGrpcEndpoint
}
return endpoint
}

func (config *OtelConfig) defaultSignalPath(signal otelSignal) string {
// In case of gRPC, we return empty string gRPC doesn't need a path to be set for it.
if config.OtelProtocol() == OtelProtocolGRPC {
return ""
}

switch signal {
case traces:
return OtelExporterHttpTracesPath
case metrics:
return OtelExporterHttpMetricsPath
case logs:
return OtelExporterHttpLogsPath
}
return ""
}

type HostData struct {
HostID string `json:"host_id,omitempty"`
LatticeRPCPrefix string `json:"lattice_rpc_prefix,omitempty"`
Expand Down
165 changes: 165 additions & 0 deletions host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,168 @@ func TestRedactedStringLogging(t *testing.T) {
t.Error("text slog handler should not have contained the secret string")
}
}

func TestOtelConfigProtocol(t *testing.T) {
type test struct {
name string
config OtelConfig
protocol string
}

tests := []test{
{
name: "Defaults to http",
config: OtelConfig{},
protocol: "http",
},
{
name: "Explicit Grpc Rust enum variant",
config: OtelConfig{Protocol: "Grpc"},
protocol: "grpc",
},
{
name: "Explicit Http Rust enum variant",
config: OtelConfig{Protocol: "Http"},
protocol: "http",
},
}

for _, tc := range tests {
if tc.config.OtelProtocol() != tc.protocol {
t.Fatalf("%s / OtelProtocol: expected %q, got: %q", tc.name, tc.protocol, tc.config.OtelProtocol())
}
}
}

func TestOtelConfigURLs(t *testing.T) {
type test struct {
name string
config OtelConfig
tracesURL string
metricsURL string
logsURL string
}

tests := []test{
{
name: "Defaults with HTTP",
config: OtelConfig{},
tracesURL: "http://localhost:4318/v1/traces",
metricsURL: "http://localhost:4318/v1/metrics",
logsURL: "http://localhost:4318/v1/logs",
},
{
name: "Defaults with gRPC",
config: OtelConfig{Protocol: "Grpc"},
tracesURL: "http://localhost:4317",
metricsURL: "http://localhost:4317",
logsURL: "http://localhost:4317",
},
{
name: "Custom ObservabilityEndpoint",
config: OtelConfig{ObservabilityEndpoint: "https://api.opentelemetry.com"},
tracesURL: "https://api.opentelemetry.com/v1/traces",
metricsURL: "https://api.opentelemetry.com/v1/metrics",
logsURL: "https://api.opentelemetry.com/v1/logs",
},
{
name: "Custom ObservabilityEndpoint with gRPC",
config: OtelConfig{Protocol: "grpc", ObservabilityEndpoint: "https://api.opentelemetry.com"},
tracesURL: "https://api.opentelemetry.com",
metricsURL: "https://api.opentelemetry.com",
logsURL: "https://api.opentelemetry.com",
},
{
name: "Custom TracesEndpoint",
config: OtelConfig{TracesEndpoint: "https://api.opentelemetry.com/v1/traces"},
tracesURL: "https://api.opentelemetry.com/v1/traces",
metricsURL: "http://localhost:4318/v1/metrics",
logsURL: "http://localhost:4318/v1/logs",
},
{
name: "Custom MetricsEndpoint",
config: OtelConfig{MetricsEndpoint: "https://api.opentelemetry.com/v1/metrics"},
tracesURL: "http://localhost:4318/v1/traces",
metricsURL: "https://api.opentelemetry.com/v1/metrics",
logsURL: "http://localhost:4318/v1/logs",
},
{
name: "Custom LogsEndpoint",
config: OtelConfig{LogsEndpoint: "https://api.opentelemetry.com/v1/logs"},
tracesURL: "http://localhost:4318/v1/traces",
metricsURL: "http://localhost:4318/v1/metrics",
logsURL: "https://api.opentelemetry.com/v1/logs",
},
}

for _, tc := range tests {
if tc.config.TracesURL() != tc.tracesURL {
t.Fatalf("%s / TracesURL: expected %s, got: %s", tc.name, tc.tracesURL, tc.config.TracesURL())
}
if tc.config.MetricsURL() != tc.metricsURL {
t.Fatalf("%s / MetricsURL: expected %s, got: %s", tc.name, tc.metricsURL, tc.config.MetricsURL())
}
if tc.config.LogsURL() != tc.logsURL {
t.Fatalf("%s / LogsURL: expected %s, got: %s", tc.name, tc.logsURL, tc.config.LogsURL())
}
}
}

func TestOtelConfigBooleans(t *testing.T) {
type test struct {
name string
config OtelConfig
tracesEnabled bool
metricsEnabled bool
logsEnabled bool
}

tests := []test{
{
name: "Defaults",
config: OtelConfig{},
tracesEnabled: false,
metricsEnabled: false,
logsEnabled: false,
},
{
name: "Enable all with EnableObservability",
config: OtelConfig{EnableObservability: true},
tracesEnabled: true,
metricsEnabled: true,
logsEnabled: true,
},
{
name: "Enable just traces",
config: OtelConfig{EnableTraces: true},
tracesEnabled: true,
metricsEnabled: false,
logsEnabled: false,
},
{
name: "Enable just metrics",
config: OtelConfig{EnableMetrics: true},
tracesEnabled: false,
metricsEnabled: true,
logsEnabled: false,
},
{
name: "Enable just logs",
config: OtelConfig{EnableLogs: true},
tracesEnabled: false,
metricsEnabled: false,
logsEnabled: true,
},
}
for _, tc := range tests {
if tc.config.TracesEnabled() != tc.tracesEnabled {
t.Fatalf("%s / TracesEnabled: expected %t, got: %t", tc.name, tc.tracesEnabled, tc.config.TracesEnabled())
}
if tc.config.MetricsEnabled() != tc.metricsEnabled {
t.Fatalf("%s / MetricsEnabled: expected %t, got: %t", tc.name, tc.metricsEnabled, tc.config.MetricsEnabled())
}
if tc.config.LogsEnabled() != tc.logsEnabled {
t.Fatalf("%s / LogsEnabled: expected %t, got: %t", tc.name, tc.logsEnabled, tc.config.LogsEnabled())
}
}
}
34 changes: 9 additions & 25 deletions observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

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

endpoint := config.TracesEndpoint
if endpoint == "" {
endpoint = config.ObservabilityEndpoint
}

switch strings.ToLower(config.Protocol) {
switch config.OtelProtocol() {
case OtelProtocolGRPC:
exporter, err = otlptracegrpc.New(ctx, otlptracegrpc.WithEndpointURL(endpoint))
exporter, err = otlptracegrpc.New(ctx, otlptracegrpc.WithEndpointURL(config.TracesURL()))
case OtelProtocolHTTP:
exporter, err = otlptracehttp.New(ctx, otlptracehttp.WithEndpointURL(endpoint))
exporter, err = otlptracehttp.New(ctx, otlptracehttp.WithEndpointURL(config.TracesURL()))
default:
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
}
Expand Down Expand Up @@ -82,16 +76,11 @@ func newMeterProvider(ctx context.Context, config OtelConfig, serviceResource *r
var exporter metric.Exporter
var err error

endpoint := config.MetricsEndpoint
if endpoint == "" {
endpoint = config.ObservabilityEndpoint
}

switch strings.ToLower(config.Protocol) {
switch config.OtelProtocol() {
case OtelProtocolGRPC:
exporter, err = otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpointURL(endpoint))
exporter, err = otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithEndpointURL(config.MetricsURL()))
case OtelProtocolHTTP:
exporter, err = otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpointURL(endpoint))
exporter, err = otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpointURL(config.MetricsURL()))
default:
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
}
Expand All @@ -113,16 +102,11 @@ func newLoggerProvider(ctx context.Context, config OtelConfig, serviceResource *
var exporter log.Exporter
var err error

endpoint := config.LogsEndpoint
if endpoint == "" {
endpoint = config.ObservabilityEndpoint
}

switch strings.ToLower(config.Protocol) {
switch config.OtelProtocol() {
case OtelProtocolGRPC:
exporter, err = otlploggrpc.New(ctx, otlploggrpc.WithEndpointURL(endpoint))
exporter, err = otlploggrpc.New(ctx, otlploggrpc.WithEndpointURL(config.LogsURL()))
case OtelProtocolHTTP:
exporter, err = otlploghttp.New(ctx, otlploghttp.WithEndpointURL(endpoint))
exporter, err = otlploghttp.New(ctx, otlploghttp.WithEndpointURL(config.LogsURL()))
default:
return nil, fmt.Errorf("unknown observability protocol %q", config.Protocol)
}
Expand Down
6 changes: 3 additions & 3 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func NewWithHostDataSource(source io.Reader, options ...ProviderHandler) (*Wasmc
return nil, err
}

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

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

if hostData.OtelConfig.EnableObservability || hostData.OtelConfig.EnableLogs {
if hostData.OtelConfig.LogsEnabled() {
loggerProvider, err := newLoggerProvider(context.Background(), hostData.OtelConfig, serviceResource)
if err != nil {
return nil, err
Expand Down