1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2024-12-10 09:50:58 +02:00

Update example to use jaeger exporter and update Readme (#143)

* update http example to use jaeger exporter and sdk.

* update image location.

* remove extra parameters for jaeger.
This commit is contained in:
rghetia 2019-09-25 13:22:33 -07:00 committed by GitHub
parent 7966e63342
commit a853377a2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 224 additions and 51 deletions

59
example/README.md Normal file
View File

@ -0,0 +1,59 @@
# Example
## HTTP
This is a simple example that demonstrates tracing http request from client to server. The example
shows key aspects of tracing such as
- Root Span (on Client)
- Child Span (on Client)
- Child Span from a Remote Parent (on Server)
- SpanContext Propagation (from Client to Server)
- Span Events
- Span Attributes
Example uses
- open-telemetry SDK as trace instrumentation provider,
- httptrace plugin to facilitate tracing http request on client and server
- http trace_context propagation to propagate SpanContext on the wire.
- jaeger exporter to export spans to visualize and store them.
### How to run?
#### Prequisites
- go 1.12 installed
- GOPATH is configured.
#### 1 Download git repo
```
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
```
cd $GOPATH/src/go.opentelemetry.io/example/http/
go run ./server/server.go
```
#### 4 Start Client
```
cd $GOPATH/src/go.opentelemetry.io/example/http/
go run ./client/client.go
```
#### 5 Check traces on Jaeger UI
Visit http://localhost:16686 with a web browser
Click on 'Find' to see traces.
[Sample Snapshot](http/images/JaegarTraceExample.png)

View File

@ -18,27 +18,49 @@ import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"google.golang.org/grpc/codes"
"go.opentelemetry.io/api/key"
"go.opentelemetry.io/api/tag"
"go.opentelemetry.io/api/trace"
"go.opentelemetry.io/exporter/trace/jaeger"
"go.opentelemetry.io/plugin/httptrace"
sdktrace "go.opentelemetry.io/sdk/trace"
)
var (
tracer = trace.GlobalTracer().
WithService("client").
WithComponent("main").
WithResources(
key.New("whatevs").String("yesss"),
)
)
func initTracer() {
// Register SDK as trace provider.
sdktrace.Register()
// 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",
},
})
if err != nil {
log.Fatal(err)
}
// Wrap Jaeger exporter with SimpleSpanProcessor and register the processor.
ssp := sdktrace.NewSimpleSpanProcessor(exporter)
sdktrace.RegisterSpanProcessor(ssp)
// For the demonstration, use sdktrace.AlwaysSample sampler to sample all traces.
// In a production application, use sdktrace.ProbabilitySampler with a desired probability.
sdktrace.ApplyConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})
}
func main() {
fmt.Printf("Tracer %v\n", tracer)
initTracer()
client := http.DefaultClient
ctx := tag.NewContext(context.Background(),
tag.Insert(key.New("username").String("donuts")),
@ -46,13 +68,14 @@ func main() {
var body []byte
err := tracer.WithSpan(ctx, "say hello",
err := trace.GlobalTracer().WithSpan(ctx, "say hello",
func(ctx context.Context) error {
req, _ := http.NewRequest("GET", "http://localhost:7777/hello", nil)
ctx, req = httptrace.W3C(ctx, req)
httptrace.Inject(ctx, req)
fmt.Printf("Sending request...\n")
res, err := client.Do(req)
if err != nil {
panic(err)
@ -68,5 +91,8 @@ func main() {
panic(err)
}
fmt.Printf("%s", body)
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")
}

View File

@ -4,6 +4,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -64,6 +65,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@ -188,6 +190,7 @@ golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -16,24 +16,43 @@ package main
import (
"io"
"log"
"net/http"
"go.opentelemetry.io/api/key"
"go.opentelemetry.io/api/tag"
"go.opentelemetry.io/api/trace"
"go.opentelemetry.io/exporter/trace/jaeger"
"go.opentelemetry.io/plugin/httptrace"
sdktrace "go.opentelemetry.io/sdk/trace"
)
var (
tracer = trace.GlobalTracer().
WithService("server").
WithComponent("main").
WithResources(
key.New("whatevs").String("nooooo"),
)
)
func initTracer() {
sdktrace.Register()
// 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",
},
})
if err != nil {
log.Fatal(err)
}
// Wrap Jaeger exporter with SimpleSpanProcessor and register the processor.
ssp := sdktrace.NewSimpleSpanProcessor(exporter)
sdktrace.RegisterSpanProcessor(ssp)
// For the demonstration, use sdktrace.AlwaysSample sampler to sample all traces.
// In a production application, use sdktrace.ProbabilitySampler with a desired probability.
sdktrace.ApplyConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()})
}
func main() {
initTracer()
helloHandler := func(w http.ResponseWriter, req *http.Request) {
attrs, tags, spanCtx := httptrace.Extract(req.Context(), req)
@ -41,7 +60,7 @@ func main() {
MultiKV: tags,
})))
ctx, span := tracer.Start(
ctx, span := trace.GlobalTracer().Start(
req.Context(),
"hello",
trace.WithAttributes(attrs...),

View File

@ -22,13 +22,12 @@ import (
"io/ioutil"
"log"
"net/http"
"time"
"google.golang.org/grpc/codes"
"github.com/apache/thrift/lib/go/thrift"
"google.golang.org/api/support/bundler"
"google.golang.org/grpc/codes"
"go.opentelemetry.io/api/core"
gen "go.opentelemetry.io/exporter/trace/jaeger/internal/gen-go/jaeger"
"go.opentelemetry.io/sdk/trace"
)
@ -165,38 +164,33 @@ func (e *Exporter) ExportSpan(data *trace.SpanData) {
func spanDataToThrift(data *trace.SpanData) *gen.Span {
tags := make([]*gen.Tag, 0, len(data.Attributes))
for k, v := range data.Attributes {
tag := attributeToTag(k, v)
if tag != nil {
tags = append(tags, tag)
}
for _, kv := range data.Attributes {
tag := coreAttributeToTag(kv)
tags = append(tags, tag)
}
tags = append(tags,
attributeToTag("status.code", int32(data.Status)),
attributeToTag("status.message", data.Status.String()),
tags = append(tags, getInt64Tag("status.code", int64(data.Status)),
getStringTag("status.message", data.Status.String()),
)
// Ensure that if Status.Code is not OK, that we set the "error" tag on the Jaeger span.
// See Issue https://github.com/census-instrumentation/opencensus-go/issues/1041
if data.Status != codes.OK {
tags = append(tags, attributeToTag("error", true))
tags = append(tags, getBoolTag("error", true))
}
var logs []*gen.Log
for _, a := range data.MessageEvents {
fields := make([]*gen.Tag, 0, len(a.Attributes))
for _, kv := range a.Attributes {
tag := attributeToTag(kv.Key.Name, kv.Value.Emit())
tag := coreAttributeToTag(kv)
if tag != nil {
fields = append(fields, tag)
}
}
fields = append(fields, attributeToTag("message", a.Message))
fields = append(fields, getStringTag("message", a.Message))
logs = append(logs, &gen.Log{
//Timestamp: a.Time.UnixNano() / 1000,
//TODO: [rghetia] update when time is supported in the event.
Timestamp: time.Now().UnixNano() / 1000,
Timestamp: a.Time.UnixNano() / 1000,
Fields: fields,
})
}
@ -227,6 +221,61 @@ func spanDataToThrift(data *trace.SpanData) *gen.Span {
}
}
func coreAttributeToTag(kv core.KeyValue) *gen.Tag {
var tag *gen.Tag
switch kv.Value.Type {
case core.STRING:
tag = &gen.Tag{
Key: kv.Key.Name,
VStr: &kv.Value.String,
VType: gen.TagType_STRING,
}
case core.BOOL:
tag = &gen.Tag{
Key: kv.Key.Name,
VBool: &kv.Value.Bool,
VType: gen.TagType_BOOL,
}
case core.INT32, core.INT64:
tag = &gen.Tag{
Key: kv.Key.Name,
VLong: &kv.Value.Int64,
VType: gen.TagType_LONG,
}
case core.FLOAT32, core.FLOAT64:
tag = &gen.Tag{
Key: kv.Key.Name,
VDouble: &kv.Value.Float64,
VType: gen.TagType_DOUBLE,
}
}
return tag
}
func getInt64Tag(k string, i int64) *gen.Tag {
return &gen.Tag{
Key: k,
VLong: &i,
VType: gen.TagType_LONG,
}
}
func getStringTag(k, s string) *gen.Tag {
return &gen.Tag{
Key: k,
VStr: &s,
VType: gen.TagType_STRING,
}
}
func getBoolTag(k string, b bool) *gen.Tag {
return &gen.Tag{
Key: k,
VBool: &b,
VType: gen.TagType_BOOL,
}
}
// TODO(rghetia): remove interface{}. see https://github.com/open-telemetry/opentelemetry-go/pull/112/files#r321444786
func attributeToTag(key string, a interface{}) *gen.Tag {
var tag *gen.Tag

View File

@ -55,9 +55,15 @@ func Test_spanDataToThrift(t *testing.T) {
Name: "/foo",
StartTime: now,
EndTime: now,
Attributes: map[string]interface{}{
"double": doubleValue,
"key": keyValue,
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},
},
},
// TODO: [rghetia] add events test after event is concrete type.
Status: codes.Unknown,

View File

@ -86,7 +86,7 @@ type SpanData struct {
// from StartTime by the duration of the span.
EndTime time.Time
// The values of Attributes each have type string, bool, or int64.
Attributes map[string]interface{}
Attributes []core.KeyValue
MessageEvents []Event
Links []apitrace.Link
Status codes.Code

View File

@ -272,13 +272,14 @@ func (s *span) interfaceArrayToMessageEventArray() []Event {
return messageEventArr
}
func (s *span) lruAttributesToAttributeMap() map[string]interface{} {
attributes := make(map[string]interface{})
func (s *span) lruAttributesToAttributeMap() []core.KeyValue {
attributes := make([]core.KeyValue, 0, s.lruAttributes.simpleLruMap.Len())
for _, key := range s.lruAttributes.simpleLruMap.Keys() {
value, ok := s.lruAttributes.simpleLruMap.Get(key)
if ok {
key := key.(core.Key)
attributes[key.Name] = value
value := value.(core.Value)
attributes = append(attributes, core.KeyValue{Key: key, Value: value})
}
}
return attributes

View File

@ -186,9 +186,12 @@ func TestSetSpanAttributes(t *testing.T) {
TraceID: tid,
TraceOptions: 0x1,
},
ParentSpanID: sid,
Name: "span0",
Attributes: map[string]interface{}{"key1": core.Value{Type: core.STRING, String: "value1"}},
ParentSpanID: sid,
Name: "span0",
Attributes: []core.KeyValue{{
Key: core.Key{Name: "key1"},
Value: core.Value{Type: core.STRING, String: "value1"},
}},
HasRemoteParent: true,
}
if diff := cmp.Diff(got, want); diff != "" {
@ -217,9 +220,16 @@ func TestSetSpanAttributesOverLimit(t *testing.T) {
},
ParentSpanID: sid,
Name: "span0",
Attributes: map[string]interface{}{
"key1": core.Value{Type: core.STRING, String: "value3"},
"key4": core.Value{Type: core.STRING, String: "value4"}},
Attributes: []core.KeyValue{
{
Key: core.Key{Name: "key1"},
Value: core.Value{Type: core.STRING, String: "value3"},
},
{
Key: core.Key{Name: "key4"},
Value: core.Value{Type: core.STRING, String: "value4"},
},
},
HasRemoteParent: true,
DroppedAttributeCount: 1,
}