diff --git a/logger.go b/logger.go index 1da87f4..28f9309 100644 --- a/logger.go +++ b/logger.go @@ -92,8 +92,12 @@ func newLoggerProvider( otelDisabled bool, res *resource.Resource, ) (*log.LoggerProvider, error) { - logsEndpoint := getDefault(config.OtlpLogsEndpoint, config.OtlpEndpoint) - if otelDisabled || config.LogsExporter != "otlp" || logsEndpoint == "" { + logsEndpoint := config.OtlpLogsEndpoint + if logsEndpoint == "" && config.OtlpEndpoint != "" { + logsEndpoint = config.OtlpEndpoint + "/v1/logs" + } + + if otelDisabled || config.LogsExporter != OTELLogsExporterOTLP || logsEndpoint == "" { return log.NewLoggerProvider(), nil } @@ -136,7 +140,7 @@ func newLoggerProvider( } options := []otlploghttp.Option{ - otlploghttp.WithEndpoint(endpoint), + otlploghttp.WithEndpointURL(endpoint), otlploghttp.WithCompression(otlploghttp.Compression(compressorInt)), } diff --git a/middleware.go b/middleware.go index 8169354..9afc13f 100644 --- a/middleware.go +++ b/middleware.go @@ -145,12 +145,15 @@ func (tm *tracingMiddleware) ServeHTTP( //nolint:gocognit,cyclop,funlen,maintidx httpLogger := logger.With(slog.String("type", "http-log")) isDebug := logger.Enabled(ctx, slog.LevelDebug) + if tm.Options.CustomAttributesFunc != nil { + metricAttrs = append(metricAttrs, tm.Options.CustomAttributesFunc(r)...) + } // Add HTTP semantic attributes to the server span // See: https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-semantic-conventions span.SetAttributes(metricAttrs...) - if !tm.Options.HighCardinalityMetricDisabled { - metricAttrs = append(metricAttrs, attribute.String("http.request.path", r.URL.Path)) + if tm.Options.HighCardinalityMetrics { + metricAttrs = append(metricAttrs, semconv.URLPath(r.URL.Path)) } activeRequestsAttrSet := metric.WithAttributeSet(attribute.NewSet(metricAttrs...)) @@ -343,35 +346,46 @@ func (tm *tracingMiddleware) ServeHTTP( //nolint:gocognit,cyclop,funlen,maintidx } type tracingMiddlewareOptions struct { - HighCardinalitySpanDisabled bool - HighCardinalityMetricDisabled bool - DebugPaths []string - AllowedRequestHeaders []string - AllowedResponseHeaders []string - ResponseWriterWrapperFunc NewWrapResponseWriterFunc + HighCardinalitySpans bool + HighCardinalityMetrics bool + DebugPaths []string + AllowedRequestHeaders []string + AllowedResponseHeaders []string + ResponseWriterWrapperFunc NewWrapResponseWriterFunc + CustomAttributesFunc CustomAttributesFunc } +// CustomAttributesFunc abstracts a hook function to add custom attributes. +type CustomAttributesFunc func(r *http.Request) []attribute.KeyValue + // TracingMiddlewareOption abstracts a function to apply options to the tracing middleware. type TracingMiddlewareOption func(*tracingMiddlewareOptions) -// DisableHighCardinalitySpans set the option to disable high cardinality spans. +// WithHighCardinalitySpans set the option to enable high cardinality spans. // The request path is removed from the span name. -func DisableHighCardinalitySpans(disabled bool) TracingMiddlewareOption { +func WithHighCardinalitySpans(enabled bool) TracingMiddlewareOption { + return func(tmo *tracingMiddlewareOptions) { + tmo.HighCardinalitySpans = enabled + } +} + +// WithHighCardinalityMetrics set the option to enable high cardinality request path labels. +func WithHighCardinalityMetrics(enabled bool) TracingMiddlewareOption { return func(tmo *tracingMiddlewareOptions) { - tmo.HighCardinalitySpanDisabled = disabled + tmo.HighCardinalityMetrics = enabled } } -// DisableHighCardinalityMetrics set the option to disable high cardinality http_path labels. -func DisableHighCardinalityMetrics(disabled bool) TracingMiddlewareOption { +// WithCustomAttributesFunc set the option to add custom OpenTelemetry attributes. +func WithCustomAttributesFunc(fn CustomAttributesFunc) TracingMiddlewareOption { return func(tmo *tracingMiddlewareOptions) { - tmo.HighCardinalityMetricDisabled = disabled + tmo.CustomAttributesFunc = fn } } -// DebugPaths return an option to add request paths to be printed logs in the debug level. +// WithDebugPaths return an option to add request paths to be printed logs in the debug level. // By default, metrics and health check endpoints are added to avoid noisy logs. -func DebugPaths(paths []string) TracingMiddlewareOption { +func WithDebugPaths(paths []string) TracingMiddlewareOption { return func(tmo *tracingMiddlewareOptions) { tmo.DebugPaths = append(tmo.DebugPaths, paths...) } @@ -401,10 +415,14 @@ func ResponseWriterWrapperFunc(wrapper NewWrapResponseWriterFunc) TracingMiddlew } func (opts *tracingMiddlewareOptions) getRequestSpanName(req *http.Request) string { - if opts.HighCardinalitySpanDisabled || req.URL.Path == "" { + if !opts.HighCardinalitySpans { return req.Method } + if req.URL.Path == "" { + return req.Method + " /" + } + if req.URL.Path[0] == '/' { return req.Method + " " + req.URL.Path } diff --git a/middleware_test.go b/middleware_test.go index 00c0f88..50e305d 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -13,6 +13,7 @@ import ( "testing" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) func TestTracingMiddleware(t *testing.T) { @@ -41,9 +42,12 @@ func TestTracingMiddleware(t *testing.T) { options := []TracingMiddlewareOption{ AllowRequestHeaders([]string{}), AllowResponseHeaders([]string{"Content-Type"}), - DebugPaths([]string{"/world"}), - DisableHighCardinalitySpans(false), - DisableHighCardinalityMetrics(true), + WithDebugPaths([]string{"/world"}), + WithHighCardinalitySpans(false), + WithHighCardinalityMetrics(true), + WithCustomAttributesFunc(func(r *http.Request) []attribute.KeyValue { + return []attribute.KeyValue{} + }), } mux.Handle("/hello", NewTracingMiddleware(exporters, options...)(handler)) diff --git a/provider.go b/provider.go index f7725f1..19cf0df 100644 --- a/provider.go +++ b/provider.go @@ -202,7 +202,10 @@ func setupOTelTraceProvider( resources *resource.Resource, otelDisabled bool, ) (*trace.TracerProvider, error) { - tracesEndpoint := getDefault(config.OtlpTracesEndpoint, config.OtlpEndpoint) + tracesEndpoint := config.OtlpTracesEndpoint + if tracesEndpoint == "" && config.OtlpEndpoint != "" { + tracesEndpoint = config.OtlpEndpoint + "/v1/traces" + } if otelDisabled || tracesEndpoint == "" { return trace.NewTracerProvider(trace.WithResource(resources)), nil @@ -252,7 +255,7 @@ func setupOTelTraceProvider( } options := []otlptracehttp.Option{ - otlptracehttp.WithEndpoint(endpoint), + otlptracehttp.WithEndpointURL(endpoint), otlptracehttp.WithCompression(otlptracehttp.Compression(compressorInt)), } @@ -326,7 +329,11 @@ func setupMetricExporterOTLP( config *OTLPConfig, metricOptions []metric.Option, ) ([]metric.Option, error) { - metricsEndpoint := getDefault(config.OtlpMetricsEndpoint, config.OtlpEndpoint) + metricsEndpoint := config.OtlpMetricsEndpoint + if metricsEndpoint == "" && config.OtlpEndpoint != "" { + metricsEndpoint = config.OtlpEndpoint + "/v1/metrics" + } + if metricsEndpoint == "" { return nil, errMetricsOTLPEndpointRequired } @@ -369,7 +376,7 @@ func setupMetricExporterOTLP( } options := []otlpmetrichttp.Option{ - otlpmetrichttp.WithEndpoint(endpoint), + otlpmetrichttp.WithEndpointURL(endpoint), otlpmetrichttp.WithCompression(otlpmetrichttp.Compression(compressorInt)), } if insecure { @@ -438,8 +445,10 @@ func parseOTLPEndpoint( } switch protocol { - case OTLPProtocolGRPC, OTLPProtocolHTTPProtobuf: + case OTLPProtocolGRPC: return host, protocol, insecure, nil + case OTLPProtocolHTTPProtobuf: + return endpoint, protocol, insecure, nil case "": // auto detect via default OTLP port if uri.Port() == otlpDefaultHTTPPort {