diff --git a/exporters/stdout/stdouttrace/exporter.go b/exporters/stdout/stdouttrace/exporter.go deleted file mode 100644 index 02df19d2dca7..000000000000 --- a/exporters/stdout/stdouttrace/exporter.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" - -import ( - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -type Exporter struct { - traceExporter -} - -var ( - _ sdktrace.SpanExporter = &Exporter{} -) - -// New creates an Exporter with the passed options. -func New(options ...Option) (*Exporter, error) { - cfg, err := newConfig(options...) - if err != nil { - return nil, err - } - return &Exporter{ - traceExporter: traceExporter{config: cfg}, - }, nil -} diff --git a/exporters/stdout/stdouttrace/trace.go b/exporters/stdout/stdouttrace/trace.go index 657f1e7a2c55..ba391851d40e 100644 --- a/exporters/stdout/stdouttrace/trace.go +++ b/exporters/stdout/stdouttrace/trace.go @@ -17,7 +17,6 @@ package stdouttrace // import "go.opentelemetry.io/otel/exporters/stdout/stdoutt import ( "context" "encoding/json" - "fmt" "sync" "time" @@ -27,16 +26,37 @@ import ( var zeroTime time.Time +var _ trace.SpanExporter = &Exporter{} + +// New creates an Exporter with the passed options. +func New(options ...Option) (*Exporter, error) { + cfg, err := newConfig(options...) + if err != nil { + return nil, err + } + + enc := json.NewEncoder(cfg.Writer) + if cfg.PrettyPrint { + enc.SetIndent("", "\t") + } + + return &Exporter{ + encoder: enc, + timestamps: cfg.Timestamps, + }, nil +} + // Exporter is an implementation of trace.SpanSyncer that writes spans to stdout. -type traceExporter struct { - config config +type Exporter struct { + encoder *json.Encoder + timestamps bool stoppedMu sync.RWMutex stopped bool } // ExportSpans writes spans in json format to stdout. -func (e *traceExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { +func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { e.stoppedMu.RLock() stopped := e.stopped e.stoppedMu.RUnlock() @@ -47,10 +67,13 @@ func (e *traceExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlyS if len(spans) == 0 { return nil } + stubs := tracetest.SpanStubsFromReadOnlySpans(spans) - if !e.config.Timestamps { - for i := range stubs { - stub := &stubs[i] + + for i := range stubs { + stub := &stubs[i] + // Remove timestamps + if !e.timestamps { stub.StartTime = zeroTime stub.EndTime = zeroTime for j := range stub.Events { @@ -58,18 +81,17 @@ func (e *traceExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlyS ev.Time = zeroTime } } - } - out, err := e.marshal(stubs) - if err != nil { - return err + // Encode span stubs, one by one + if err := e.encoder.Encode(stub); err != nil { + return err + } } - _, err = fmt.Fprintln(e.config.Writer, string(out)) - return err + return nil } // Shutdown is called to stop the exporter, it preforms no action. -func (e *traceExporter) Shutdown(ctx context.Context) error { +func (e *Exporter) Shutdown(ctx context.Context) error { e.stoppedMu.Lock() e.stopped = true e.stoppedMu.Unlock() @@ -81,11 +103,3 @@ func (e *traceExporter) Shutdown(ctx context.Context) error { } return nil } - -// marshal v with approriate indentation. -func (e *traceExporter) marshal(v interface{}) ([]byte, error) { - if e.config.PrettyPrint { - return json.MarshalIndent(v, "", "\t") - } - return json.Marshal(v) -} diff --git a/exporters/stdout/stdouttrace/trace_test.go b/exporters/stdout/stdouttrace/trace_test.go index 8ee7512b67b4..1095cb5666c8 100644 --- a/exporters/stdout/stdouttrace/trace_test.go +++ b/exporters/stdout/stdouttrace/trace_test.go @@ -43,32 +43,30 @@ func TestExporter_ExportSpan(t *testing.T) { doubleValue := 123.456 resource := resource.NewSchemaless(attribute.String("rk1", "rv11")) - ro := tracetest.SpanStubs{ - { - SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: traceID, - SpanID: spanID, - TraceState: traceState, - }), - Name: "/foo", - StartTime: now, - EndTime: now, - Attributes: []attribute.KeyValue{ - attribute.String("key", keyValue), - attribute.Float64("double", doubleValue), - }, - Events: []tracesdk.Event{ - {Name: "foo", Attributes: []attribute.KeyValue{attribute.String("key", keyValue)}, Time: now}, - {Name: "bar", Attributes: []attribute.KeyValue{attribute.Float64("double", doubleValue)}, Time: now}, - }, - SpanKind: trace.SpanKindInternal, - Status: tracesdk.Status{ - Code: codes.Error, - Description: "interesting", - }, - Resource: resource, + ss := tracetest.SpanStub{ + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceState: traceState, + }), + Name: "/foo", + StartTime: now, + EndTime: now, + Attributes: []attribute.KeyValue{ + attribute.String("key", keyValue), + attribute.Float64("double", doubleValue), + }, + Events: []tracesdk.Event{ + {Name: "foo", Attributes: []attribute.KeyValue{attribute.String("key", keyValue)}, Time: now}, + {Name: "bar", Attributes: []attribute.KeyValue{attribute.Float64("double", doubleValue)}, Time: now}, + }, + SpanKind: trace.SpanKindInternal, + Status: tracesdk.Status{ + Code: codes.Error, + Description: "interesting", }, - }.Snapshots() + Resource: resource, + } tests := []struct { opts []stdouttrace.Option @@ -91,107 +89,106 @@ func TestExporter_ExportSpan(t *testing.T) { ex, err := stdouttrace.New(append(tt.opts, stdouttrace.WithWriter(&b))...) require.Nil(t, err) - err = ex.ExportSpans(ctx, ro) + err = ex.ExportSpans(ctx, tracetest.SpanStubs{ss, ss}.Snapshots()) require.Nil(t, err) got := b.String() - assert.Equal(t, expectedJSON(tt.expectNow), got) + wantone := expectedJSON(tt.expectNow) + assert.Equal(t, wantone+wantone, got) } } func expectedJSON(now time.Time) string { serializedNow, _ := json.Marshal(now) - return `[ - { - "Name": "/foo", - "SpanContext": { - "TraceID": "0102030405060708090a0b0c0d0e0f10", - "SpanID": "0102030405060708", - "TraceFlags": "00", - "TraceState": "key=val", - "Remote": false - }, - "Parent": { - "TraceID": "00000000000000000000000000000000", - "SpanID": "0000000000000000", - "TraceFlags": "00", - "TraceState": "", - "Remote": false + return `{ + "Name": "/foo", + "SpanContext": { + "TraceID": "0102030405060708090a0b0c0d0e0f10", + "SpanID": "0102030405060708", + "TraceFlags": "00", + "TraceState": "key=val", + "Remote": false + }, + "Parent": { + "TraceID": "00000000000000000000000000000000", + "SpanID": "0000000000000000", + "TraceFlags": "00", + "TraceState": "", + "Remote": false + }, + "SpanKind": 1, + "StartTime": ` + string(serializedNow) + `, + "EndTime": ` + string(serializedNow) + `, + "Attributes": [ + { + "Key": "key", + "Value": { + "Type": "STRING", + "Value": "value" + } }, - "SpanKind": 1, - "StartTime": ` + string(serializedNow) + `, - "EndTime": ` + string(serializedNow) + `, - "Attributes": [ - { - "Key": "key", - "Value": { - "Type": "STRING", - "Value": "value" - } - }, - { - "Key": "double", - "Value": { - "Type": "FLOAT64", - "Value": 123.456 - } + { + "Key": "double", + "Value": { + "Type": "FLOAT64", + "Value": 123.456 } - ], - "Events": [ - { - "Name": "foo", - "Attributes": [ - { - "Key": "key", - "Value": { - "Type": "STRING", - "Value": "value" - } - } - ], - "DroppedAttributeCount": 0, - "Time": ` + string(serializedNow) + ` - }, - { - "Name": "bar", - "Attributes": [ - { - "Key": "double", - "Value": { - "Type": "FLOAT64", - "Value": 123.456 - } + } + ], + "Events": [ + { + "Name": "foo", + "Attributes": [ + { + "Key": "key", + "Value": { + "Type": "STRING", + "Value": "value" } - ], - "DroppedAttributeCount": 0, - "Time": ` + string(serializedNow) + ` - } - ], - "Links": null, - "Status": { - "Code": "Error", - "Description": "interesting" + } + ], + "DroppedAttributeCount": 0, + "Time": ` + string(serializedNow) + ` }, - "DroppedAttributes": 0, - "DroppedEvents": 0, - "DroppedLinks": 0, - "ChildSpanCount": 0, - "Resource": [ - { - "Key": "rk1", - "Value": { - "Type": "STRING", - "Value": "rv11" + { + "Name": "bar", + "Attributes": [ + { + "Key": "double", + "Value": { + "Type": "FLOAT64", + "Value": 123.456 + } } + ], + "DroppedAttributeCount": 0, + "Time": ` + string(serializedNow) + ` + } + ], + "Links": null, + "Status": { + "Code": "Error", + "Description": "interesting" + }, + "DroppedAttributes": 0, + "DroppedEvents": 0, + "DroppedLinks": 0, + "ChildSpanCount": 0, + "Resource": [ + { + "Key": "rk1", + "Value": { + "Type": "STRING", + "Value": "rv11" } - ], - "InstrumentationLibrary": { - "Name": "", - "Version": "", - "SchemaURL": "" } + ], + "InstrumentationLibrary": { + "Name": "", + "Version": "", + "SchemaURL": "" } -] +} ` }