You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-19 21:45:50 +02:00
sdk/trace: avoid logging exporter endpoint configuration (#8438)
Prevent internal trace exporter logging from including endpoint configuration. `MarshalLog` now keeps the existing log shape where practical, but logs exporter/client type strings instead of recursively serializing exporter and client internals. OTLP trace HTTP/gRPC and Zipkin exporter log payloads no longer include endpoint URLs, embedded credentials, query tokens, internal hostnames, or insecure transport configuration. ## Changes - Stop recursive logging of trace exporter/client config from `sdk/trace` span processors and `otlptrace.Exporter`. - Remove endpoint/URL fields from OTLP trace HTTP/gRPC and Zipkin `MarshalLog` output. - Add regression coverage for credential-bearing endpoints not appearing in internal log output.
This commit is contained in:
@@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
### Fixed
|
||||
|
||||
- Interpret HTTP `Retry-After` header values as seconds instead of nanoseconds when retrying OTLP HTTP exports in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8383)
|
||||
- Stop including trace exporter endpoint configuration in internal logs from `go.opentelemetry.io/otel/sdk/trace`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, and `go.opentelemetry.io/otel/exporters/zipkin`. (#8438)
|
||||
- Fix `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` self-observability to record `error.type` on operation duration histogram when the `exportedSpans` metric is disabled. (#8432)
|
||||
|
||||
<!-- Released section -->
|
||||
|
||||
@@ -97,9 +97,9 @@ func NewUnstarted(client Client) *Exporter {
|
||||
func (e *Exporter) MarshalLog() any {
|
||||
return struct {
|
||||
Type string
|
||||
Client Client
|
||||
Client string
|
||||
}{
|
||||
Type: "otlptrace",
|
||||
Client: e.client,
|
||||
Client: fmt.Sprintf("%T", e.client),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
package otlptrace_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr/funcr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
|
||||
|
||||
@@ -16,7 +18,8 @@ import (
|
||||
)
|
||||
|
||||
type client struct {
|
||||
uploadErr error
|
||||
uploadErr error
|
||||
logEndpoint string
|
||||
}
|
||||
|
||||
var _ otlptrace.Client = &client{}
|
||||
@@ -33,6 +36,26 @@ func (c *client) UploadTraces(context.Context, []*tracepb.ResourceSpans) error {
|
||||
return c.uploadErr
|
||||
}
|
||||
|
||||
func (c *client) MarshalLog() any {
|
||||
return struct{ Endpoint string }{Endpoint: c.logEndpoint}
|
||||
}
|
||||
|
||||
func TestExporterMarshalLogDoesNotIncludeClientConfig(t *testing.T) {
|
||||
const sensitiveEndpoint = "user:pass@collector.internal:4318"
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := funcr.New(func(_, args string) {
|
||||
_, _ = buf.WriteString(args)
|
||||
}, funcr.Options{})
|
||||
|
||||
exp := otlptrace.NewUnstarted(&client{logEndpoint: sensitiveEndpoint})
|
||||
logger.Info("exporter", "config", exp)
|
||||
|
||||
logged := buf.String()
|
||||
assert.Contains(t, logged, "otlptrace")
|
||||
assert.NotContains(t, logged, sensitiveEndpoint)
|
||||
}
|
||||
|
||||
func TestExporterClientError(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
exp, err := otlptrace.New(ctx, &client{
|
||||
|
||||
@@ -3,6 +3,7 @@ module go.opentelemetry.io/otel/exporters/otlp/otlptrace
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
@@ -15,7 +16,6 @@ require (
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
||||
@@ -328,12 +328,10 @@ func throttleDelay(s *status.Status) (bool, time.Duration) {
|
||||
}
|
||||
|
||||
// MarshalLog is the marshaling function used by the logging system to represent this Client.
|
||||
func (c *client) MarshalLog() any {
|
||||
func (*client) MarshalLog() any {
|
||||
return struct {
|
||||
Type string
|
||||
Endpoint string
|
||||
Type string
|
||||
}{
|
||||
Type: "otlptracegrpc",
|
||||
Endpoint: c.endpoint,
|
||||
Type: "otlptracegrpc",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package otlptracegrpc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr/funcr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
@@ -48,6 +50,25 @@ func TestMain(m *testing.M) {
|
||||
|
||||
var roSpans = tracetest.SpanStubs{{Name: "Span 0"}}.Snapshots()
|
||||
|
||||
func TestClientMarshalLogDoesNotIncludeEndpointConfig(t *testing.T) {
|
||||
const sensitiveEndpoint = "user:pass@collector.internal:4317"
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := funcr.New(func(_, args string) {
|
||||
_, _ = buf.WriteString(args)
|
||||
}, funcr.Options{})
|
||||
|
||||
client := otlptracegrpc.NewClient(
|
||||
otlptracegrpc.WithEndpoint(sensitiveEndpoint),
|
||||
otlptracegrpc.WithInsecure(),
|
||||
)
|
||||
logger.Info("client", "config", client)
|
||||
|
||||
logged := buf.String()
|
||||
assert.Contains(t, logged, "otlptracegrpc")
|
||||
assert.NotContains(t, logged, sensitiveEndpoint)
|
||||
}
|
||||
|
||||
func contextWithTimeout(
|
||||
parent context.Context,
|
||||
t *testing.T,
|
||||
|
||||
@@ -4,6 +4,7 @@ go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v5 v5.0.3
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0
|
||||
@@ -21,7 +22,6 @@ require (
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||
|
||||
@@ -334,15 +334,11 @@ func (c *client) newRequest(body []byte) (request, error) {
|
||||
}
|
||||
|
||||
// MarshalLog is the marshaling function used by the logging system to represent this Client.
|
||||
func (c *client) MarshalLog() any {
|
||||
func (*client) MarshalLog() any {
|
||||
return struct {
|
||||
Type string
|
||||
Endpoint string
|
||||
Insecure bool
|
||||
Type string
|
||||
}{
|
||||
Type: "otlptracehttp",
|
||||
Endpoint: c.cfg.Endpoint,
|
||||
Insecure: c.cfg.Insecure,
|
||||
Type: "otlptracehttp",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
package otlptracehttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr/funcr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -16,3 +18,20 @@ func TestRetryAfterUsesSeconds(t *testing.T) {
|
||||
_, throttle := evaluate(err)
|
||||
assert.Equal(t, 10*time.Second, throttle)
|
||||
}
|
||||
|
||||
func TestClientMarshalLogDoesNotIncludeEndpointConfig(t *testing.T) {
|
||||
const sensitiveEndpoint = "user:pass@collector.internal:4318"
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := funcr.New(func(_, args string) {
|
||||
_, _ = buf.WriteString(args)
|
||||
}, funcr.Options{})
|
||||
|
||||
client := NewClient(WithEndpoint(sensitiveEndpoint), WithInsecure())
|
||||
logger.Info("client", "config", client)
|
||||
|
||||
logged := buf.String()
|
||||
assert.Contains(t, logged, "otlptracehttp")
|
||||
assert.NotContains(t, logged, sensitiveEndpoint)
|
||||
assert.NotContains(t, logged, "Insecure")
|
||||
}
|
||||
|
||||
@@ -201,12 +201,10 @@ func (e *Exporter) errf(format string, args ...any) error {
|
||||
}
|
||||
|
||||
// MarshalLog is the marshaling function used by the logging system to represent this Exporter.
|
||||
func (e *Exporter) MarshalLog() any {
|
||||
func (*Exporter) MarshalLog() any {
|
||||
return struct {
|
||||
Type string
|
||||
URL string
|
||||
}{
|
||||
Type: "zipkin",
|
||||
URL: e.url,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,25 @@ func TestNewRawExporterCollectorURLFromEnv(t *testing.T) {
|
||||
assert.Equal(t, expectedEndpoint, exp.url)
|
||||
}
|
||||
|
||||
func TestExporterMarshalLogDoesNotIncludeURL(t *testing.T) {
|
||||
const sensitiveURL = "http://user:pass@zipkin.internal:9411/api/v2/spans?token=secret"
|
||||
|
||||
exp, err := New(sensitiveURL)
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := funcr.New(func(_, args string) {
|
||||
_, _ = buf.WriteString(args)
|
||||
}, funcr.Options{})
|
||||
logger.Info("exporter", "config", exp)
|
||||
|
||||
logged := buf.String()
|
||||
assert.Contains(t, logged, "zipkin")
|
||||
assert.NotContains(t, logged, sensitiveURL)
|
||||
assert.NotContains(t, logged, "user:pass")
|
||||
assert.NotContains(t, logged, "token=secret")
|
||||
}
|
||||
|
||||
type mockZipkinCollector struct {
|
||||
t *testing.T
|
||||
url string
|
||||
|
||||
@@ -6,6 +6,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -435,11 +436,11 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan)
|
||||
func (bsp *batchSpanProcessor) MarshalLog() any {
|
||||
return struct {
|
||||
Type string
|
||||
SpanExporter SpanExporter
|
||||
SpanExporter string
|
||||
Config BatchSpanProcessorOptions
|
||||
}{
|
||||
Type: "BatchSpanProcessor",
|
||||
SpanExporter: bsp.e,
|
||||
SpanExporter: fmt.Sprintf("%T", bsp.e),
|
||||
Config: bsp.o,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr/funcr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
@@ -57,6 +60,56 @@ func (*shutdownSpanProcessor) ForceFlush(context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const sensitiveExporterEndpoint = "user:pass@collector.internal:4318"
|
||||
|
||||
type marshalingSpanExporter struct{}
|
||||
|
||||
func (*marshalingSpanExporter) ExportSpans(context.Context, []ReadOnlySpan) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*marshalingSpanExporter) Shutdown(context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*marshalingSpanExporter) MarshalLog() any {
|
||||
return struct{ Endpoint string }{Endpoint: sensitiveExporterEndpoint}
|
||||
}
|
||||
|
||||
func TestTracerProviderCreatedLogDoesNotIncludeExporterConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opt func(SpanExporter) TracerProviderOption
|
||||
}{
|
||||
{
|
||||
name: "batch",
|
||||
opt: func(e SpanExporter) TracerProviderOption { return WithBatcher(e) },
|
||||
},
|
||||
{
|
||||
name: "simple",
|
||||
opt: WithSyncer,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
orig := global.GetLogger()
|
||||
global.SetLogger(funcr.New(func(_, args string) {
|
||||
_, _ = buf.WriteString(args)
|
||||
}, funcr.Options{Verbosity: 4}))
|
||||
t.Cleanup(func() { global.SetLogger(orig) })
|
||||
|
||||
tp := NewTracerProvider(tt.opt(&marshalingSpanExporter{}))
|
||||
require.NoError(t, tp.Shutdown(t.Context()))
|
||||
|
||||
logged := buf.String()
|
||||
assert.Contains(t, logged, "TracerProvider created")
|
||||
assert.NotContains(t, logged, sensitiveExporterEndpoint)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownCallsTracerMethod(t *testing.T) {
|
||||
stp := NewTracerProvider()
|
||||
sp := &shutdownSpanProcessor{
|
||||
|
||||
@@ -5,6 +5,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -140,11 +141,18 @@ func (*simpleSpanProcessor) ForceFlush(context.Context) error {
|
||||
// MarshalLog is the marshaling function used by the logging system to represent
|
||||
// this Span Processor.
|
||||
func (ssp *simpleSpanProcessor) MarshalLog() any {
|
||||
// Shutdown clears exporter under exporterMu.
|
||||
// Copy it while holding the same lock so MarshalLog
|
||||
// can run concurrently without racing with Shutdown.
|
||||
ssp.exporterMu.Lock()
|
||||
exp := ssp.exporter
|
||||
ssp.exporterMu.Unlock()
|
||||
|
||||
return struct {
|
||||
Type string
|
||||
Exporter SpanExporter
|
||||
Exporter string
|
||||
}{
|
||||
Type: "SimpleSpanProcessor",
|
||||
Exporter: ssp.exporter,
|
||||
Exporter: fmt.Sprintf("%T", exp),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user