From c912b179fd595cc7c6eec0b22e6c1371e31241d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Wed, 25 Aug 2021 18:48:48 +0300 Subject: [PATCH 1/4] Print JSON objects to stdout without a wrapping array (#2196) * Encode JSON objects to stdout one by one; not wrapped in lists * Add changelog entry --- CHANGELOG.md | 1 + exporters/stdout/stdouttrace/exporter.go | 38 ---- exporters/stdout/stdouttrace/trace.go | 58 +++--- exporters/stdout/stdouttrace/trace_test.go | 217 ++++++++++----------- 4 files changed, 144 insertions(+), 170 deletions(-) delete mode 100644 exporters/stdout/stdouttrace/exporter.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 05be353a44a..a8b5d5d0d71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Metric SDK/API implementation type `InstrumentKind` moves into `sdkapi` sub-package. (#2091) - The Metrics SDK export record no longer contains a Resource pointer, the SDK `"go.opentelemetry.io/otel/sdk/trace/export/metric".Exporter.Export()` function for push-based exporters now takes a single Resource argument, pull-based exporters use `"go.opentelemetry.io/otel/sdk/metric/controller/basic".Controller.Resource()`. (#2120) +- The JSON output of the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` is harmonized now such that the output is "plain" JSON objects after each other of the form `{ ... } { ... } { ... }`. Earlier the JSON objects describing a span were wrapped in a slice for each `Exporter.ExportSpans` call, like `[ { ... } ][ { ... } { ... } ]`. Outputting JSON object directly after each other is consistent with JSON loggers, and a bit easier to parse and read. (#2196) ### Deprecated diff --git a/exporters/stdout/stdouttrace/exporter.go b/exporters/stdout/stdouttrace/exporter.go deleted file mode 100644 index 02df19d2dca..00000000000 --- 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 657f1e7a2c5..ba391851d40 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 8ee7512b67b..1095cb5666c 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": "" } -] +} ` } From bcd7ff7ba28a14d7c84229727063e634b84f2183 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Aug 2021 07:27:40 -0700 Subject: [PATCH 2/4] Bump codecov/codecov-action from 2.0.2 to 2.0.3 (#2205) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2.0.2 to 2.0.3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v2.0.2...v2.0.3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f52d1392cb..1dfbaa3ec77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: cp coverage.txt $TEST_RESULTS cp coverage.html $TEST_RESULTS - name: Upload coverage report - uses: codecov/codecov-action@v2.0.2 + uses: codecov/codecov-action@v2.0.3 with: file: ./coverage.txt fail_ci_if_error: true From 3b05ba021c0c779283854a01ade4f547436b0b84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Aug 2021 14:59:58 -0700 Subject: [PATCH 3/4] Bump actions/setup-go from 2.1.3 to 2.1.4 (#2206) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.1.3 to 2.1.4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v2.1.3...v2.1.4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/dependabot.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dfbaa3ec77..ddb5e79b982 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v2.1.4 with: go-version: ${{ env.DEFAULT_GO_VERSION }} - name: Checkout Repo @@ -123,7 +123,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Install Go - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v2.1.4 with: go-version: ${{ matrix.go-version }} - name: Checkout code diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 8de285f94e2..727c6ec9bfe 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v2 with: ref: ${{ github.head_ref }} - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v2.1.4 with: go-version: '^1.15.0' - uses: evantorrie/mott-the-tidier@v1-beta From abf6afe00ebfa8f282959c74ecb88340bb3126af Mon Sep 17 00:00:00 2001 From: tomas-mota Date: Mon, 30 Aug 2021 17:09:46 +0200 Subject: [PATCH 4/4] Update otel example guide (#2210) --- example/otel-collector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/otel-collector/README.md b/example/otel-collector/README.md index 765071f1f02..5a1e937dd5b 100644 --- a/example/otel-collector/README.md +++ b/example/otel-collector/README.md @@ -132,7 +132,7 @@ need to create the Jaeger and Prometheus exporters: ```yml ... exporters: - jaeger_grpc: + jaeger: endpoint: "jaeger-collector.observability.svc.cluster.local:14250" prometheus: