Skip to content

Commit

Permalink
feat: add Trace Context HTTP headers
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Apr 4, 2023
1 parent 085ed9d commit 53a2de0
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 10 deletions.
7 changes: 7 additions & 0 deletions examples/gateway/car/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"flag"
"io"
"log"
Expand Down Expand Up @@ -32,6 +33,12 @@ func main() {
log.Fatal(err)
}

ctx := context.Background()
tp := common.NewTracerProvider()
defer func() {
_ = tp.Shutdown(ctx)
}()

handler := common.NewHandler(gwAPI)

log.Printf("Listening on http://localhost:%d", *port)
Expand Down
28 changes: 24 additions & 4 deletions examples/gateway/car/main_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"io"
"net/http"
"net/http/httptest"
Expand All @@ -17,7 +18,7 @@ const (
BaseCID = "bafybeidhua2wpy27vo3t7ms22ybc7m7iqkm2opiebpjmo24lvixcnvznnu"
)

func newTestServer() (*httptest.Server, io.Closer, error) {
func newTestServer(t *testing.T) (*httptest.Server, io.Closer, error) {
blockService, _, f, err := newBlockServiceFromCAR("./test.car")
if err != nil {
return nil, nil, err
Expand All @@ -29,13 +30,19 @@ func newTestServer() (*httptest.Server, io.Closer, error) {
return nil, nil, err
}

ctx := context.Background()
tp := common.NewTracerProvider()
t.Cleanup(func() {
_ = tp.Shutdown(ctx)
})

handler := common.NewHandler(gateway)
ts := httptest.NewServer(handler)
return ts, f, nil
}

func TestDirectoryTraverse(t *testing.T) {
ts, f, err := newTestServer()
ts, f, err := newTestServer(t)
assert.Nil(t, err)
defer f.Close()

Expand All @@ -49,7 +56,7 @@ func TestDirectoryTraverse(t *testing.T) {
}

func TestFile(t *testing.T) {
ts, f, err := newTestServer()
ts, f, err := newTestServer(t)
assert.Nil(t, err)
defer f.Close()

Expand All @@ -63,7 +70,7 @@ func TestFile(t *testing.T) {
}

func TestDirectoryAsDAG(t *testing.T) {
ts, f, err := newTestServer()
ts, f, err := newTestServer(t)
assert.Nil(t, err)
defer f.Close()

Expand Down Expand Up @@ -107,3 +114,16 @@ func TestDirectoryAsDAG(t *testing.T) {
assert.Nil(t, err)
assert.EqualValues(t, hash.String(), "bafybeigmlfksb374fdkxih4urny2yiyazyra2375y2e4a72b3jcrnthnau")
}

func TestTraceContextPresent(t *testing.T) {
ts, f, err := newTestServer(t)
assert.Nil(t, err)
defer f.Close()

res, err := http.Get(ts.URL + "/ipfs/" + BaseCID)
assert.Nil(t, err)
defer res.Body.Close()

traceparent := res.Header.Get("Traceparent")
assert.NotEmpty(t, traceparent)
}
8 changes: 7 additions & 1 deletion examples/gateway/common/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ipfs/boxo/gateway"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func NewHandler(gwAPI gateway.IPFSBackend) http.Handler {
Expand Down Expand Up @@ -58,10 +59,15 @@ func NewHandler(gwAPI gateway.IPFSBackend) http.Handler {
var handler http.Handler
handler = gateway.WithHostname(mux, gwAPI, publicGateways, noDNSLink)

// Finally, wrap with the withConnect middleware. This is required since we use
// Then, wrap with the withConnect middleware. This is required since we use
// http.ServeMux which does not support CONNECT by default.
handler = withConnect(handler)

// Finally, wrap with the otelhttp handler. This will allow the tracing system
// to work and for correct propagation of tracing headers. This step is optional
// and only required if you want to use tracing.
handler = otelhttp.NewHandler(handler, "Gateway.Request")

return handler
}

Expand Down
16 changes: 16 additions & 0 deletions examples/gateway/common/tracing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package common

import (
"go.opentelemetry.io/contrib/propagators/autoprop"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/sdk/trace"
)

func NewTracerProvider() *trace.TracerProvider {
tp := trace.NewTracerProvider()

otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())

return tp
}
7 changes: 7 additions & 0 deletions examples/gateway/proxy/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"flag"
"log"
"net/http"
Expand Down Expand Up @@ -37,6 +38,12 @@ func main() {
log.Fatal(err)
}

ctx := context.Background()
tp := common.NewTracerProvider()
defer func() {
_ = tp.Shutdown(ctx)
}()

handler := common.NewHandler(gwAPI)

log.Printf("Listening on http://localhost:%d", *port)
Expand Down
11 changes: 10 additions & 1 deletion examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ require (
github.com/multiformats/go-multicodec v0.8.1
github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.2
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/sdk v1.14.0
)

require (
Expand All @@ -33,6 +37,7 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/elastic/gosigar v0.14.2 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
Expand Down Expand Up @@ -125,7 +130,11 @@ require (
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.15.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/dig v1.15.0 // indirect
Expand Down
18 changes: 18 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
Expand Down Expand Up @@ -644,8 +646,24 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI=
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0 h1:Lj33jj7eIrBfIShiK8NU91u2BglKnUS1UUxVemuQJtw=
go.opentelemetry.io/contrib/propagators/autoprop v0.40.0/go.mod h1:6QO816FeZ+6zahs6hYqbUCCsnNBm7o+t4iwVySpzcdI=
go.opentelemetry.io/contrib/propagators/aws v1.15.0 h1:FLe+bRTMAhEALItDQt1U2S/rdq8/rGGJTJpOpCDvMu0=
go.opentelemetry.io/contrib/propagators/aws v1.15.0/go.mod h1:Z/nqdjqKjErrS3gYoEMZt8//dt8VZbqalD0V+7vh7lM=
go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q=
go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c=
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug=
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw=
go.opentelemetry.io/contrib/propagators/ot v1.15.0 h1:iBNejawWy7wWZ5msuZDNcMjBy14Wc0v3gCAXukGHN/Q=
go.opentelemetry.io/contrib/propagators/ot v1.15.0/go.mod h1:0P7QQ+MHt6SXR1ATaMpewSiWlp8NbKErNLKcaU4EEKI=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs=
go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
Expand Down
5 changes: 4 additions & 1 deletion gateway/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ type handler struct {
// NewHandler returns an http.Handler that can act as a gateway to IPFS content
// offlineApi is a version of the API that should not make network requests for missing data
func NewHandler(c Config, api IPFSBackend) http.Handler {
return newHandlerWithMetrics(c, api)
var h http.Handler
h = newHandlerWithMetrics(c, api)
h = withTraceContext(h)
return h
}

// StatusResponseWriter enables us to override HTTP Status Code passed to
Expand Down
20 changes: 17 additions & 3 deletions gateway/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package gateway

import (
"context"
"fmt"
"io"
"net/http"
"time"

"github.com/ipfs/boxo/coreiface/path"
Expand All @@ -12,6 +12,7 @@ import (
prometheus "github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -294,7 +295,20 @@ func newHistogramMetric(name string, help string) *prometheus.HistogramVec {
return histogramMetric
}

var tracer = otel.Tracer("boxo/gateway")

// spanTrace starts a new span using the standard IPFS tracing conventions.
func spanTrace(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return otel.Tracer("boxo").Start(ctx, fmt.Sprintf("%s.%s", " Gateway", spanName), opts...)
func spanTrace(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return tracer.Start(ctx, name, opts...)
}

// withTraceContext sets the Trace Context (https://github.com/w3c/trace-context)
// headers on the request based on the context and the propagator.
func withTraceContext(next http.Handler) http.Handler {
props := otel.GetTextMapPropagator()

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
props.Inject(r.Context(), propagation.HeaderCarrier(w.Header()))
next.ServeHTTP(w, r)
})
}

0 comments on commit 53a2de0

Please sign in to comment.