Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenCensus tracing #25

Merged
merged 16 commits into from
Nov 2, 2018
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ vet:
golint:
golint -set_exit_status ./...
gocyclo:
gocyclo -over 12 $(GO_FILES_NO_TEST)
gocyclo -over 13 $(GO_FILES_NO_TEST)
errcheck:
errcheck -ignoretests ./...
nakedret:
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ require (
github.com/kisielk/errcheck v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
go.opencensus.io v0.18.0
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 // indirect
golang.org/x/tools v0.0.0-20181101071927-45ff765b4815 // indirect
golang.org/x/net v0.0.0-20181029044818-c44066c5c816 // indirect
golang.org/x/tools v0.0.0-20181102050050-92b943e6bff7 // indirect
)
31 changes: 29 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/alexkohler/nakedret v0.0.0-20171106223215-c0e305a4f690 h1:+tfdYWf4oDrj9c0/77f5oDBxZT2EPjS1AJf+PApGNCk=
github.com/alexkohler/nakedret v0.0.0-20171106223215-c0e305a4f690/go.mod h1:tfDQbtPt67HhBK/6P0yNktIX7peCxfOp0jO9007DrLE=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 h1:roDmqJ4Qes7hrDOsWsMCce0vQHz3xiMPjJ9m4c2eeNs=
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816 h1:mVFkLpejdFLXVUv9E42f3XJVfMdqd0IVLVIVLjZWn5o=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181101071927-45ff765b4815 h1:MFLfgxncXyUrOtGJGmWoQH0yNFWSHGuJjDWqYe1h9Ug=
golang.org/x/tools v0.0.0-20181101071927-45ff765b4815/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181102050050-92b943e6bff7 h1:BdbtPv/+DFTRA5+9560n/FamYyleF+XonScH/3H1+Tk=
golang.org/x/tools v0.0.0-20181102050050-92b943e6bff7/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
96 changes: 88 additions & 8 deletions search.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/url"
"strconv"
"time"

"go.opencensus.io/trace"
)

