1
0
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:
Robert Pająk
2026-06-09 19:09:30 +02:00
committed by GitHub
parent 1802033e48
commit 3a1412d2b3
14 changed files with 161 additions and 24 deletions
+1
View File
@@ -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 -->
+2 -2
View File
@@ -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),
}
}
+24 -1
View File
@@ -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{
+1 -1
View File
@@ -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")
}
+1 -3
View File
@@ -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,
}
}
+19
View File
@@ -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
+3 -2
View File
@@ -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,
}
}
+53
View File
@@ -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{
+10 -2
View File
@@ -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),
}
}