1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-07-17 01:12:45 +02:00

Remove context check on stdout exporters (#5189)

This commit is contained in:
Prasad Shirodkar
2024-04-25 08:11:45 -07:00
committed by GitHub
parent 37619eada9
commit fd6d4db8c3
5 changed files with 69 additions and 52 deletions

View File

@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Changed ### Changed
- `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` won't print timestamps when `WithoutTimestamps` option is set. (#5241) - `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` won't print timestamps when `WithoutTimestamps` option is set. (#5241)
- The `Shutdown` method of `Exporter` in `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` ignores the context cancellation and always returns `nil`. (#5189)
- The `ForceFlush` and `Shutdown` methods of the exporter returned by `New` in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` ignore the context cancellation and always return `nil`. (#5189)
## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24 ## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24

View File

@ -51,12 +51,8 @@ func (e *exporter) Aggregation(k metric.InstrumentKind) metric.Aggregation {
} }
func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics) error { func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics) error {
select { if err := ctx.Err(); err != nil {
case <-ctx.Done(): return err
// Don't do anything if the context has already timed out.
return ctx.Err()
default:
// Context is still valid, continue.
} }
if e.redactTimestamps { if e.redactTimestamps {
redactTimestamps(data) redactTimestamps(data)
@ -67,18 +63,18 @@ func (e *exporter) Export(ctx context.Context, data *metricdata.ResourceMetrics)
return e.encVal.Load().(encoderHolder).Encode(data) return e.encVal.Load().(encoderHolder).Encode(data)
} }
func (e *exporter) ForceFlush(ctx context.Context) error { func (e *exporter) ForceFlush(context.Context) error {
// exporter holds no state, nothing to flush. // exporter holds no state, nothing to flush.
return ctx.Err() return nil
} }
func (e *exporter) Shutdown(ctx context.Context) error { func (e *exporter) Shutdown(context.Context) error {
e.shutdownOnce.Do(func() { e.shutdownOnce.Do(func() {
e.encVal.Store(encoderHolder{ e.encVal.Store(encoderHolder{
encoder: shutdownEncoder{}, encoder: shutdownEncoder{},
}) })
}) })
return ctx.Err() return nil
} }
func (e *exporter) MarshalLog() interface{} { func (e *exporter) MarshalLog() interface{} {

View File

@ -28,8 +28,7 @@ func testEncoderOption() stdoutmetric.Option {
func testCtxErrHonored(factory func(*testing.T) func(context.Context) error) func(t *testing.T) { func testCtxErrHonored(factory func(*testing.T) func(context.Context) error) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Helper() t.Helper()
ctx, cancel := context.WithCancel(context.Background()) ctx := context.Background()
t.Cleanup(cancel)
t.Run("DeadlineExceeded", func(t *testing.T) { t.Run("DeadlineExceeded", func(t *testing.T) {
innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond) innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond)
@ -55,19 +54,27 @@ func testCtxErrHonored(factory func(*testing.T) func(context.Context) error) fun
} }
} }
func TestExporterHonorsContextErrors(t *testing.T) { func testCtxErrIgnored(factory func(*testing.T) func(context.Context) error) func(t *testing.T) {
t.Run("Shutdown", testCtxErrHonored(func(t *testing.T) func(context.Context) error { return func(t *testing.T) {
exp, err := stdoutmetric.New(testEncoderOption()) t.Helper()
require.NoError(t, err) ctx := context.Background()
return exp.Shutdown
}))
t.Run("ForceFlush", testCtxErrHonored(func(t *testing.T) func(context.Context) error { t.Run("Canceled Ignored", func(t *testing.T) {
exp, err := stdoutmetric.New(testEncoderOption()) innerCtx, innerCancel := context.WithCancel(ctx)
require.NoError(t, err) innerCancel()
return exp.ForceFlush
}))
f := factory(t)
assert.NoError(t, f(innerCtx))
})
t.Run("NoError", func(t *testing.T) {
f := factory(t)
assert.NoError(t, f(ctx))
})
}
}
func TestExporterExportHonorsContextErrors(t *testing.T) {
t.Run("Export", testCtxErrHonored(func(t *testing.T) func(context.Context) error { t.Run("Export", testCtxErrHonored(func(t *testing.T) func(context.Context) error {
exp, err := stdoutmetric.New(testEncoderOption()) exp, err := stdoutmetric.New(testEncoderOption())
require.NoError(t, err) require.NoError(t, err)
@ -78,6 +85,22 @@ func TestExporterHonorsContextErrors(t *testing.T) {
})) }))
} }
func TestExporterForceFlushIgnoresContextErrors(t *testing.T) {
t.Run("ForceFlush", testCtxErrIgnored(func(t *testing.T) func(context.Context) error {
exp, err := stdoutmetric.New(testEncoderOption())
require.NoError(t, err)
return exp.ForceFlush
}))
}
func TestExporterShutdownIgnoresContextErrors(t *testing.T) {
t.Run("Shutdown", testCtxErrIgnored(func(t *testing.T) func(context.Context) error {
exp, err := stdoutmetric.New(testEncoderOption())
require.NoError(t, err)
return exp.Shutdown
}))
}
func TestShutdownExporterReturnsShutdownErrorOnExport(t *testing.T) { func TestShutdownExporterReturnsShutdownErrorOnExport(t *testing.T) {
var ( var (
data = new(metricdata.ResourceMetrics) data = new(metricdata.ResourceMetrics)

View File

@ -47,6 +47,9 @@ type Exporter struct {
// ExportSpans writes spans in json format to stdout. // ExportSpans writes spans in json format to stdout.
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
if err := ctx.Err(); err != nil {
return err
}
e.stoppedMu.RLock() e.stoppedMu.RLock()
stopped := e.stopped stopped := e.stopped
e.stoppedMu.RUnlock() e.stoppedMu.RUnlock()
@ -88,11 +91,6 @@ func (e *Exporter) Shutdown(ctx context.Context) error {
e.stopped = true e.stopped = true
e.stoppedMu.Unlock() e.stoppedMu.Unlock()
select {
case <-ctx.Done():
return ctx.Err()
default:
}
return nil return nil
} }

View File

@ -60,30 +60,44 @@ func TestExporterExportSpan(t *testing.T) {
tests := []struct { tests := []struct {
opts []stdouttrace.Option opts []stdouttrace.Option
expectNow time.Time expectNow time.Time
ctx context.Context
wantErr error
}{ }{
{ {
opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint()}, opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint()},
expectNow: now, expectNow: now,
ctx: context.Background(),
}, },
{ {
opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint(), stdouttrace.WithoutTimestamps()}, opts: []stdouttrace.Option{stdouttrace.WithPrettyPrint(), stdouttrace.WithoutTimestamps()},
// expectNow is an empty time.Time // expectNow is an empty time.Time
ctx: context.Background(),
},
{
opts: []stdouttrace.Option{},
ctx: func() context.Context {
ctx, cancel := context.WithCancel(context.Background())
cancel()
return ctx
}(),
wantErr: context.Canceled,
}, },
} }
ctx := context.Background()
for _, tt := range tests { for _, tt := range tests {
// write to buffer for testing // write to buffer for testing
var b bytes.Buffer var b bytes.Buffer
ex, err := stdouttrace.New(append(tt.opts, stdouttrace.WithWriter(&b))...) ex, err := stdouttrace.New(append(tt.opts, stdouttrace.WithWriter(&b))...)
require.Nil(t, err) require.Nil(t, err)
err = ex.ExportSpans(ctx, tracetest.SpanStubs{ss, ss}.Snapshots()) err = ex.ExportSpans(tt.ctx, tracetest.SpanStubs{ss, ss}.Snapshots())
require.Nil(t, err) assert.Equal(t, tt.wantErr, err)
got := b.String() if tt.wantErr == nil {
wantone := expectedJSON(tt.expectNow) got := b.String()
assert.Equal(t, wantone+wantone, got) wantone := expectedJSON(tt.expectNow)
assert.Equal(t, wantone+wantone, got)
}
} }
} }
@ -181,23 +195,7 @@ func expectedJSON(now time.Time) string {
` `
} }
func TestExporterShutdownHonorsTimeout(t *testing.T) { func TestExporterShutdownIgnoresContext(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
e, err := stdouttrace.New()
if err != nil {
t.Fatalf("failed to create exporter: %v", err)
}
innerCtx, innerCancel := context.WithTimeout(ctx, time.Nanosecond)
defer innerCancel()
<-innerCtx.Done()
err = e.Shutdown(innerCtx)
assert.ErrorIs(t, err, context.DeadlineExceeded)
}
func TestExporterShutdownHonorsCancel(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel() defer cancel()
@ -209,7 +207,7 @@ func TestExporterShutdownHonorsCancel(t *testing.T) {
innerCtx, innerCancel := context.WithCancel(ctx) innerCtx, innerCancel := context.WithCancel(ctx)
innerCancel() innerCancel()
err = e.Shutdown(innerCtx) err = e.Shutdown(innerCtx)
assert.ErrorIs(t, err, context.Canceled) assert.NoError(t, err)
} }
func TestExporterShutdownNoError(t *testing.T) { func TestExporterShutdownNoError(t *testing.T) {