var (
Expand All @@ -27,28 +29,46 @@ var (
// - context has a deadline/timeout set and
// - seconds to wait is not after context's deadline/timeout, making this fail early
func (cms *Contentful) GetMany(ctx context.Context, parameters SearchParameters, data interface{}) error {
ctx, span := trace.StartSpan(ctx, "github.com/janivihervas/contentful-go.GetMany")
defer span.End()

response, err := cms.search(ctx, parameters)
if err != nil {
addSpanError(span, trace.StatusCodeUnknown, err)
return err
}

if response.Total == 0 || len(response.Items) == 0 {
addSpanError(span, trace.StatusCodeNotFound, ErrNoEntries)
return ErrNoEntries
}

_, spanParse := trace.StartSpan(ctx, "github.com/janivihervas/contentful-go.parse")
defer spanParse.End()
appendIncludes(&response)

flattenedItems, err := flattenItems(response.Includes, response.Items)
if err != nil {
addSpanError(spanParse, trace.StatusCodeUnknown, err)
addSpanError(span, trace.StatusCodeUnknown, err)
return err
}

bytes, err := json.Marshal(flattenedItems)
if err != nil {
addSpanError(spanParse, trace.StatusCodeInternal, err)
addSpanError(span, trace.StatusCodeInternal, err)
return err
}

return json.Unmarshal(bytes, data)
err = json.Unmarshal(bytes, data)
if err != nil {
addSpanError(spanParse, trace.StatusCodeInternal, err)
addSpanError(span, trace.StatusCodeInternal, err)
return err
}

return nil
}

// GetOne entry from Contentful. The flattened json output will be marshaled into data parameter.
Expand All @@ -58,74 +78,134 @@ func (cms *Contentful) GetMany(ctx context.Context, parameters SearchParameters,
// - context has a deadline/timeout set and
// - seconds to wait is not after context's deadline/timeout, making this fail early
func (cms *Contentful) GetOne(ctx context.Context, parameters SearchParameters, data interface{}) error {
ctx, span := trace.StartSpan(ctx, "github.com/janivihervas/contentful-go.GetOne")
defer span.End()

response, err := cms.search(ctx, parameters)
if err != nil {
addSpanError(span, trace.StatusCodeUnknown, err)
return err
}

if response.Total == 0 || len(response.Items) == 0 {
addSpanError(span, trace.StatusCodeNotFound, ErrNoEntries)
return ErrNoEntries
}

if response.Total != 1 || len(response.Items) != 1 {
addSpanError(span, trace.StatusCodeOutOfRange, ErrMoreThanOneEntry)
return ErrMoreThanOneEntry
}

_, spanParse := trace.StartSpan(ctx, "github.com/janivihervas/contentful-go.parse")
defer spanParse.End()
appendIncludes(&response)

flattenedItem, err := flattenItem(response.Includes, response.Items[0])
if err != nil {
addSpanError(spanParse, trace.StatusCodeUnknown, err)
addSpanError(span, trace.StatusCodeUnknown, err)
return err
}

bytes, err := json.Marshal(flattenedItem)
if err != nil {
addSpanError(spanParse, trace.StatusCodeInternal, err)
addSpanError(span, trace.StatusCodeInternal, err)
return err
}

return json.Unmarshal(bytes, data)
err = json.Unmarshal(bytes, data)
if err != nil {
addSpanError(spanParse, trace.StatusCodeInternal, err)
addSpanError(span, trace.StatusCodeInternal, err)
return err
}

return nil
}

func (cms *Contentful) search(ctx context.Context, parameters SearchParameters) (searchResults, error) {
ctx, span := trace.StartSpan(ctx, "github.com/janivihervas/contentful-go.search")
defer span.End()

response := searchResults{}
if parameters.Values == nil {
parameters.Values = url.Values{}
}
parameters.Set("include", "10")

u := cms.url + "/spaces/" + cms.spaceID + "/entries?" + parameters.Encode()
req, err := http.NewRequest("GET", u, nil)
urlStr := cms.url + "/spaces/" + cms.spaceID + "/entries?" + parameters.Encode()
urlParsed, err := url.Parse(urlStr)
if err != nil {
addSpanError(span, trace.StatusCodeInternal, err)
return response, err
}

span.AddAttributes(trace.StringAttribute("http.host", urlParsed.Host))
span.AddAttributes(trace.StringAttribute("http.method", http.MethodGet))
span.AddAttributes(trace.StringAttribute("http.path", urlParsed.Path))
span.AddAttributes(trace.StringAttribute("http.query", urlParsed.RawQuery))

req, err := http.NewRequest(http.MethodGet, urlStr, nil)
if err != nil {
addSpanError(span, trace.StatusCodeInternal, err)
return response, err
}

req.Header.Add("Authorization", "Bearer "+cms.token)
req = req.WithContext(ctx)
resp, err := http.DefaultClient.Do(req)
if err == context.Canceled {
addSpanError(span, trace.StatusCodeCancelled, err)
return response, err
}
if err == context.DeadlineExceeded {
addSpanError(span, trace.StatusCodeDeadlineExceeded, err)
return response, err
}
if err != nil {
addSpanError(span, trace.StatusCodeUnknown, err)
return response, err
}
defer func() {
_ = resp.Body.Close()
}()

span.AddAttributes(trace.Int64Attribute("http.status_code", int64(resp.StatusCode)))

if resp.StatusCode == http.StatusTooManyRequests {
addSpanError(span, trace.StatusCodeResourceExhausted, ErrTooManyRequests)
seconds := retryAfter(ctx, resp)
if seconds == -1 {
addSpanError(span, trace.StatusCodeDeadlineExceeded, ErrTooManyRequests)
return response, ErrTooManyRequests
}

time.Sleep(time.Second * time.Duration(seconds))
return cms.search(ctx, parameters)
span.AddAttributes(trace.Int64Attribute("http.ratelimit_reset", int64(seconds)))

select {
case <-time.After(time.Second * time.Duration(seconds)):
return cms.search(ctx, parameters)
case <-ctx.Done():
addSpanError(span, trace.StatusCodeCancelled, err)
return response, ctx.Err()
}
}

if resp.StatusCode != http.StatusOK {
return response, fmt.Errorf("non-ok status code: %d", resp.StatusCode)
err = fmt.Errorf("non-ok status code: %d", resp.StatusCode)
addSpanError(span, trace.StatusCodeUnknown, err)
return response, err
}

err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
addSpanError(span, trace.StatusCodeInternal, err)
return response, err
}

return response, err
return response, nil
}

func retryAfter(ctx context.Context, resp *http.Response) int {
Expand Down
9 changes: 9 additions & 0 deletions span.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package contentful

import "go.opencensus.io/trace"

func addSpanError(span *trace.Span, statusCode int32, err error) {
span.SetStatus(trace.Status{Code: statusCode, Message: err.Error()})
// For Jaeger
span.AddAttributes(trace.BoolAttribute("error", true))
}