diff --git a/example/README.md b/example/README.md index a7538d015..b7e639b3a 100644 --- a/example/README.md +++ b/example/README.md @@ -28,32 +28,20 @@ Example uses GO111MODULE="" go get -d go.opentelemetry.io ``` -#### 2 Start All-in-one Jaeger - -``` -docker run -d --name jaeger \ - -p 16686:16686 \ - -p 14268:14268 \ - jaegertracing/all-in-one:1.8 -``` - -#### 3 Start Server +#### 2 Start Server ``` cd $GOPATH/src/go.opentelemetry.io/example/http/ go run ./server/server.go ``` -#### 4 Start Client +#### 3 Start Client ``` cd $GOPATH/src/go.opentelemetry.io/example/http/ go run ./client/client.go ``` -#### 5 Check traces on Jaeger UI +#### 4 Check traces in stdout -Visit http://localhost:16686 with a web browser -Click on 'Find' to see traces. - -[Sample Snapshot](http/images/JaegarTraceExample.png) +The spans should be visible in stdout in the order that they were exported. diff --git a/example/http/client/client.go b/example/http/client/client.go index 8b8a99176..325f7c3d8 100644 --- a/example/http/client/client.go +++ b/example/http/client/client.go @@ -28,7 +28,7 @@ import ( "go.opentelemetry.io/api/key" "go.opentelemetry.io/api/tag" "go.opentelemetry.io/api/trace" - "go.opentelemetry.io/exporter/trace/jaeger" + "go.opentelemetry.io/exporter/trace/stdout" "go.opentelemetry.io/plugin/httptrace" sdktrace "go.opentelemetry.io/sdk/trace" ) @@ -37,14 +37,9 @@ func initTracer() { // Register SDK as trace provider. sdktrace.Register() - // Create Jaeger exporter to be able to retrieve + // Create stdout exporter to be able to retrieve // the collected spans. - exporter, err := jaeger.NewExporter(jaeger.Options{ - CollectorEndpoint: "http://localhost:14268/api/traces", - Process: jaeger.Process{ - ServiceName: "trace-demo", - }, - }) + exporter, err := stdout.NewExporter(stdout.Options{PrettyPrint: true}) if err != nil { log.Fatal(err) } @@ -94,5 +89,5 @@ func main() { fmt.Printf("Response Received: %s\n\n\n", body) fmt.Printf("Waiting for few seconds to export spans ...\n\n") time.Sleep(10 * time.Second) - fmt.Printf("Check traces on http://localhost:16686\n") + fmt.Printf("Inspect traces on stdout") } diff --git a/example/http/go.sum b/example/http/go.sum index 41694af5f..1c4d833f4 100644 --- a/example/http/go.sum +++ b/example/http/go.sum @@ -84,6 +84,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54= github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= diff --git a/example/http/server/server.go b/example/http/server/server.go index 2d5e556b4..a9bb712aa 100644 --- a/example/http/server/server.go +++ b/example/http/server/server.go @@ -21,7 +21,7 @@ import ( "go.opentelemetry.io/api/tag" "go.opentelemetry.io/api/trace" - "go.opentelemetry.io/exporter/trace/jaeger" + "go.opentelemetry.io/exporter/trace/stdout" "go.opentelemetry.io/plugin/httptrace" sdktrace "go.opentelemetry.io/sdk/trace" ) @@ -31,12 +31,7 @@ func initTracer() { // Create Jaeger exporter to be able to retrieve // the collected spans. - exporter, err := jaeger.NewExporter(jaeger.Options{ - CollectorEndpoint: "http://localhost:14268/api/traces", - Process: jaeger.Process{ - ServiceName: "trace-demo", - }, - }) + exporter, err := stdout.NewExporter(stdout.Options{PrettyPrint: true}) if err != nil { log.Fatal(err) } diff --git a/exporter/trace/stdout/doc.go b/exporter/trace/stdout/doc.go new file mode 100644 index 000000000..042c21957 --- /dev/null +++ b/exporter/trace/stdout/doc.go @@ -0,0 +1,16 @@ +// Copyright 2019, 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 stdout contains an OpenTelemetry tracing exporter for writing to stdout. +package stdout // import "go.opentelemetry.io/exporter/trace/stdout" \ No newline at end of file diff --git a/exporter/trace/stdout/stdout.go b/exporter/trace/stdout/stdout.go new file mode 100644 index 000000000..45366d84d --- /dev/null +++ b/exporter/trace/stdout/stdout.go @@ -0,0 +1,61 @@ +// Copyright 2019, 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 stdout + +import ( + "encoding/json" + "io" + "os" + + "go.opentelemetry.io/sdk/trace" +) + +// Options are the options to be used when initializing a stdout exporter. +type Options struct { + // PrettyPrint will pretty the json representation of the span, + // making it print "pretty". Default is false. + PrettyPrint bool +} + +// Exporter is an implementation of trace.Exporter that writes spans to stdout. +type Exporter struct { + pretty bool + outputWriter io.Writer +} + +func NewExporter(o Options) (*Exporter, error) { + return &Exporter{ + pretty: o.PrettyPrint, + outputWriter: os.Stdout, + }, nil +} + +// ExportSpan writes a SpanData in json format to stdout. +func (e *Exporter) ExportSpan(data *trace.SpanData) { + var jsonSpan []byte + var err error + if e.pretty { + jsonSpan, err = json.MarshalIndent(data, "", "\t") + } else { + jsonSpan, err = json.Marshal(data) + } + if err != nil { + // ignore writer failures for now + _, _ = e.outputWriter.Write([]byte("Error converting spanData to json: " + err.Error())) + return + } + // ignore writer failures for now + _, _ = e.outputWriter.Write(append(jsonSpan, byte('\n'))) +} diff --git a/exporter/trace/stdout/stdout_test.go b/exporter/trace/stdout/stdout_test.go new file mode 100644 index 000000000..2f5667443 --- /dev/null +++ b/exporter/trace/stdout/stdout_test.go @@ -0,0 +1,101 @@ +// Copyright 2019, 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 stdout + +import ( + "bytes" + "encoding/json" + "testing" + "time" + + "google.golang.org/grpc/codes" + + "go.opentelemetry.io/api/core" + "go.opentelemetry.io/sdk/trace" +) + +func TestExporter_ExportSpan(t *testing.T) { + exporter, err := NewExporter(Options{}) + if err != nil { + t.Errorf("Error constructing stdout exporter %s", err) + } + + // override output writer for testing + var b bytes.Buffer + exporter.outputWriter = &b + + // setup test span + now := time.Now() + traceID := core.TraceID{High: 0x0102030405060708, Low: 0x090a0b0c0d0e0f10} + spanID := uint64(0x0102030405060708) + keyValue := "value" + doubleValue := float64(123.456) + + testSpan := &trace.SpanData{ + SpanContext: core.SpanContext{ + TraceID: traceID, + SpanID: spanID, + }, + Name: "/foo", + StartTime: now, + EndTime: now, + Attributes: []core.KeyValue{ + { + Key: core.Key{Name: "key"}, + Value: core.Value{Type: core.STRING, String: keyValue}, + }, + { + Key: core.Key{Name: "double"}, + Value: core.Value{Type: core.FLOAT64, Float64: doubleValue}, + }, + }, + Status: codes.Unknown, + } + exporter.ExportSpan(testSpan) + + expectedSerializedNow, _ := json.Marshal(now) + + got := b.String() + expectedOutput := `{"SpanContext":{` + + `"TraceID":{"High":72623859790382856,"Low":651345242494996240},` + + `"SpanID":72623859790382856,"TraceFlags":0},` + + `"ParentSpanID":0,` + + `"SpanKind":0,` + + `"Name":"/foo",` + + `"StartTime":` + string(expectedSerializedNow) + "," + + `"EndTime":` + string(expectedSerializedNow) + "," + + `"Attributes":[` + + `{` + + `"Key":{"Name":"key"},` + + `"Value":{"Type":8,"Bool":false,"Int64":0,"Uint64":0,"Float64":0,"String":"value","Bytes":null}` + + `},` + + `{` + + `"Key":{"Name":"double"},` + + `"Value":{"Type":7,"Bool":false,"Int64":0,"Uint64":0,"Float64":123.456,"String":"","Bytes":null}` + + `}` + + `],` + + `"MessageEvents":null,` + + `"Links":null,` + + `"Status":2,` + + `"HasRemoteParent":false,` + + `"DroppedAttributeCount":0,` + + `"DroppedMessageEventCount":0,` + + `"DroppedLinkCount":0,` + + `"ChildSpanCount":0}` + "\n" + + if got != expectedOutput { + t.Errorf("Want: %v but got: %v", expectedOutput, got) + } +}