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:
parent
7966e63342
commit
a853377a2f
59
example/README.md
Normal file
59
example/README.md
Normal 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)
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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=
|
||||
|
BIN
example/http/images/JaegarTraceExample.png
Normal file
BIN
example/http/images/JaegarTraceExample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 KiB |
@ -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...),
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user