From 57d23ee0efe7fd31226c106aabe8120e93db8603 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Thu, 3 Aug 2023 21:46:01 -0600 Subject: [PATCH 1/9] feat(go): Transition to otel protos and enable Honeycomb --- .gitignore | 1 + go/.gitignore | 1 + go/adapter/honeycomb/adapter.go | 134 +++++++++++++++++++++++++++ go/adapter/otel_formatter/format.go | 138 ++++++++++++---------------- go/adapter/otel_stdout/adapter.go | 21 ++--- go/bin/honeycomb/main.go | 76 +++++++++++++++ go/go.mod | 5 + go/go.sum | 19 +++- scripts/generate-otel-ts.sh | 0 9 files changed, 302 insertions(+), 93 deletions(-) create mode 100644 go/adapter/honeycomb/adapter.go create mode 100644 go/bin/honeycomb/main.go mode change 100644 => 100755 scripts/generate-otel-ts.sh diff --git a/.gitignore b/.gitignore index f36be82..00940c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ rust/target rust/Cargo.lock target +.env \ No newline at end of file diff --git a/go/.gitignore b/go/.gitignore index eb5a316..796603f 100644 --- a/go/.gitignore +++ b/go/.gitignore @@ -1 +1,2 @@ target +.env \ No newline at end of file diff --git a/go/adapter/honeycomb/adapter.go b/go/adapter/honeycomb/adapter.go new file mode 100644 index 0000000..e265fc7 --- /dev/null +++ b/go/adapter/honeycomb/adapter.go @@ -0,0 +1,134 @@ +package honeycomb + +import ( + "bytes" + "fmt" + "io" + "log" + "net/http" + "net/url" + + observe "github.com/dylibso/observe-sdk/go" + otel "github.com/dylibso/observe-sdk/go/adapter/otel_formatter" + trace "go.opentelemetry.io/proto/otlp/trace/v1" + proto "google.golang.org/protobuf/proto" +) + +type HoneycombConfig struct { + ApiKey string + Dataset string + EmitTracesInterval uint32 + TraceBatchMax uint32 + Host string +} + +type HoneycombAdapter struct { + *observe.AdapterBase + Config *HoneycombConfig +} + +func NewHoneycombAdapter(config *HoneycombConfig) *HoneycombAdapter { + base := observe.NewAdapterBase(1, 0) + adapter := &HoneycombAdapter{ + AdapterBase: &base, + Config: config, + } + + adapter.AdapterBase.SetFlusher(adapter) + + return adapter +} + +func (h *HoneycombAdapter) Start() { + h.AdapterBase.Start(h) +} + +func (h *HoneycombAdapter) HandleTraceEvent(te observe.TraceEvent) { + h.AdapterBase.HandleTraceEvent(te) +} + +func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { + for _, te := range evts { + traceId := te.TelemetryId.ToHex16() + + var allSpans []*trace.Span + for _, e := range te.Events { + switch event := e.(type) { + case observe.CallEvent: // TODO: consider renaming to FunctionCall for consistency across Rust & JS + spans := h.makeCallSpans(event, nil, traceId) + if len(spans) > 0 { + allSpans = append(allSpans, spans...) + } + case observe.MemoryGrowEvent: + log.Println("MemoryGrowEvent should be attached to a span") + case observe.CustomEvent: + log.Println("Otel adapter does not respect custom events") + } + } + + if len(allSpans) <= 1 { + log.Println("No spans built for honeycomb") + return nil + } + + t := otel.NewTrace(traceId, h.Config.Dataset, allSpans) + data, err := proto.Marshal(t.TracesData) + if err != nil { + log.Println("failed to marshal TracesData:", err) + return nil + } + + url, err := url.JoinPath(h.Config.Host, "v1", "traces") + if err != nil { + log.Println("failed to create honeycomb endpoint url:", err) + return nil + } + + client := http.Client{} + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + log.Println("failed to create honeycomb endpoint url:", err) + } + + req.Header = http.Header{ + "content-type": {"application/protobuf"}, + "x-honeycomb-team": {h.Config.ApiKey}, + } + + resp, err := client.Do(req) + if err != nil { + log.Println("failed to create http client", err) + } + + if resp.StatusCode != http.StatusOK { + log.Println("unexpected status code from honeycomb:", resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Println("error reading response body:", err) + } + resp.Body.Close() + log.Println(string(body)) + } + } + + return nil +} + +func (h *HoneycombAdapter) makeCallSpans(event observe.CallEvent, parentId []byte, traceId string) []*trace.Span { + name := event.FunctionName() + span := otel.NewSpan(traceId, parentId, name, event.Time, event.Time.Add(event.Duration)) + span.Attributes = append(span.Attributes, otel.NewKeyValueString("function-name", fmt.Sprintf("function-call-%s", name))) + + spans := []*trace.Span{span} + for _, ev := range event.Within() { + if call, ok := ev.(observe.CallEvent); ok { + spans = append(spans, h.makeCallSpans(call, span.SpanId, traceId)...) + } + if alloc, ok := ev.(observe.MemoryGrowEvent); ok { + last := spans[len(spans)-1] + last.Attributes = append(last.Attributes, otel.NewKeyValueInt64("allocation", int64(alloc.MemoryGrowAmount()))) + } + } + return spans +} diff --git a/go/adapter/otel_formatter/format.go b/go/adapter/otel_formatter/format.go index 3a09371..f9247cb 100644 --- a/go/adapter/otel_formatter/format.go +++ b/go/adapter/otel_formatter/format.go @@ -3,96 +3,76 @@ package otel_formatter import ( "time" - "github.com/dylibso/observe-sdk/go" -) - -type OtelFormatter struct { - ResourceSpans []ResourceSpan `json:"resourceSpans"` -} - -func New() *OtelFormatter { - return &OtelFormatter{} -} - -func (o *OtelFormatter) AddResourceSpan(span ResourceSpan) { - o.ResourceSpans = append(o.ResourceSpans, span) -} - -type ResourceSpan struct { - Resource Resource `json:"resource"` - ScopeSpans []ScopeSpan `json:"scopeSpans"` -} + common "go.opentelemetry.io/proto/otlp/common/v1" + resource "go.opentelemetry.io/proto/otlp/resource/v1" + trace "go.opentelemetry.io/proto/otlp/trace/v1" -func NewResourceSpan() *ResourceSpan { - return &ResourceSpan{} -} + observe "github.com/dylibso/observe-sdk/go" +) -func (r *ResourceSpan) AddAttribute(key string, value any) *ResourceSpan { - r.Resource.Attributes = append(r.Resource.Attributes, Attribute{Key: key, Value: value}) - return r +type Trace struct { + TraceId string + TracesData *trace.TracesData } -func (r *ResourceSpan) AddSpans(spans []*Span) { - r.ScopeSpans = append(r.ScopeSpans, ScopeSpan{ - Scope: Scope{ - Name: "event", +func NewTrace(traceId string, serviceName string, spans []*trace.Span) *Trace { + return &Trace{ + TraceId: traceId, + TracesData: &trace.TracesData{ + ResourceSpans: []*trace.ResourceSpans{ + { + Resource: &resource.Resource{ + Attributes: []*common.KeyValue{ + NewKeyValueString("service.name", serviceName), + }, + }, + ScopeSpans: []*trace.ScopeSpans{ + { + Spans: spans, + }, + }, + }, + }, }, - Spans: spans, - }) -} - -type Resource struct { - Attributes []Attribute `json:"attributes"` -} - -type ScopeSpan struct { - Scope Scope `json:"scope"` - Spans []*Span `json:"spans"` -} - -type Attribute struct { - Key string `json:"key"` - Value any `json:"value"` -} - -type Scope struct { - Name string `json:"name"` -} - -type Span struct { - TraceId string `json:"traceId"` - SpanId string `json:"spanId"` - ParentSpanId string `json:"parentSpanId"` - Name string `json:"name"` - Kind int64 `json:"kind"` - StartTimeNano int64 `json:"startTimeUnixNano"` - EndTimeNano int64 `json:"endTimeUnixNano"` - Attributes []Attribute `json:"attributes"` - DroppedAttributesCount int64 `json:"droppedAttributesCount"` - DroppedEventsCount int64 `json:"droppedEventsCount"` - DroppedLinksCount int64 `json:"droppedLinksCount"` - Status Status `json:"status"` + } } -type Status struct{} - -func NewSpan(traceId string, parentId *string, name string, start, end time.Time) *Span { +func NewSpan(traceId string, parentId []byte, name string, start, end time.Time) *trace.Span { if parentId == nil { - var empty string - parentId = &empty + parentId = []byte{} } - return &Span{ - TraceId: traceId, - SpanId: observe.NewSpanId().ToHex8(), - ParentSpanId: *parentId, - Name: name, - Kind: 1, - StartTimeNano: start.UnixNano(), - EndTimeNano: end.UnixNano(), + return &trace.Span{ + TraceId: []byte(traceId), + SpanId: []byte(observe.NewSpanId().ToHex8()), + ParentSpanId: parentId, + Name: name, + Kind: 1, + StartTimeUnixNano: uint64(start.UnixNano()), + EndTimeUnixNano: uint64(end.UnixNano()), // uses empty defaults for remaining fields... } } -func (s *Span) AddAttribute(key string, value any) { - s.Attributes = append(s.Attributes, Attribute{Key: key, Value: value}) +func NewKeyValueString(key string, value string) *common.KeyValue { + strVal := &common.AnyValue_StringValue{ + StringValue: value, + } + return &common.KeyValue{ + Key: key, + Value: &common.AnyValue{ + Value: strVal, + }, + } +} + +func NewKeyValueInt64(key string, value int64) *common.KeyValue { + strVal := &common.AnyValue_IntValue{ + IntValue: value, + } + return &common.KeyValue{ + Key: key, + Value: &common.AnyValue{ + Value: strVal, + }, + } } diff --git a/go/adapter/otel_stdout/adapter.go b/go/adapter/otel_stdout/adapter.go index d2aa972..9f0da3a 100644 --- a/go/adapter/otel_stdout/adapter.go +++ b/go/adapter/otel_stdout/adapter.go @@ -6,8 +6,8 @@ import ( "log" observe "github.com/dylibso/observe-sdk/go" - "github.com/dylibso/observe-sdk/go/adapter/otel_formatter" otel "github.com/dylibso/observe-sdk/go/adapter/otel_formatter" + trace "go.opentelemetry.io/proto/otlp/trace/v1" ) type OtelStdoutAdapter struct { @@ -33,7 +33,7 @@ func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { for _, te := range evts { traceId := te.TelemetryId.ToHex16() - var allSpans []*otel_formatter.Span + var allSpans []*trace.Span for _, e := range te.Events { switch event := e.(type) { case observe.CallEvent: @@ -53,11 +53,8 @@ func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { return nil } - output := otel.New() - resourceSpan := otel.NewResourceSpan() - resourceSpan.AddSpans(allSpans) - output.AddResourceSpan(*resourceSpan) - b, err := json.Marshal(output) + t := otel.NewTrace(traceId, "golang", allSpans) + b, err := json.Marshal(t.TracesData) if err != nil { log.Println("failed to encode CallEvent spans") return nil @@ -69,19 +66,19 @@ func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { return nil } -func (o *OtelStdoutAdapter) makeCallSpans(event observe.CallEvent, parentId *string, traceId string) []*otel.Span { +func (o *OtelStdoutAdapter) makeCallSpans(event observe.CallEvent, parentId []byte, traceId string) []*trace.Span { name := event.FunctionName() span := otel.NewSpan(traceId, parentId, name, event.Time, event.Time.Add(event.Duration)) - span.AddAttribute("function_name", fmt.Sprintf("function-call-%s", name)) + span.Attributes = append(span.Attributes, otel.NewKeyValueString("function_name", fmt.Sprintf("function-call-%s", name))) - spans := []*otel.Span{span} + spans := []*trace.Span{span} for _, ev := range event.Within() { if call, ok := ev.(observe.CallEvent); ok { - spans = append(spans, o.makeCallSpans(call, &span.SpanId, traceId)...) + spans = append(spans, o.makeCallSpans(call, span.SpanId, traceId)...) } if alloc, ok := ev.(observe.MemoryGrowEvent); ok { last := spans[len(spans)-1] - last.AddAttribute("allocation", alloc.MemoryGrowAmount()) + last.Attributes = append(last.Attributes, otel.NewKeyValueInt64("allocation", int64(alloc.MemoryGrowAmount()))) } } diff --git a/go/bin/honeycomb/main.go b/go/bin/honeycomb/main.go new file mode 100644 index 0000000..5287685 --- /dev/null +++ b/go/bin/honeycomb/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/dylibso/observe-sdk/go/adapter/datadog" + "github.com/dylibso/observe-sdk/go/adapter/honeycomb" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +func main() { + ctx := context.Background() + + // we only need to create and start once per instance of our host app + conf := &honeycomb.HoneycombConfig{ + ApiKey: os.Getenv("HONEYCOMB_API_KEY"), + Dataset: "golang", + EmitTracesInterval: 1000, + TraceBatchMax: 100, + Host: "https://api.honeycomb.io", + } + adapter := honeycomb.NewHoneycombAdapter(conf) + defer adapter.Stop(true) + adapter.Start() + + // Load WASM from disk + wasm, err := os.ReadFile(os.Args[1]) + if err != nil { + log.Panicln(err) + } + + cfg := wazero.NewRuntimeConfig().WithCustomSections(true) + rt := wazero.NewRuntimeWithConfig(ctx, cfg) + traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) + if err != nil { + log.Panicln(err) + } + wasi_snapshot_preview1.MustInstantiate(ctx, rt) + + config := wazero.NewModuleConfig(). + WithStdin(os.Stdin). + WithStdout(os.Stdout). + WithStderr(os.Stderr). + WithArgs(os.Args[1:]...). + WithStartFunctions("_start") + m, err := rt.InstantiateWithConfig(ctx, wasm, config) + if err != nil { + log.Panicln(err) + } + defer m.Close(ctx) + + // normally this metadata would be in your web-server framework + // or derived when you need them. we're just gonna initialize + // some example values here + resourceName := "my-resource" + httpUrl := "https://example.com/my-endpoint" + httpStatusCode := 200 + spanKind := datadog.Server + clientIp := "66.210.227.34" + + meta := datadog.DatadogMetadata{ + ResourceName: &resourceName, + HttpUrl: &httpUrl, + HttpStatusCode: &httpStatusCode, + SpanKind: &spanKind, + HttpClientIp: &clientIp, + } + traceCtx.Metadata(meta) + + traceCtx.Finish() + time.Sleep(2 * time.Second) +} diff --git a/go/go.mod b/go/go.mod index de39998..0738297 100644 --- a/go/go.mod +++ b/go/go.mod @@ -7,3 +7,8 @@ require ( github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 github.com/tetratelabs/wazero v1.2.1 ) + +require ( + go.opentelemetry.io/proto/otlp v1.0.0 + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/go/go.sum b/go/go.sum index eda2a6d..d21de56 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1,8 +1,23 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q= github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk= -github.com/tetratelabs/wazero v1.1.0 h1:EByoAhC+QcYpwSZJSs/aV0uokxPwBgKxfiokSUwAknQ= -github.com/tetratelabs/wazero v1.1.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs= github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/scripts/generate-otel-ts.sh b/scripts/generate-otel-ts.sh old mode 100644 new mode 100755 From a3aa749e9c45b7e874165bb36c5d9d403e0b8410 Mon Sep 17 00:00:00 2001 From: Rob Date: Fri, 4 Aug 2023 09:29:13 -0600 Subject: [PATCH 2/9] Update go/adapter/honeycomb/adapter.go Co-authored-by: Steve Manuel --- go/adapter/honeycomb/adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/adapter/honeycomb/adapter.go b/go/adapter/honeycomb/adapter.go index e265fc7..af65469 100644 --- a/go/adapter/honeycomb/adapter.go +++ b/go/adapter/honeycomb/adapter.go @@ -62,7 +62,7 @@ func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { case observe.MemoryGrowEvent: log.Println("MemoryGrowEvent should be attached to a span") case observe.CustomEvent: - log.Println("Otel adapter does not respect custom events") + log.Println("Honeycomb adapter does not respect custom events") } } From 3437e09cab15370b62c0d510c87dfdd48a00a45e Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Fri, 4 Aug 2023 12:10:04 -0600 Subject: [PATCH 3/9] chore: implement PR feedback --- go/adapter/datadog/adapter.go | 27 ++++++++++++++++++--------- go/adapter/honeycomb/adapter.go | 25 ++++++++++++++----------- go/adapter/otel_formatter/format.go | 12 ++++++++++++ go/adapter/otel_stdout/adapter.go | 2 +- go/bin/honeycomb/main.go | 19 +++++-------------- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/go/adapter/datadog/adapter.go b/go/adapter/datadog/adapter.go index f93bedf..4ff21b2 100644 --- a/go/adapter/datadog/adapter.go +++ b/go/adapter/datadog/adapter.go @@ -9,7 +9,7 @@ import ( "net/url" "time" - "github.com/dylibso/observe-sdk/go" + observe "github.com/dylibso/observe-sdk/go" "github.com/dylibso/observe-sdk/go/adapter/datadog_formatter" ) @@ -86,7 +86,7 @@ func (d *DatadogAdapter) Flush(evts []observe.TraceEvent) error { } } - if len(allSpans) <= 1 { + if len(allSpans) == 0 { log.Println("No spans built for datadog trace") return nil } @@ -150,23 +150,32 @@ func (d *DatadogAdapter) Flush(evts []observe.TraceEvent) error { data := bytes.NewBuffer(b) - host, err := url.JoinPath(d.Config.AgentHost, "v0.3", "traces") + url, err := url.JoinPath(d.Config.AgentHost, "v0.3", "traces") if err != nil { log.Println("failed to create datadog agent endpoint url:", err) return nil } - resp, err := http.Post(host, "application/json", data) + client := http.Client{ + Timeout: time.Second * 2, + } + req, err := http.NewRequest("POST", url, data) if err != nil { - log.Println("failed to send trace request to datadog:", err) - return nil + log.Println("failed to create datadog endpoint url:", err) } - if resp.StatusCode != http.StatusOK { - log.Println("unexpected status code from datadog agent:", resp.StatusCode) + req.Header = http.Header{ + "content-type": {"application/json"}, + } + + resp, err := client.Do(req) + if err != nil { + log.Println("failed to send data to datadog", err) } - log.Println("Submitted traces") + if resp.StatusCode != http.StatusOK { + log.Println("unexpected status code from datadog:", resp.StatusCode) + } return nil } diff --git a/go/adapter/honeycomb/adapter.go b/go/adapter/honeycomb/adapter.go index e265fc7..721c50f 100644 --- a/go/adapter/honeycomb/adapter.go +++ b/go/adapter/honeycomb/adapter.go @@ -3,10 +3,10 @@ package honeycomb import ( "bytes" "fmt" - "io" "log" "net/http" "net/url" + "time" observe "github.com/dylibso/observe-sdk/go" otel "github.com/dylibso/observe-sdk/go/adapter/otel_formatter" @@ -66,12 +66,20 @@ func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { } } - if len(allSpans) <= 1 { + if len(allSpans) == 0 { log.Println("No spans built for honeycomb") return nil } t := otel.NewTrace(traceId, h.Config.Dataset, allSpans) + if te.AdapterMeta != nil { + meta, ok := te.AdapterMeta.(map[string]string) + if ok { + t.SetMetadata(&te, meta) + } else { + log.Println("metadata must be of type map[string]string") + } + } data, err := proto.Marshal(t.TracesData) if err != nil { log.Println("failed to marshal TracesData:", err) @@ -84,7 +92,9 @@ func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { return nil } - client := http.Client{} + client := http.Client{ + Timeout: time.Second * 2, + } req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) if err != nil { log.Println("failed to create honeycomb endpoint url:", err) @@ -97,18 +107,11 @@ func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { resp, err := client.Do(req) if err != nil { - log.Println("failed to create http client", err) + log.Println("failed to send data to honeycomb", err) } if resp.StatusCode != http.StatusOK { log.Println("unexpected status code from honeycomb:", resp.StatusCode) - - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Println("error reading response body:", err) - } - resp.Body.Close() - log.Println(string(body)) } } diff --git a/go/adapter/otel_formatter/format.go b/go/adapter/otel_formatter/format.go index f9247cb..cc3f36c 100644 --- a/go/adapter/otel_formatter/format.go +++ b/go/adapter/otel_formatter/format.go @@ -37,6 +37,18 @@ func NewTrace(traceId string, serviceName string, spans []*trace.Span) *Trace { } } +func (t *Trace) SetMetadata(te *observe.TraceEvent, meta map[string]string) { + for _, rs := range t.TracesData.ResourceSpans { + for _, ss := range rs.ScopeSpans { + for _, span := range ss.Spans { + for key, value := range meta { + span.Attributes = append(span.Attributes, NewKeyValueString(key, value)) + } + } + } + } +} + func NewSpan(traceId string, parentId []byte, name string, start, end time.Time) *trace.Span { if parentId == nil { parentId = []byte{} diff --git a/go/adapter/otel_stdout/adapter.go b/go/adapter/otel_stdout/adapter.go index 9f0da3a..874ff21 100644 --- a/go/adapter/otel_stdout/adapter.go +++ b/go/adapter/otel_stdout/adapter.go @@ -48,7 +48,7 @@ func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { } } - if len(allSpans) <= 1 { + if len(allSpans) == 0 { log.Println("No spans built for datadog trace") return nil } diff --git a/go/bin/honeycomb/main.go b/go/bin/honeycomb/main.go index 5287685..bfbb97c 100644 --- a/go/bin/honeycomb/main.go +++ b/go/bin/honeycomb/main.go @@ -6,7 +6,6 @@ import ( "os" "time" - "github.com/dylibso/observe-sdk/go/adapter/datadog" "github.com/dylibso/observe-sdk/go/adapter/honeycomb" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" @@ -54,20 +53,12 @@ func main() { defer m.Close(ctx) // normally this metadata would be in your web-server framework - // or derived when you need them. we're just gonna initialize - // some example values here - resourceName := "my-resource" - httpUrl := "https://example.com/my-endpoint" - httpStatusCode := 200 - spanKind := datadog.Server - clientIp := "66.210.227.34" + // or derived when you need them - meta := datadog.DatadogMetadata{ - ResourceName: &resourceName, - HttpUrl: &httpUrl, - HttpStatusCode: &httpStatusCode, - SpanKind: &spanKind, - HttpClientIp: &clientIp, + meta := map[string]string{ + "http.url": "https://example.com/my-endpoint", + "http.status_code": "200", + "http.client_ip": "66.210.227.34", } traceCtx.Metadata(meta) From d29544004393e030109ebc073fe4528f63f4dd81 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Mon, 7 Aug 2023 11:02:46 -0600 Subject: [PATCH 4/9] feat: add context to adapter start --- go/adapter.go | 8 ++++++-- go/adapter/datadog/adapter.go | 5 +++-- go/adapter/honeycomb/adapter.go | 5 +++-- go/adapter/otel_stdout/adapter.go | 5 +++-- go/adapter/stdout/adapter.go | 5 +++-- go/bin/datadog/main.go | 2 +- go/bin/honeycomb/main.go | 2 +- go/bin/otelstdout/main.go | 2 +- go/bin/stdout/main.go | 2 +- go/go.mod | 1 + 10 files changed, 23 insertions(+), 14 deletions(-) diff --git a/go/adapter.go b/go/adapter.go index a7be4a5..502458b 100644 --- a/go/adapter.go +++ b/go/adapter.go @@ -2,6 +2,7 @@ package observe import ( "context" + "log" "time" "github.com/tetratelabs/wazero" @@ -16,7 +17,7 @@ type AdapterConfig struct { // or provide some custom logic. HandleTraceEvent is called after // an invocation of a wasm module is done and all events are collected. type Adapter interface { - Start() + Start(context.Context) Stop(wait bool) HandleTraceEvent(e TraceEvent) } @@ -61,12 +62,15 @@ func (b *AdapterBase) HandleTraceEvent(te TraceEvent) { b.eventBucket.addEvent(te, b.flusher) } -func (b *AdapterBase) Start(a Adapter) { +func (b *AdapterBase) Start(a Adapter, ctx context.Context) { b.stop = make(chan bool) go func() { for { select { + case <-ctx.Done(): + log.Println("context cancelled") + return case event := <-b.TraceEvents: a.HandleTraceEvent(event) case <-b.stop: diff --git a/go/adapter/datadog/adapter.go b/go/adapter/datadog/adapter.go index 4ff21b2..0bacf54 100644 --- a/go/adapter/datadog/adapter.go +++ b/go/adapter/datadog/adapter.go @@ -2,6 +2,7 @@ package datadog import ( "bytes" + "context" "encoding/json" "fmt" "log" @@ -54,8 +55,8 @@ func NewDatadogAdapter(config *DatadogConfig) (*DatadogAdapter, error) { return adapter, nil } -func (d *DatadogAdapter) Start() { - d.AdapterBase.Start(d) +func (d *DatadogAdapter) Start(ctx context.Context) { + d.AdapterBase.Start(d, ctx) } func (d *DatadogAdapter) Stop(wait bool) { diff --git a/go/adapter/honeycomb/adapter.go b/go/adapter/honeycomb/adapter.go index b3c71e7..b34f420 100644 --- a/go/adapter/honeycomb/adapter.go +++ b/go/adapter/honeycomb/adapter.go @@ -2,6 +2,7 @@ package honeycomb import ( "bytes" + "context" "fmt" "log" "net/http" @@ -39,8 +40,8 @@ func NewHoneycombAdapter(config *HoneycombConfig) *HoneycombAdapter { return adapter } -func (h *HoneycombAdapter) Start() { - h.AdapterBase.Start(h) +func (h *HoneycombAdapter) Start(ctx context.Context) { + h.AdapterBase.Start(h, ctx) } func (h *HoneycombAdapter) HandleTraceEvent(te observe.TraceEvent) { diff --git a/go/adapter/otel_stdout/adapter.go b/go/adapter/otel_stdout/adapter.go index 874ff21..b8e4642 100644 --- a/go/adapter/otel_stdout/adapter.go +++ b/go/adapter/otel_stdout/adapter.go @@ -1,6 +1,7 @@ package otel_stdout import ( + "context" "encoding/json" "fmt" "log" @@ -85,6 +86,6 @@ func (o *OtelStdoutAdapter) makeCallSpans(event observe.CallEvent, parentId []by return spans } -func (o *OtelStdoutAdapter) Start() { - o.AdapterBase.Start(o) +func (o *OtelStdoutAdapter) Start(ctx context.Context) { + o.AdapterBase.Start(o, ctx) } diff --git a/go/adapter/stdout/adapter.go b/go/adapter/stdout/adapter.go index 0126baf..7b4ac55 100644 --- a/go/adapter/stdout/adapter.go +++ b/go/adapter/stdout/adapter.go @@ -1,6 +1,7 @@ package stdout import ( + "context" "log" "strings" @@ -57,6 +58,6 @@ func (s *StdoutAdapter) printEvents(event observe.CallEvent, indentation int) { } } -func (s *StdoutAdapter) Start() { - s.AdapterBase.Start(s) +func (s *StdoutAdapter) Start(ctx context.Context) { + s.AdapterBase.Start(s, ctx) } diff --git a/go/bin/datadog/main.go b/go/bin/datadog/main.go index 985ba33..7f8f0b2 100644 --- a/go/bin/datadog/main.go +++ b/go/bin/datadog/main.go @@ -21,7 +21,7 @@ func main() { log.Panicln(err) } defer adapter.Stop(true) - adapter.Start() + adapter.Start(ctx) // Load WASM from disk wasm, err := os.ReadFile(os.Args[1]) diff --git a/go/bin/honeycomb/main.go b/go/bin/honeycomb/main.go index bfbb97c..16362ae 100644 --- a/go/bin/honeycomb/main.go +++ b/go/bin/honeycomb/main.go @@ -24,7 +24,7 @@ func main() { } adapter := honeycomb.NewHoneycombAdapter(conf) defer adapter.Stop(true) - adapter.Start() + adapter.Start(ctx) // Load WASM from disk wasm, err := os.ReadFile(os.Args[1]) diff --git a/go/bin/otelstdout/main.go b/go/bin/otelstdout/main.go index e2da5a1..8746f43 100644 --- a/go/bin/otelstdout/main.go +++ b/go/bin/otelstdout/main.go @@ -17,7 +17,7 @@ func main() { // we only need to create and start once per instance of our host app adapter := otel_stdout.NewOtelStdoutAdapter() defer adapter.Stop(true) - adapter.Start() + adapter.Start(ctx) // Load WASM from disk wasm, err := os.ReadFile(os.Args[1]) diff --git a/go/bin/stdout/main.go b/go/bin/stdout/main.go index eb824db..b084a79 100644 --- a/go/bin/stdout/main.go +++ b/go/bin/stdout/main.go @@ -17,7 +17,7 @@ func main() { // we only need to create and start once per instance of our host app adapter := stdout.NewStdoutAdapter() defer adapter.Stop(true) - adapter.Start() + adapter.Start(ctx) // Load WASM from disk wasm, err := os.ReadFile(os.Args[1]) diff --git a/go/go.mod b/go/go.mod index 0738297..595981c 100644 --- a/go/go.mod +++ b/go/go.mod @@ -9,6 +9,7 @@ require ( ) require ( + github.com/golang/protobuf v1.5.3 go.opentelemetry.io/proto/otlp v1.0.0 google.golang.org/protobuf v1.31.0 // indirect ) From b86f5def624d6be20f773c8a10c6b1e71ae4f813 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Wed, 9 Aug 2023 12:53:18 -0600 Subject: [PATCH 5/9] feat(go): Add Lightstep adapter --- go/adapter/lightstep/adapter.go | 150 ++++++++++++++++++++++++++++++++ go/bin/lightstep/main.go | 68 +++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 go/adapter/lightstep/adapter.go create mode 100644 go/bin/lightstep/main.go diff --git a/go/adapter/lightstep/adapter.go b/go/adapter/lightstep/adapter.go new file mode 100644 index 0000000..3881c07 --- /dev/null +++ b/go/adapter/lightstep/adapter.go @@ -0,0 +1,150 @@ +package lightstep + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "net/http" + "net/url" + "time" + + observe "github.com/dylibso/observe-sdk/go" + otel "github.com/dylibso/observe-sdk/go/adapter/otel_formatter" + trace "go.opentelemetry.io/proto/otlp/trace/v1" + proto "google.golang.org/protobuf/proto" +) + +type LightstepConfig struct { + ApiKey string + Dataset string + EmitTracesInterval uint32 + TraceBatchMax uint32 + Host string +} + +type LightstepAdapter struct { + *observe.AdapterBase + Config *LightstepConfig +} + +func NewLightstepAdapter(config *LightstepConfig) *LightstepAdapter { + base := observe.NewAdapterBase(1, 0) + adapter := &LightstepAdapter{ + AdapterBase: &base, + Config: config, + } + + adapter.AdapterBase.SetFlusher(adapter) + + return adapter +} + +func (h *LightstepAdapter) Start(ctx context.Context) { + h.AdapterBase.Start(h, ctx) +} + +func (h *LightstepAdapter) HandleTraceEvent(te observe.TraceEvent) { + h.AdapterBase.HandleTraceEvent(te) +} + +func (h *LightstepAdapter) Flush(evts []observe.TraceEvent) error { + for _, te := range evts { + traceId := te.TelemetryId.ToHex16() + + var allSpans []*trace.Span + for _, e := range te.Events { + switch event := e.(type) { + case observe.CallEvent: // TODO: consider renaming to FunctionCall for consistency across Rust & JS + spans := h.makeCallSpans(event, nil, traceId) + if len(spans) > 0 { + allSpans = append(allSpans, spans...) + } + case observe.MemoryGrowEvent: + log.Println("MemoryGrowEvent should be attached to a span") + case observe.CustomEvent: + log.Println("lightstep adapter does not respect custom events") + } + } + + if len(allSpans) == 0 { + log.Println("No spans built for lightstep") + return nil + } + + t := otel.NewTrace(traceId, h.Config.Dataset, allSpans) + if te.AdapterMeta != nil { + meta, ok := te.AdapterMeta.(map[string]string) + if ok { + t.SetMetadata(&te, meta) + } else { + log.Println("metadata must be of type map[string]string") + } + } + data, err := proto.Marshal(t.TracesData) + if err != nil { + log.Println("failed to marshal TracesData:", err) + return nil + } + + url, err := url.JoinPath(h.Config.Host, "traces", "otlp", "v0.9") + if err != nil { + log.Println("failed to create lightstep endpoint url:", err) + return nil + } + + client := http.Client{ + Timeout: time.Second * 2, + } + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + log.Println("failed to create lightstep endpoint url:", err) + } + + req.Header = http.Header{ + // "content-type": {"application/protobuf"}, + "lightstep-access-token": {h.Config.ApiKey}, + } + + resp, err := client.Do(req) + if err != nil { + log.Println("failed to send data to lightstep", err) + } + + if resp.StatusCode != http.StatusOK { + log.Println("unexpected status code from lightstep:", resp.StatusCode) + // read response body + body, error := io.ReadAll(resp.Body) + if error != nil { + fmt.Println(error) + } + // close response body + resp.Body.Close() + + // print response body + fmt.Println(string(body)) + } + + } + + return nil +} + +func (h *LightstepAdapter) makeCallSpans(event observe.CallEvent, parentId []byte, traceId string) []*trace.Span { + name := event.FunctionName() + span := otel.NewSpan(traceId, parentId, name, event.Time, event.Time.Add(event.Duration)) + span.Attributes = append(span.Attributes, otel.NewKeyValueString("function-name", fmt.Sprintf("function-call-%s", name))) + + spans := []*trace.Span{span} + for _, ev := range event.Within() { + if call, ok := ev.(observe.CallEvent); ok { + spans = append(spans, h.makeCallSpans(call, span.SpanId, traceId)...) + } + if alloc, ok := ev.(observe.MemoryGrowEvent); ok { + last := spans[len(spans)-1] + last.Attributes = append(last.Attributes, otel.NewKeyValueInt64("allocation", int64(alloc.MemoryGrowAmount()))) + } + } + return spans +} diff --git a/go/bin/lightstep/main.go b/go/bin/lightstep/main.go new file mode 100644 index 0000000..44bc16d --- /dev/null +++ b/go/bin/lightstep/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/dylibso/observe-sdk/go/adapter/lightstep" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +func main() { + ctx := context.Background() + + // we only need to create and start once per instance of our host app + conf := &lightstep.LightstepConfig{ + ApiKey: os.Getenv("LIGHTSTEP_API_KEY"), + Dataset: "golang", + EmitTracesInterval: 1000, + TraceBatchMax: 100, + Host: "https://ingest.lightstep.com", + } + adapter := lightstep.NewLightstepAdapter(conf) + defer adapter.Stop(true) + adapter.Start(ctx) + + // Load WASM from disk + wasm, err := os.ReadFile(os.Args[1]) + if err != nil { + log.Panicln(err) + } + + cfg := wazero.NewRuntimeConfig().WithCustomSections(true) + rt := wazero.NewRuntimeWithConfig(ctx, cfg) + traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) + if err != nil { + log.Panicln(err) + } + wasi_snapshot_preview1.MustInstantiate(ctx, rt) + + config := wazero.NewModuleConfig(). + WithStdin(os.Stdin). + WithStdout(os.Stdout). + WithStderr(os.Stderr). + WithArgs(os.Args[1:]...). + WithStartFunctions("_start") + m, err := rt.InstantiateWithConfig(ctx, wasm, config) + if err != nil { + log.Panicln(err) + } + defer m.Close(ctx) + + // normally this metadata would be in your web-server framework + // or derived when you need them + + meta := map[string]string{ + "http.url": "https://example.com/my-endpoint", + "http.status_code": "200", + "http.client_ip": "66.210.227.34", + } + traceCtx.Metadata(meta) + + traceCtx.Finish() + time.Sleep(2 * time.Second) +} From 73133957081a5ca668982fc251b05bb765a82f10 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Wed, 9 Aug 2023 13:40:38 -0600 Subject: [PATCH 6/9] fix: use x-protobuf in header --- go/adapter/lightstep/adapter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/adapter/lightstep/adapter.go b/go/adapter/lightstep/adapter.go index 3881c07..87315f2 100644 --- a/go/adapter/lightstep/adapter.go +++ b/go/adapter/lightstep/adapter.go @@ -103,7 +103,7 @@ func (h *LightstepAdapter) Flush(evts []observe.TraceEvent) error { } req.Header = http.Header{ - // "content-type": {"application/protobuf"}, + "content-type": {"application/x-protobuf"}, "lightstep-access-token": {h.Config.ApiKey}, } From c432589824c499d700de22e6e50369016a843478 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Thu, 10 Aug 2023 10:56:41 -0600 Subject: [PATCH 7/9] fix(go): lightstep naming conventions --- go/adapter/lightstep/adapter.go | 4 ++-- go/bin/lightstep/main.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go/adapter/lightstep/adapter.go b/go/adapter/lightstep/adapter.go index 87315f2..eab70fe 100644 --- a/go/adapter/lightstep/adapter.go +++ b/go/adapter/lightstep/adapter.go @@ -18,7 +18,7 @@ import ( type LightstepConfig struct { ApiKey string - Dataset string + ServiceName string EmitTracesInterval uint32 TraceBatchMax uint32 Host string @@ -73,7 +73,7 @@ func (h *LightstepAdapter) Flush(evts []observe.TraceEvent) error { return nil } - t := otel.NewTrace(traceId, h.Config.Dataset, allSpans) + t := otel.NewTrace(traceId, h.Config.ServiceName, allSpans) if te.AdapterMeta != nil { meta, ok := te.AdapterMeta.(map[string]string) if ok { diff --git a/go/bin/lightstep/main.go b/go/bin/lightstep/main.go index 44bc16d..938f794 100644 --- a/go/bin/lightstep/main.go +++ b/go/bin/lightstep/main.go @@ -18,7 +18,7 @@ func main() { // we only need to create and start once per instance of our host app conf := &lightstep.LightstepConfig{ ApiKey: os.Getenv("LIGHTSTEP_API_KEY"), - Dataset: "golang", + ServiceName: "golang", EmitTracesInterval: 1000, TraceBatchMax: 100, Host: "https://ingest.lightstep.com", From 27538cb9e16040a0262dc448187fb87587a10022 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Thu, 10 Aug 2023 10:58:59 -0600 Subject: [PATCH 8/9] fix(go): reduce verbosity of errors in go --- go/adapter/lightstep/adapter.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/go/adapter/lightstep/adapter.go b/go/adapter/lightstep/adapter.go index eab70fe..e724688 100644 --- a/go/adapter/lightstep/adapter.go +++ b/go/adapter/lightstep/adapter.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "io" "log" "net/http" "net/url" @@ -114,16 +113,6 @@ func (h *LightstepAdapter) Flush(evts []observe.TraceEvent) error { if resp.StatusCode != http.StatusOK { log.Println("unexpected status code from lightstep:", resp.StatusCode) - // read response body - body, error := io.ReadAll(resp.Body) - if error != nil { - fmt.Println(error) - } - // close response body - resp.Body.Close() - - // print response body - fmt.Println(string(body)) } } From cfe8ad6a4b6f9e1feeecc0c6bc26ceb4cf482c30 Mon Sep 17 00:00:00 2001 From: Rob Wong Date: Fri, 11 Aug 2023 21:09:21 -0600 Subject: [PATCH 9/9] chore: PR feedback --- go/adapter/datadog/adapter.go | 1 - go/adapter/honeycomb/adapter.go | 3 +-- go/adapter/lightstep/adapter.go | 3 +-- go/adapter/otel_stdout/adapter.go | 1 - go/bin/honeycomb/main.go | 2 +- go/bin/lightstep/main.go | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/go/adapter/datadog/adapter.go b/go/adapter/datadog/adapter.go index 0bacf54..2af71ec 100644 --- a/go/adapter/datadog/adapter.go +++ b/go/adapter/datadog/adapter.go @@ -88,7 +88,6 @@ func (d *DatadogAdapter) Flush(evts []observe.TraceEvent) error { } if len(allSpans) == 0 { - log.Println("No spans built for datadog trace") return nil } diff --git a/go/adapter/honeycomb/adapter.go b/go/adapter/honeycomb/adapter.go index b34f420..98aae93 100644 --- a/go/adapter/honeycomb/adapter.go +++ b/go/adapter/honeycomb/adapter.go @@ -18,7 +18,7 @@ import ( type HoneycombConfig struct { ApiKey string Dataset string - EmitTracesInterval uint32 + EmitTracesInterval time.Duration TraceBatchMax uint32 Host string } @@ -68,7 +68,6 @@ func (h *HoneycombAdapter) Flush(evts []observe.TraceEvent) error { } if len(allSpans) == 0 { - log.Println("No spans built for honeycomb") return nil } diff --git a/go/adapter/lightstep/adapter.go b/go/adapter/lightstep/adapter.go index e724688..a0cf7dc 100644 --- a/go/adapter/lightstep/adapter.go +++ b/go/adapter/lightstep/adapter.go @@ -18,7 +18,7 @@ import ( type LightstepConfig struct { ApiKey string ServiceName string - EmitTracesInterval uint32 + EmitTracesInterval time.Duration TraceBatchMax uint32 Host string } @@ -68,7 +68,6 @@ func (h *LightstepAdapter) Flush(evts []observe.TraceEvent) error { } if len(allSpans) == 0 { - log.Println("No spans built for lightstep") return nil } diff --git a/go/adapter/otel_stdout/adapter.go b/go/adapter/otel_stdout/adapter.go index b8e4642..1c9389c 100644 --- a/go/adapter/otel_stdout/adapter.go +++ b/go/adapter/otel_stdout/adapter.go @@ -50,7 +50,6 @@ func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { } if len(allSpans) == 0 { - log.Println("No spans built for datadog trace") return nil } diff --git a/go/bin/honeycomb/main.go b/go/bin/honeycomb/main.go index 16362ae..065ddf3 100644 --- a/go/bin/honeycomb/main.go +++ b/go/bin/honeycomb/main.go @@ -18,7 +18,7 @@ func main() { conf := &honeycomb.HoneycombConfig{ ApiKey: os.Getenv("HONEYCOMB_API_KEY"), Dataset: "golang", - EmitTracesInterval: 1000, + EmitTracesInterval: time.Second * 1, TraceBatchMax: 100, Host: "https://api.honeycomb.io", } diff --git a/go/bin/lightstep/main.go b/go/bin/lightstep/main.go index 938f794..db48407 100644 --- a/go/bin/lightstep/main.go +++ b/go/bin/lightstep/main.go @@ -19,7 +19,7 @@ func main() { conf := &lightstep.LightstepConfig{ ApiKey: os.Getenv("LIGHTSTEP_API_KEY"), ServiceName: "golang", - EmitTracesInterval: 1000, + EmitTracesInterval: time.Second * 1, TraceBatchMax: 100, Host: "https://ingest.lightstep.com", }