From 66b4fe393c11ec5d49f0ecfcb08466b2b6882ec8 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 3 Dec 2021 13:34:23 -0500 Subject: [PATCH] feat: add basic gateway tracing --- cmd/ipfs/main.go | 28 +++++-- core/coreapi/path.go | 15 ++++ core/coreapi/unixfs.go | 6 ++ core/corehttp/gateway.go | 5 +- core/corehttp/gateway_handler.go | 39 +++++++--- core/coreunix/add.go | 25 ++++-- go.mod | 8 ++ go.sum | 47 ++++++++++-- tracing/tracing.go | 127 +++++++++++++++++++++++++++++++ 9 files changed, 269 insertions(+), 31 deletions(-) create mode 100644 tracing/tracing.go diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index d975d18971f9..1bc63e78d70c 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -20,6 +20,8 @@ import ( loader "github.com/ipfs/go-ipfs/plugin/loader" repo "github.com/ipfs/go-ipfs/repo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" + "github.com/ipfs/go-ipfs/tracing" + "go.opentelemetry.io/otel" cmds "github.com/ipfs/go-ipfs-cmds" "github.com/ipfs/go-ipfs-cmds/cli" @@ -71,21 +73,33 @@ func main() { os.Exit(mainRet()) } -func mainRet() int { +func printErr(err error) int { + fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) + return 1 +} + +func mainRet() (exitCode int) { rand.Seed(time.Now().UnixNano()) ctx := logging.ContextWithLoggable(context.Background(), loggables.Uuid("session")) var err error - // we'll call this local helper to output errors. - // this is so we control how to print errors in one place. - printErr := func(err error) { - fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) + tp, err := tracing.NewTracerProvider(ctx) + if err != nil { + return printErr(err) } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + exitCode = printErr(err) + } + }() + otel.SetTracerProvider(tp) + + ctx, span := otel.Tracer("").Start(ctx, "Run") + defer span.End() stopFunc, err := profileIfEnabled() if err != nil { - printErr(err) - return 1 + return printErr(err) } defer stopFunc() // to be executed as late as possible diff --git a/core/coreapi/path.go b/core/coreapi/path.go index b9bf83e0df64..a6928d987246 100644 --- a/core/coreapi/path.go +++ b/core/coreapi/path.go @@ -6,6 +6,9 @@ import ( gopath "path" "github.com/ipfs/go-namesys/resolve" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ipfs/go-cid" "github.com/ipfs/go-fetcher" @@ -19,6 +22,12 @@ import ( // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the // resolved Node. func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, error) { + ctx, span := otel.Tracer("").Start( + ctx, + "CoreAPI-ResolveNode", + trace.WithAttributes(attribute.String("Path", p.String())), + ) + defer span.End() rp, err := api.ResolvePath(ctx, p) if err != nil { return nil, err @@ -34,6 +43,12 @@ func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, er // ResolvePath resolves the path `p` using Unixfs resolver, returns the // resolved path. func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.Resolved, error) { + ctx, span := otel.Tracer("").Start( + ctx, + "CoreAPI-ResolvePath", + trace.WithAttributes(attribute.String("Path", p.String())), + ) + defer span.End() if _, ok := p.(path.Resolved); ok { return p.(path.Resolved), nil } diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go index 55410dcb0987..5a9585b143c4 100644 --- a/core/coreapi/unixfs.go +++ b/core/coreapi/unixfs.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/ipfs/go-ipfs/core" + "go.opentelemetry.io/otel" "github.com/ipfs/go-ipfs/core/coreunix" @@ -55,6 +56,9 @@ func getOrCreateNilNode() (*core.IpfsNode, error) { // Add builds a merkledag node from a reader, adds it to the blockstore, // and returns the key representing that node. func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options.UnixfsAddOption) (path.Resolved, error) { + ctx, span := otel.Tracer("").Start(ctx, "UnixfsAPI-Add") + defer span.End() + settings, prefix, err := options.UnixfsAddOptions(opts...) if err != nil { return nil, err @@ -179,6 +183,8 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options } func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error) { + ctx, span := otel.Tracer("").Start(ctx, "UnixFSGet") + defer span.End() ses := api.core().getSession(ctx) nd, err := ses.ResolveNode(ctx, p) diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index fb1524da529e..c3d008431f09 100644 --- a/core/corehttp/gateway.go +++ b/core/corehttp/gateway.go @@ -9,6 +9,7 @@ import ( version "github.com/ipfs/go-ipfs" core "github.com/ipfs/go-ipfs/core" coreapi "github.com/ipfs/go-ipfs/core/coreapi" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" options "github.com/ipfs/interface-go-ipfs-core/options" id "github.com/libp2p/go-libp2p/p2p/protocol/identify" @@ -87,12 +88,14 @@ func GatewayOption(writable bool, paths ...string) ServeOption { "X-Stream-Output", }, headers[ACEHeadersName]...)) - gateway := newGatewayHandler(GatewayConfig{ + var gateway http.Handler = newGatewayHandler(GatewayConfig{ Headers: headers, Writable: writable, PathPrefixes: cfg.Gateway.PathPrefixes, }, api) + gateway = otelhttp.NewHandler(gateway, "GatewayRequest") + for _, p := range paths { mux.Handle(p+"/", gateway) } diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index f5ee54d8cfd1..c11c7adab003 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -29,6 +29,7 @@ import ( ipath "github.com/ipfs/interface-go-ipfs-core/path" routing "github.com/libp2p/go-libp2p-core/routing" prometheus "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/otel" ) const ( @@ -136,6 +137,7 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // the hour is a hard fallback, we don't expect it to happen, but just in case ctx, cancel := context.WithTimeout(r.Context(), time.Hour) defer cancel() + r = r.WithContext(ctx) defer func() { @@ -194,6 +196,9 @@ func (i *gatewayHandler) optionsHandler(w http.ResponseWriter, r *http.Request) } func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { + ctx, span := otel.Tracer("").Start(r.Context(), "gatewayHandler-getOrHeadHandler") + defer span.End() + begin := time.Now() urlPath := r.URL.Path escapedURLPath := r.URL.EscapedPath() @@ -271,7 +276,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } // Resolve path to the final DAG node for the ETag - resolvedPath, err := i.api.ResolvePath(r.Context(), parsedPath) + resolvedPath, err := i.api.ResolvePath(ctx, parsedPath) switch err { case nil: case coreiface.ErrOffline: @@ -286,7 +291,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request return } - dr, err := i.api.Unixfs().Get(r.Context(), resolvedPath) + dr, err := i.api.Unixfs().Get(ctx, resolvedPath) if err != nil { webError(w, "ipfs cat "+escapedURLPath, err, http.StatusNotFound) return @@ -345,7 +350,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } else { name = getFilename(urlPath) } - i.serveFile(w, r, name, modtime, f) + i.serveFile(ctx, w, r, name, modtime, f) return } dir, ok := dr.(files.Directory) @@ -354,7 +359,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request return } - idx, err := i.api.Unixfs().Get(r.Context(), ipath.Join(resolvedPath, "index.html")) + idx, err := i.api.Unixfs().Get(ctx, ipath.Join(resolvedPath, "index.html")) switch err.(type) { case nil: dirwithoutslash := urlPath[len(urlPath)-1] != '/' @@ -377,7 +382,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } // write to request - i.serveFile(w, r, "index.html", modtime, f) + i.serveFile(ctx, w, r, "index.html", modtime, f) return case resolver.ErrNoLink: // no index.html; noop @@ -403,6 +408,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } // storage for directory listing + span.AddEvent("DirListingStart") var dirListing []directoryItem dirit := dir.Entries() for dirit.Next() { @@ -412,7 +418,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request size = humanize.Bytes(uint64(s)) } - resolved, err := i.api.ResolvePath(r.Context(), ipath.Join(resolvedPath, dirit.Name())) + resolved, err := i.api.ResolvePath(ctx, ipath.Join(resolvedPath, dirit.Name())) if err != nil { internalWebError(w, err) return @@ -429,6 +435,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } dirListing = append(dirListing, di) } + span.AddEvent("DirListingEnd") if dirit.Err() != nil { internalWebError(w, dirit.Err()) return @@ -470,7 +477,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request var gwURL string // Get gateway hostname and build gateway URL. - if h, ok := r.Context().Value("gw-hostname").(string); ok { + if h, ok := ctx.Value("gw-hostname").(string); ok { gwURL = "//" + h } else { gwURL = "" @@ -497,7 +504,11 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request } } -func (i *gatewayHandler) serveFile(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, file files.File) { +func (i *gatewayHandler) serveFile(ctx context.Context, w http.ResponseWriter, req *http.Request, name string, modtime time.Time, file files.File) { + ctx, span := otel.Tracer("").Start(ctx, "gatewayHandler-serveFile") + defer span.End() + req = req.WithContext(ctx) + size, err := file.Size() if err != nil { http.Error(w, "cannot serve files with unknown sizes", http.StatusBadGateway) @@ -577,7 +588,10 @@ func (i *gatewayHandler) servePretty404IfPresent(w http.ResponseWriter, r *http. } func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { - p, err := i.api.Unixfs().Add(r.Context(), files.NewReaderFile(r.Body)) + ctx, span := otel.Tracer("").Start(r.Context(), "gatewayHandler-postHandler") + defer span.End() + + p, err := i.api.Unixfs().Add(ctx, files.NewReaderFile(r.Body)) if err != nil { internalWebError(w, err) return @@ -589,7 +603,9 @@ func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { } func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() + ctx, span := otel.Tracer("").Start(r.Context(), "gatewayHandler-putHandler") + defer span.End() + ds := i.api.Dag() // Parse the path @@ -681,7 +697,8 @@ func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { } func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() + ctx, span := otel.Tracer("").Start(r.Context(), "gatewayHandler-deleteHandler") + defer span.End() // parse the path diff --git a/core/coreunix/add.go b/core/coreunix/add.go index 387a977784d8..24d8022fc484 100644 --- a/core/coreunix/add.go +++ b/core/coreunix/add.go @@ -24,6 +24,7 @@ import ( "github.com/ipfs/go-unixfs/importer/trickle" coreiface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/interface-go-ipfs-core/path" + "go.opentelemetry.io/otel" ) var log = logging.Logger("coreunix") @@ -158,20 +159,23 @@ func (adder *Adder) curRootNode() (ipld.Node, error) { // Recursively pins the root node of Adder and // writes the pin state to the backing datastore. -func (adder *Adder) PinRoot(root ipld.Node) error { +func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error { + ctx, span := otel.Tracer("").Start(ctx, "Adder-PinRoot") + defer span.End() + if !adder.Pin { return nil } rnk := root.Cid() - err := adder.dagService.Add(adder.ctx, root) + err := adder.dagService.Add(ctx, root) if err != nil { return err } if adder.tempRoot.Defined() { - err := adder.pinning.Unpin(adder.ctx, adder.tempRoot, true) + err := adder.pinning.Unpin(ctx, adder.tempRoot, true) if err != nil { return err } @@ -179,7 +183,7 @@ func (adder *Adder) PinRoot(root ipld.Node) error { } adder.pinning.PinWithMode(rnk, pin.Recursive) - return adder.pinning.Flush(adder.ctx) + return adder.pinning.Flush(ctx) } func (adder *Adder) outputDirs(path string, fsn mfs.FSNode) error { @@ -255,6 +259,9 @@ func (adder *Adder) addNode(node ipld.Node, path string) error { // AddAllAndPin adds the given request's files and pin them. func (adder *Adder) AddAllAndPin(ctx context.Context, file files.Node) (ipld.Node, error) { + ctx, span := otel.Tracer("").Start(ctx, "Adder-AddAllAndPin") + defer span.End() + if adder.Pin { adder.unlocker = adder.gcLocker.PinLock(ctx) } @@ -330,10 +337,13 @@ func (adder *Adder) AddAllAndPin(ctx context.Context, file files.Node) (ipld.Nod if !adder.Pin { return nd, nil } - return nd, adder.PinRoot(nd) + return nd, adder.PinRoot(ctx, nd) } func (adder *Adder) addFileNode(ctx context.Context, path string, file files.Node, toplevel bool) error { + ctx, span := otel.Tracer("").Start(ctx, "Adder-addFileNode") + defer span.End() + defer file.Close() err := adder.maybePauseForGC(ctx) @@ -436,13 +446,16 @@ func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory } func (adder *Adder) maybePauseForGC(ctx context.Context) error { + ctx, span := otel.Tracer("").Start(ctx, "Adder-maybePauseForGC") + defer span.End() + if adder.unlocker != nil && adder.gcLocker.GCRequested(ctx) { rn, err := adder.curRootNode() if err != nil { return err } - err = adder.PinRoot(rn) + err = adder.PinRoot(ctx, rn) if err != nil { return err } diff --git a/go.mod b/go.mod index c96e45e40aae..9d4f7f09518d 100644 --- a/go.mod +++ b/go.mod @@ -104,6 +104,14 @@ require ( github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 go.opencensus.io v0.23.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 + go.opentelemetry.io/otel v1.2.0 + go.opentelemetry.io/otel/exporters/jaeger v1.2.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 + go.opentelemetry.io/otel/sdk v1.2.0 + go.opentelemetry.io/otel/trace v1.2.0 go.uber.org/fx v1.13.1 go.uber.org/zap v1.19.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 diff --git a/go.sum b/go.sum index f2e60135d7ab..3efa82350362 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -133,7 +135,11 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -191,12 +197,15 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= @@ -294,8 +303,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -338,6 +348,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= @@ -1319,6 +1330,7 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1392,15 +1404,35 @@ 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.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 h1:0BgiNWjN7rUWO9HdjF4L12r8OW86QkVQcYmCjnayJLo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0/go.mod h1:bdvm3YpMxWAgEfQhtTBaVR8ceXPRuRBSQrvOBnIlHxc= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0 h1:C/5Egj3MJBXRJi22cSl07suqPqtZLnLFmH//OxETUEc= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 h1:xzbcGykysUh776gzD1LUPsNNHKWN0kQWDnJhn1ddUuk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0/go.mod h1:14T5gr+Y6s2AgHPqBMgnGwp04csUjQmYXFWPeiBoq5s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 h1:VsgsSCDwOSuO8eMVh63Cd4nACMqgjpmAeJSIvVNneD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0/go.mod h1:9mLBBnPRf3sf+ASVH2p9xREXVBvwib02FxcKnavtExg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0 h1:j/jXNzS6Dy0DFgO/oyCvin4H7vTQBg2Vdi6idIzWhCI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0/go.mod h1:k5GnE4m4Jyy2DNh6UAzG6Nml51nuqQyszV7O1ksQAnE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 h1:OiYdrCq1Ctwnovp6EofSPwlp5aGy4LgKNbkg7PtEUw8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0/go.mod h1:DUFCmFkXr0VtAHl5Zq2JRx24G6ze5CAq8YfdD36RdX8= +go.opentelemetry.io/otel/internal/metric v0.25.0 h1:w/7RXe16WdPylaIXDgcYM6t/q0K5lXgSdZOEbIEyliE= +go.opentelemetry.io/otel/internal/metric v0.25.0/go.mod h1:Nhuw26QSX7d6n4duoqAFi5KOQR4AuzyMcl5eXOgwxtc= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= +go.opentelemetry.io/otel/metric v0.25.0 h1:7cXOnCADUsR3+EOqxPaSKwhEuNu0gz/56dRN1hpIdKw= +go.opentelemetry.io/otel/metric v0.25.0/go.mod h1:E884FSpQfnJOMMUaq+05IWlJ4rjZpk2s/F1Ju+TEEm8= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= +go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.10.0 h1:n7brgtEbDvXEgGyKKo8SobKT1e9FewlDtXzkVP5djoE= +go.opentelemetry.io/proto/otlp v0.10.0/go.mod h1:zG20xCK0szZ1xdokeSOwEcmlXu+x9kkdRe6N1DhKcfU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1654,6 +1686,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1830,8 +1863,10 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/tracing/tracing.go b/tracing/tracing.go new file mode 100644 index 000000000000..a6cd40326134 --- /dev/null +++ b/tracing/tracing.go @@ -0,0 +1,127 @@ +package tracing + +import ( + "context" + "fmt" + "os" + "strconv" + + version "github.com/ipfs/go-ipfs" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + traceapi "go.opentelemetry.io/otel/trace" +) + +var _ trace.SpanExporter = &fileExporter{} + +// fileExporter wraps a file-writing exporter and closes the file when the exporter is shutdown. +type fileExporter struct { + file *os.File + writerExporter *stdouttrace.Exporter +} + +var exporterBuilders = map[string]func(context.Context, string) (trace.SpanExporter, error){ + "IPFS_TRACING_JAEGER": func(ctx context.Context, s string) (trace.SpanExporter, error) { + return jaeger.New(jaeger.WithCollectorEndpoint()) + }, + "IPFS_TRACING_FILE": func(ctx context.Context, s string) (trace.SpanExporter, error) { + return newFileExporter(s) + }, + "IPFS_TRACING_OTLP_HTTP": func(ctx context.Context, s string) (trace.SpanExporter, error) { + return otlptracehttp.New(ctx) + }, + "IPFS_TRACING_OTLP_GRPC": func(ctx context.Context, s string) (trace.SpanExporter, error) { + return otlptracegrpc.New(ctx) + }, +} + +func newFileExporter(file string) (*fileExporter, error) { + f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + return nil, fmt.Errorf("opening %s: %w", file, err) + } + stdoutExporter, err := stdouttrace.New(stdouttrace.WithWriter(f)) + if err != nil { + return nil, err + } + return &fileExporter{ + writerExporter: stdoutExporter, + file: f, + }, nil +} + +func (e *fileExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { + return e.writerExporter.ExportSpans(ctx, spans) +} +func (e *fileExporter) Shutdown(ctx context.Context) error { + if err := e.writerExporter.Shutdown(ctx); err != nil { + return err + } + if err := e.file.Close(); err != nil { + return fmt.Errorf("closing trace file: %w", err) + } + return nil +} + +type noopShutdownTracerProvider struct { + tp traceapi.TracerProvider +} + +func (n *noopShutdownTracerProvider) Shutdown(ctx context.Context) error { + return nil +} +func (n *noopShutdownTracerProvider) Tracer(instrumentationName string, opts ...traceapi.TracerOption) traceapi.Tracer { + return n.tp.Tracer(instrumentationName, opts...) +} + +type ShutdownTracerProvider interface { + traceapi.TracerProvider + Shutdown(ctx context.Context) error +} + +func NewTracerProvider(ctx context.Context) (ShutdownTracerProvider, error) { + if os.Getenv("IPFS_TRACING") == "" { + return &noopShutdownTracerProvider{tp: traceapi.NewNoopTracerProvider()}, nil + } + + options := []trace.TracerProviderOption{} + + traceRatio := 1.0 + if envRatio := os.Getenv("IPFS_TRACING_RATIO"); envRatio != "" { + r, err := strconv.ParseFloat(envRatio, 64) + if err == nil { + traceRatio = r + } + } + options = append(options, trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(traceRatio)))) + + r, err := resource.Merge( + resource.Default(), + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("go-ipfs"), + semconv.ServiceVersionKey.String(version.CurrentVersionNumber), + ), + ) + if err != nil { + return nil, err + } + options = append(options, trace.WithResource(r)) + + for envVar, builder := range exporterBuilders { + if val := os.Getenv(envVar); val != "" { + exporter, err := builder(ctx, val) + if err != nil { + return nil, err + } + options = append(options, trace.WithBatcher(exporter)) + } + } + + return trace.NewTracerProvider(options...), nil +}