@cryptotaxi247 / kubo / commits / f855bfe6e

feat: add basic gateway tracing (#8595)

* add deprecation warning when tracer plugins are loaded * add response format attribute to span in gateway handler * add note about tracing's experimental status in godoc * add nil check for TTL when adding name span attrs * add basic sharness test for integration with otel collector * add nil check in UnixFSAPI.processLink * test: sharness check all json objs for swarm span * add env var docs to docs/environment-variables.md * chore: pin the otel collector version * add tracing spans per response type (#8841) * docs: tracing with jaeger-ui Co-authored-by: Marcin Rataj <lidel@lidel.org>

Gus Eggert committed Apr 4, 2022 at 13:24 UTC f855bfe6ef8fe8a2633df889ce766cddc8d0effb
30 files changed +725 -53
cmd/ipfs/main.go
+18 -7
@@ -20,6 +20,8 @@ import (
20 loader "github.com/ipfs/go-ipfs/plugin/loader"
21 repo "github.com/ipfs/go-ipfs/repo"
22 fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
23 + "github.com/ipfs/go-ipfs/tracing"
24 + "go.opentelemetry.io/otel"
25
26 cmds "github.com/ipfs/go-ipfs-cmds"
27 "github.com/ipfs/go-ipfs-cmds/cli"
@@ -70,21 +72,30 @@ func main() {
72 os.Exit(mainRet())
73 }
74
73 -func mainRet() int {
75 +func printErr(err error) int {
76 + fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
77 + return 1
78 +}
79 +
80 +func mainRet() (exitCode int) {
81 rand.Seed(time.Now().UnixNano())
82 ctx := logging.ContextWithLoggable(context.Background(), loggables.Uuid("session"))
83 var err error
84
78 - // we'll call this local helper to output errors.
79 - // this is so we control how to print errors in one place.
80 - printErr := func(err error) {
81 - fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
85 + tp, err := tracing.NewTracerProvider(ctx)
86 + if err != nil {
87 + return printErr(err)
88 }
89 + defer func() {
90 + if err := tp.Shutdown(ctx); err != nil {
91 + exitCode = printErr(err)
92 + }
93 + }()
94 + otel.SetTracerProvider(tp)
95
96 stopFunc, err := profileIfEnabled()
97 if err != nil {
86 - printErr(err)
87 - return 1
98 + return printErr(err)
99 }
100 defer stopFunc() // to be executed as late as possible
101
core/coreapi/block.go
+14
@@ -13,8 +13,11 @@ import (
13 coreiface "github.com/ipfs/interface-go-ipfs-core"
14 caopts "github.com/ipfs/interface-go-ipfs-core/options"
15 path "github.com/ipfs/interface-go-ipfs-core/path"
16 + "go.opentelemetry.io/otel/attribute"
17 + "go.opentelemetry.io/otel/trace"
18
19 util "github.com/ipfs/go-ipfs/blocks/blockstoreutil"
20 + "github.com/ipfs/go-ipfs/tracing"
21 )
22
23 type BlockAPI CoreAPI
@@ -25,6 +28,9 @@ type BlockStat struct {
28 }
29
30 func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.BlockPutOption) (coreiface.BlockStat, error) {
31 + ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Put")
32 + defer span.End()
33 +
34 settings, pref, err := caopts.BlockPutOptions(opts...)
35 if err != nil {
36 return nil, err
@@ -65,6 +71,8 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc
71 }
72
73 func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) {
74 + ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Get", trace.WithAttributes(attribute.String("path", p.String())))
75 + defer span.End()
76 rp, err := api.core().ResolvePath(ctx, p)
77 if err != nil {
78 return nil, err
@@ -79,6 +87,9 @@ func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) {
87 }
88
89 func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRmOption) error {
90 + ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Rm", trace.WithAttributes(attribute.String("path", p.String())))
91 + defer span.End()
92 +
93 rp, err := api.core().ResolvePath(ctx, p)
94 if err != nil {
95 return err
@@ -119,6 +130,9 @@ func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRm
130 }
131
132 func (api *BlockAPI) Stat(ctx context.Context, p path.Path) (coreiface.BlockStat, error) {
133 + ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Stat", trace.WithAttributes(attribute.String("path", p.String())))
134 + defer span.End()
135 +
136 rp, err := api.core().ResolvePath(ctx, p)
137 if err != nil {
138 return nil, err
core/coreapi/dag.go
+7
@@ -5,8 +5,11 @@ import (
5
6 cid "github.com/ipfs/go-cid"
7 pin "github.com/ipfs/go-ipfs-pinner"
8 + "github.com/ipfs/go-ipfs/tracing"
9 ipld "github.com/ipfs/go-ipld-format"
10 dag "github.com/ipfs/go-merkledag"
11 + "go.opentelemetry.io/otel/attribute"
12 + "go.opentelemetry.io/otel/trace"
13 )
14
15 type dagAPI struct {
@@ -18,6 +21,8 @@ type dagAPI struct {
21 type pinningAdder CoreAPI
22
23 func (adder *pinningAdder) Add(ctx context.Context, nd ipld.Node) error {
24 + ctx, span := tracing.Span(ctx, "CoreAPI.PinningAdder", "Add", trace.WithAttributes(attribute.String("node", nd.String())))
25 + defer span.End()
26 defer adder.blockstore.PinLock(ctx).Unlock(ctx)
27
28 if err := adder.dag.Add(ctx, nd); err != nil {
@@ -30,6 +35,8 @@ func (adder *pinningAdder) Add(ctx context.Context, nd ipld.Node) error {
35 }
36
37 func (adder *pinningAdder) AddMany(ctx context.Context, nds []ipld.Node) error {
38 + ctx, span := tracing.Span(ctx, "CoreAPI.PinningAdder", "AddMany", trace.WithAttributes(attribute.Int("nodes.count", len(nds))))
39 + defer span.End()
40 defer adder.blockstore.PinLock(ctx).Unlock(ctx)
41
42 if err := adder.dag.AddMany(ctx, nds); err != nil {
core/coreapi/dht.go
+13
@@ -9,17 +9,22 @@ import (
9 cidutil "github.com/ipfs/go-cidutil"
10 blockstore "github.com/ipfs/go-ipfs-blockstore"
11 offline "github.com/ipfs/go-ipfs-exchange-offline"
12 + "github.com/ipfs/go-ipfs/tracing"
13 dag "github.com/ipfs/go-merkledag"
14 coreiface "github.com/ipfs/interface-go-ipfs-core"
15 caopts "github.com/ipfs/interface-go-ipfs-core/options"
16 path "github.com/ipfs/interface-go-ipfs-core/path"
17 peer "github.com/libp2p/go-libp2p-core/peer"
18 routing "github.com/libp2p/go-libp2p-core/routing"
19 + "go.opentelemetry.io/otel/attribute"
20 + "go.opentelemetry.io/otel/trace"
21 )
22
23 type DhtAPI CoreAPI
24
25 func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) {
26 + ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "FindPeer", trace.WithAttributes(attribute.String("peer", p.String())))
27 + defer span.End()
28 err := api.checkOnline(false)
29 if err != nil {
30 return peer.AddrInfo{}, err
@@ -34,10 +39,14 @@ func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, erro
39 }
40
41 func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopts.DhtFindProvidersOption) (<-chan peer.AddrInfo, error) {
42 + ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "FindProviders", trace.WithAttributes(attribute.String("path", p.String())))
43 + defer span.End()
44 +
45 settings, err := caopts.DhtFindProvidersOptions(opts...)
46 if err != nil {
47 return nil, err
48 }
49 + span.SetAttributes(attribute.Int("numproviders", settings.NumProviders))
50
51 err = api.checkOnline(false)
52 if err != nil {
@@ -59,10 +68,14 @@ func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopt
68 }
69
70 func (api *DhtAPI) Provide(ctx context.Context, path path.Path, opts ...caopts.DhtProvideOption) error {
71 + ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "Provide", trace.WithAttributes(attribute.String("path", path.String())))
72 + defer span.End()
73 +
74 settings, err := caopts.DhtProvideOptions(opts...)
75 if err != nil {
76 return err
77 }
78 + span.SetAttributes(attribute.Bool("recursive", settings.Recursive))
79
80 err = api.checkOnline(false)
81 if err != nil {
core/coreapi/key.go
+16
@@ -7,12 +7,15 @@ import (
7 "fmt"
8 "sort"
9
10 + "github.com/ipfs/go-ipfs/tracing"
11 ipfspath "github.com/ipfs/go-path"
12 coreiface "github.com/ipfs/interface-go-ipfs-core"
13 caopts "github.com/ipfs/interface-go-ipfs-core/options"
14 path "github.com/ipfs/interface-go-ipfs-core/path"
15 crypto "github.com/libp2p/go-libp2p-core/crypto"
16 peer "github.com/libp2p/go-libp2p-core/peer"
17 + "go.opentelemetry.io/otel/attribute"
18 + "go.opentelemetry.io/otel/trace"
19 )
20
21 type KeyAPI CoreAPI
@@ -40,6 +43,9 @@ func (k *key) ID() peer.ID {
43 // Generate generates new key, stores it in the keystore under the specified
44 // name and returns a base58 encoded multihash of its public key.
45 func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (coreiface.Key, error) {
46 + _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Generate", trace.WithAttributes(attribute.String("name", name)))
47 + defer span.End()
48 +
49 options, err := caopts.KeyGenerateOptions(opts...)
50 if err != nil {
51 return nil, err
@@ -97,6 +103,9 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key
103
104 // List returns a list keys stored in keystore.
105 func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) {
106 + _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "List")
107 + defer span.End()
108 +
109 keys, err := api.repo.Keystore().List()
110 if err != nil {
111 return nil, err
@@ -128,10 +137,14 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) {
137 // Rename renames `oldName` to `newName`. Returns the key and whether another
138 // key was overwritten, or an error.
139 func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, opts ...caopts.KeyRenameOption) (coreiface.Key, bool, error) {
140 + _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Rename", trace.WithAttributes(attribute.String("oldname", oldName), attribute.String("newname", newName)))
141 + defer span.End()
142 +
143 options, err := caopts.KeyRenameOptions(opts...)
144 if err != nil {
145 return nil, false, err
146 }
147 + span.SetAttributes(attribute.Bool("force", options.Force))
148
149 ks := api.repo.Keystore()
150
@@ -187,6 +200,9 @@ func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, o
200
201 // Remove removes keys from keystore. Returns ipns path of the removed key.
202 func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Key, error) {
203 + _, span := tracing.Span(ctx, "CoreAPI.KeyAPI", "Remove", trace.WithAttributes(attribute.String("name", name)))
204 + defer span.End()
205 +
206 ks := api.repo.Keystore()
207
208 if name == "self" {
core/coreapi/name.go
+23 -1
@@ -6,8 +6,11 @@ import (
6 "strings"
7 "time"
8
9 - "github.com/ipfs/go-ipfs-keystore"
9 + keystore "github.com/ipfs/go-ipfs-keystore"
10 + "github.com/ipfs/go-ipfs/tracing"
11 "github.com/ipfs/go-namesys"
12 + "go.opentelemetry.io/otel/attribute"
13 + "go.opentelemetry.io/otel/trace"
14
15 ipath "github.com/ipfs/go-path"
16 coreiface "github.com/ipfs/interface-go-ipfs-core"
@@ -36,6 +39,9 @@ func (e *ipnsEntry) Value() path.Path {
39
40 // Publish announces new IPNS name and returns the new IPNS entry.
41 func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (coreiface.IpnsEntry, error) {
42 + ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Publish", trace.WithAttributes(attribute.String("path", p.String())))
43 + defer span.End()
44 +
45 if err := api.checkPublishAllowed(); err != nil {
46 return nil, err
47 }
@@ -44,6 +50,14 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam
50 if err != nil {
51 return nil, err
52 }
53 + span.SetAttributes(
54 + attribute.Bool("allowoffline", options.AllowOffline),
55 + attribute.String("key", options.Key),
56 + attribute.Float64("validtime", options.ValidTime.Seconds()),
57 + )
58 + if options.TTL != nil {
59 + span.SetAttributes(attribute.Float64("ttl", options.TTL.Seconds()))
60 + }
61
62 err = api.checkOnline(options.AllowOffline)
63 if err != nil {
@@ -82,11 +96,16 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam
96 }
97
98 func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.NameResolveOption) (<-chan coreiface.IpnsResult, error) {
99 + ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Search", trace.WithAttributes(attribute.String("name", name)))
100 + defer span.End()
101 +
102 options, err := caopts.NameResolveOptions(opts...)
103 if err != nil {
104 return nil, err
105 }
106
107 + span.SetAttributes(attribute.Bool("cache", options.Cache))
108 +
109 err = api.checkOnline(true)
110 if err != nil {
111 return nil, err
@@ -124,6 +143,9 @@ func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.Name
143 // Resolve attempts to resolve the newest version of the specified name and
144 // returns its path.
145 func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.NameResolveOption) (path.Path, error) {
146 + ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Resolve", trace.WithAttributes(attribute.String("name", name)))
147 + defer span.End()
148 +
149 ctx, cancel := context.WithCancel(ctx)
150 defer cancel()
151
core/coreapi/object.go
+51
@@ -13,6 +13,7 @@ import (
13
14 cid "github.com/ipfs/go-cid"
15 pin "github.com/ipfs/go-ipfs-pinner"
16 + "github.com/ipfs/go-ipfs/tracing"
17 ipld "github.com/ipfs/go-ipld-format"
18 dag "github.com/ipfs/go-merkledag"
19 "github.com/ipfs/go-merkledag/dagutils"
@@ -20,6 +21,8 @@ import (
21 coreiface "github.com/ipfs/interface-go-ipfs-core"
22 caopts "github.com/ipfs/interface-go-ipfs-core/options"
23 ipath "github.com/ipfs/interface-go-ipfs-core/path"
24 + "go.opentelemetry.io/otel/attribute"
25 + "go.opentelemetry.io/otel/trace"
26 )
27
28 const inputLimit = 2 << 20
@@ -37,6 +40,9 @@ type Node struct {
40 }
41
42 func (api *ObjectAPI) New(ctx context.Context, opts ...caopts.ObjectNewOption) (ipld.Node, error) {
43 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "New")
44 + defer span.End()
45 +
46 options, err := caopts.ObjectNewOptions(opts...)
47 if err != nil {
48 return nil, err
@@ -60,10 +66,18 @@ func (api *ObjectAPI) New(ctx context.Context, opts ...caopts.ObjectNewOption) (
66 }
67
68 func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.ObjectPutOption) (ipath.Resolved, error) {
69 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Put")
70 + defer span.End()
71 +
72 options, err := caopts.ObjectPutOptions(opts...)
73 if err != nil {
74 return nil, err
75 }
76 + span.SetAttributes(
77 + attribute.Bool("pin", options.Pin),
78 + attribute.String("datatype", options.DataType),
79 + attribute.String("inputenc", options.InputEnc),
80 + )
81
82 data, err := ioutil.ReadAll(io.LimitReader(src, inputLimit+10))
83 if err != nil {
@@ -130,10 +144,15 @@ func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Obj
144 }
145
146 func (api *ObjectAPI) Get(ctx context.Context, path ipath.Path) (ipld.Node, error) {
147 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Get", trace.WithAttributes(attribute.String("path", path.String())))
148 + defer span.End()
149 return api.core().ResolveNode(ctx, path)
150 }
151
152 func (api *ObjectAPI) Data(ctx context.Context, path ipath.Path) (io.Reader, error) {
153 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Data", trace.WithAttributes(attribute.String("path", path.String())))
154 + defer span.End()
155 +
156 nd, err := api.core().ResolveNode(ctx, path)
157 if err != nil {
158 return nil, err
@@ -148,6 +167,9 @@ func (api *ObjectAPI) Data(ctx context.Context, path ipath.Path) (io.Reader, err
167 }
168
169 func (api *ObjectAPI) Links(ctx context.Context, path ipath.Path) ([]*ipld.Link, error) {
170 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Links", trace.WithAttributes(attribute.String("path", path.String())))
171 + defer span.End()
172 +
173 nd, err := api.core().ResolveNode(ctx, path)
174 if err != nil {
175 return nil, err
@@ -163,6 +185,9 @@ func (api *ObjectAPI) Links(ctx context.Context, path ipath.Path) ([]*ipld.Link,
185 }
186
187 func (api *ObjectAPI) Stat(ctx context.Context, path ipath.Path) (*coreiface.ObjectStat, error) {
188 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Stat", trace.WithAttributes(attribute.String("path", path.String())))
189 + defer span.End()
190 +
191 nd, err := api.core().ResolveNode(ctx, path)
192 if err != nil {
193 return nil, err
@@ -186,10 +211,18 @@ func (api *ObjectAPI) Stat(ctx context.Context, path ipath.Path) (*coreiface.Obj
211 }
212
213 func (api *ObjectAPI) AddLink(ctx context.Context, base ipath.Path, name string, child ipath.Path, opts ...caopts.ObjectAddLinkOption) (ipath.Resolved, error) {
214 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "AddLink", trace.WithAttributes(
215 + attribute.String("base", base.String()),
216 + attribute.String("name", name),
217 + attribute.String("child", child.String()),
218 + ))
219 + defer span.End()
220 +
221 options, err := caopts.ObjectAddLinkOptions(opts...)
222 if err != nil {
223 return nil, err
224 }
225 + span.SetAttributes(attribute.Bool("create", options.Create))
226
227 baseNd, err := api.core().ResolveNode(ctx, base)
228 if err != nil {
@@ -227,6 +260,12 @@ func (api *ObjectAPI) AddLink(ctx context.Context, base ipath.Path, name string,
260 }
261
262 func (api *ObjectAPI) RmLink(ctx context.Context, base ipath.Path, link string) (ipath.Resolved, error) {
263 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "RmLink", trace.WithAttributes(
264 + attribute.String("base", base.String()),
265 + attribute.String("link", link)),
266 + )
267 + defer span.End()
268 +
269 baseNd, err := api.core().ResolveNode(ctx, base)
270 if err != nil {
271 return nil, err
@@ -253,10 +292,16 @@ func (api *ObjectAPI) RmLink(ctx context.Context, base ipath.Path, link string)
292 }
293
294 func (api *ObjectAPI) AppendData(ctx context.Context, path ipath.Path, r io.Reader) (ipath.Resolved, error) {
295 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "AppendData", trace.WithAttributes(attribute.String("path", path.String())))
296 + defer span.End()
297 +
298 return api.patchData(ctx, path, r, true)
299 }
300
301 func (api *ObjectAPI) SetData(ctx context.Context, path ipath.Path, r io.Reader) (ipath.Resolved, error) {
302 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "SetData", trace.WithAttributes(attribute.String("path", path.String())))
303 + defer span.End()
304 +
305 return api.patchData(ctx, path, r, false)
306 }
307
@@ -290,6 +335,12 @@ func (api *ObjectAPI) patchData(ctx context.Context, path ipath.Path, r io.Reade
335 }
336
337 func (api *ObjectAPI) Diff(ctx context.Context, before ipath.Path, after ipath.Path) ([]coreiface.ObjectChange, error) {
338 + ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Diff", trace.WithAttributes(
339 + attribute.String("before", before.String()),
340 + attribute.String("after", after.String()),
341 + ))
342 + defer span.End()
343 +
344 beforeNd, err := api.core().ResolveNode(ctx, before)
345 if err != nil {
346 return nil, err
core/coreapi/path.go
+10
@@ -5,8 +5,12 @@ import (
5 "fmt"
6 gopath "path"
7
8 + "github.com/ipfs/go-ipfs/tracing"
9 "github.com/ipfs/go-namesys/resolve"
10
11 + "go.opentelemetry.io/otel/attribute"
12 + "go.opentelemetry.io/otel/trace"
13 +
14 "github.com/ipfs/go-cid"
15 "github.com/ipfs/go-fetcher"
16 ipld "github.com/ipfs/go-ipld-format"
@@ -19,6 +23,9 @@ import (
23 // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the
24 // resolved Node.
25 func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, error) {
26 + ctx, span := tracing.Span(ctx, "CoreAPI", "ResolveNode", trace.WithAttributes(attribute.String("path", p.String())))
27 + defer span.End()
28 +
29 rp, err := api.ResolvePath(ctx, p)
30 if err != nil {
31 return nil, err
@@ -34,6 +41,9 @@ func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, er
41 // ResolvePath resolves the path `p` using Unixfs resolver, returns the
42 // resolved path.
43 func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.Resolved, error) {
44 + ctx, span := tracing.Span(ctx, "CoreAPI", "ResolvePath", trace.WithAttributes(attribute.String("path", p.String())))
45 + defer span.End()
46 +
47 if _, ok := p.(path.Resolved); ok {
48 return p.(path.Resolved), nil
49 }
core/coreapi/pin.go
+37
@@ -8,15 +8,21 @@ import (
8 "github.com/ipfs/go-cid"
9 offline "github.com/ipfs/go-ipfs-exchange-offline"
10 pin "github.com/ipfs/go-ipfs-pinner"
11 + "github.com/ipfs/go-ipfs/tracing"
12 "github.com/ipfs/go-merkledag"
13 coreiface "github.com/ipfs/interface-go-ipfs-core"
14 caopts "github.com/ipfs/interface-go-ipfs-core/options"
15 "github.com/ipfs/interface-go-ipfs-core/path"
16 + "go.opentelemetry.io/otel/attribute"
17 + "go.opentelemetry.io/otel/trace"
18 )
19
20 type PinAPI CoreAPI
21
22 func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOption) error {
23 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Add", trace.WithAttributes(attribute.String("path", p.String())))
24 + defer span.End()
25 +
26 dagNode, err := api.core().ResolveNode(ctx, p)
27 if err != nil {
28 return fmt.Errorf("pin: %s", err)
@@ -27,6 +33,8 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
33 return err
34 }
35
36 + span.SetAttributes(attribute.Bool("recursive", settings.Recursive))
37 +
38 defer api.blockstore.PinLock(ctx).Unlock(ctx)
39
40 err = api.pinning.Pin(ctx, dagNode, settings.Recursive)
@@ -42,11 +50,16 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp
50 }
51
52 func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan coreiface.Pin, error) {
53 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Ls")
54 + defer span.End()
55 +
56 settings, err := caopts.PinLsOptions(opts...)
57 if err != nil {
58 return nil, err
59 }
60
61 + span.SetAttributes(attribute.String("type", settings.Type))
62 +
63 switch settings.Type {
64 case "all", "direct", "indirect", "recursive":
65 default:
@@ -57,6 +70,9 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan c
70 }
71
72 func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) {
73 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "IsPinned", trace.WithAttributes(attribute.String("path", p.String())))
74 + defer span.End()
75 +
76 resolved, err := api.core().ResolvePath(ctx, p)
77 if err != nil {
78 return "", false, fmt.Errorf("error resolving path: %s", err)
@@ -67,6 +83,8 @@ func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.Pin
83 return "", false, err
84 }
85
86 + span.SetAttributes(attribute.String("withtype", settings.WithType))
87 +
88 mode, ok := pin.StringToMode(settings.WithType)
89 if !ok {
90 return "", false, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.WithType)
@@ -77,6 +95,9 @@ func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.Pin
95
96 // Rm pin rm api
97 func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOption) error {
98 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Rm", trace.WithAttributes(attribute.String("path", p.String())))
99 + defer span.End()
100 +
101 rp, err := api.core().ResolvePath(ctx, p)
102 if err != nil {
103 return err
@@ -87,6 +108,8 @@ func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOpti
108 return err
109 }
110
111 + span.SetAttributes(attribute.Bool("recursive", settings.Recursive))
112 +
113 // Note: after unpin the pin sets are flushed to the blockstore, so we need
114 // to take a lock to prevent a concurrent garbage collection
115 defer api.blockstore.PinLock(ctx).Unlock(ctx)
@@ -99,11 +122,19 @@ func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOpti
122 }
123
124 func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opts ...caopts.PinUpdateOption) error {
125 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Update", trace.WithAttributes(
126 + attribute.String("from", from.String()),
127 + attribute.String("to", to.String()),
128 + ))
129 + defer span.End()
130 +
131 settings, err := caopts.PinUpdateOptions(opts...)
132 if err != nil {
133 return err
134 }
135
136 + span.SetAttributes(attribute.Bool("unpin", settings.Unpin))
137 +
138 fp, err := api.core().ResolvePath(ctx, from)
139 if err != nil {
140 return err
@@ -153,6 +184,9 @@ func (n *badNode) Err() error {
184 }
185
186 func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, error) {
187 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Verify")
188 + defer span.End()
189 +
190 visited := make(map[cid.Cid]*pinStatus)
191 bs := api.blockstore
192 DAG := merkledag.NewDAGService(bserv.New(bs, offline.Exchange(bs)))
@@ -164,6 +198,9 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro
198
199 var checkPin func(root cid.Cid) *pinStatus
200 checkPin = func(root cid.Cid) *pinStatus {
201 + ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Verify.CheckPin", trace.WithAttributes(attribute.String("cid", root.String())))
202 + defer span.End()
203 +
204 if status, ok := visited[root]; ok {
205 return status
206 }
core/coreapi/provider.go deleted
-13
@@ -1,13 +0,0 @@
1 -package coreapi
2 -
3 -import (
4 - cid "github.com/ipfs/go-cid"
5 -)
6 -
7 -// ProviderAPI brings Provider behavior to CoreAPI
8 -type ProviderAPI CoreAPI
9 -
10 -// Provide the given cid using the current provider
11 -func (api *ProviderAPI) Provide(cid cid.Cid) error {
12 - return api.provider.Provide(cid)
13 -}
core/coreapi/pubsub.go
+20
@@ -4,11 +4,14 @@ import (
4 "context"
5 "errors"
6
7 + "github.com/ipfs/go-ipfs/tracing"
8 coreiface "github.com/ipfs/interface-go-ipfs-core"
9 caopts "github.com/ipfs/interface-go-ipfs-core/options"
10 peer "github.com/libp2p/go-libp2p-core/peer"
11 routing "github.com/libp2p/go-libp2p-core/routing"
12 pubsub "github.com/libp2p/go-libp2p-pubsub"
13 + "go.opentelemetry.io/otel/attribute"
14 + "go.opentelemetry.io/otel/trace"
15 )
16
17 type PubSubAPI CoreAPI
@@ -22,6 +25,9 @@ type pubSubMessage struct {
25 }
26
27 func (api *PubSubAPI) Ls(ctx context.Context) ([]string, error) {
28 + _, span := tracing.Span(ctx, "CoreAPI.PubSubAPI", "Ls")
29 + defer span.End()
30 +
31 _, err := api.checkNode()
32 if err != nil {
33 return nil, err
@@ -31,6 +37,9 @@ func (api *PubSubAPI) Ls(ctx context.Context) ([]string, error) {
37 }
38
39 func (api *PubSubAPI) Peers(ctx context.Context, opts ...caopts.PubSubPeersOption) ([]peer.ID, error) {
40 + _, span := tracing.Span(ctx, "CoreAPI.PubSubAPI", "Peers")
41 + defer span.End()
42 +
43 _, err := api.checkNode()
44 if err != nil {
45 return nil, err
@@ -41,10 +50,15 @@ func (api *PubSubAPI) Peers(ctx context.Context, opts ...caopts.PubSubPeersOptio
50 return nil, err
51 }
52
53 + span.SetAttributes(attribute.String("topic", settings.Topic))
54 +
55 return api.pubSub.ListPeers(settings.Topic), nil
56 }
57
58 func (api *PubSubAPI) Publish(ctx context.Context, topic string, data []byte) error {
59 + _, span := tracing.Span(ctx, "CoreAPI.PubSubAPI", "Publish", trace.WithAttributes(attribute.String("topic", topic)))
60 + defer span.End()
61 +
62 _, err := api.checkNode()
63 if err != nil {
64 return err
@@ -55,6 +69,9 @@ func (api *PubSubAPI) Publish(ctx context.Context, topic string, data []byte) er
69 }
70
71 func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopts.PubSubSubscribeOption) (coreiface.PubSubSubscription, error) {
72 + _, span := tracing.Span(ctx, "CoreAPI.PubSubAPI", "Subscribe", trace.WithAttributes(attribute.String("topic", topic)))
73 + defer span.End()
74 +
75 // Parse the options to avoid introducing silent failures for invalid
76 // options. However, we don't currently have any use for them. The only
77 // subscription option, discovery, is now a no-op as it's handled by
@@ -97,6 +114,9 @@ func (sub *pubSubSubscription) Close() error {
114 }
115
116 func (sub *pubSubSubscription) Next(ctx context.Context) (coreiface.PubSubMessage, error) {
117 + ctx, span := tracing.Span(ctx, "CoreAPI.PubSubSubscription", "Next")
118 + defer span.End()
119 +
120 msg, err := sub.subscription.Next(ctx)
121 if err != nil {
122 return nil, err
core/coreapi/swarm.go
+27 -4
@@ -5,6 +5,7 @@ import (
5 "sort"
6 "time"
7
8 + "github.com/ipfs/go-ipfs/tracing"
9 coreiface "github.com/ipfs/interface-go-ipfs-core"
10 inet "github.com/libp2p/go-libp2p-core/network"
11 peer "github.com/libp2p/go-libp2p-core/peer"
@@ -12,6 +13,8 @@ import (
13 protocol "github.com/libp2p/go-libp2p-core/protocol"
14 swarm "github.com/libp2p/go-libp2p-swarm"
15 ma "github.com/multiformats/go-multiaddr"
16 + "go.opentelemetry.io/otel/attribute"
17 + "go.opentelemetry.io/otel/trace"
18 )
19
20 type SwarmAPI CoreAPI
@@ -30,6 +33,9 @@ const connectionManagerTag = "user-connect"
33 const connectionManagerWeight = 100
34
35 func (api *SwarmAPI) Connect(ctx context.Context, pi peer.AddrInfo) error {
36 + ctx, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "Connect", trace.WithAttributes(attribute.String("peerid", pi.ID.String())))
37 + defer span.End()
38 +
39 if api.peerHost == nil {
40 return coreiface.ErrOffline
41 }
@@ -47,6 +53,9 @@ func (api *SwarmAPI) Connect(ctx context.Context, pi peer.AddrInfo) error {
53 }
54
55 func (api *SwarmAPI) Disconnect(ctx context.Context, addr ma.Multiaddr) error {
56 + _, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "Disconnect", trace.WithAttributes(attribute.String("addr", addr.String())))
57 + defer span.End()
58 +
59 if api.peerHost == nil {
60 return coreiface.ErrOffline
61 }
@@ -56,6 +65,8 @@ func (api *SwarmAPI) Disconnect(ctx context.Context, addr ma.Multiaddr) error {
65 return peer.ErrInvalidAddr
66 }
67
68 + span.SetAttributes(attribute.String("peerid", id.String()))
69 +
70 net := api.peerHost.Network()
71 if taddr == nil {
72 if net.Connectedness(id) != inet.Connected {
@@ -76,7 +87,10 @@ func (api *SwarmAPI) Disconnect(ctx context.Context, addr ma.Multiaddr) error {
87 return coreiface.ErrConnNotFound
88 }
89
79 -func (api *SwarmAPI) KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) {
90 +func (api *SwarmAPI) KnownAddrs(ctx context.Context) (map[peer.ID][]ma.Multiaddr, error) {
91 + _, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "KnownAddrs")
92 + defer span.End()
93 +
94 if api.peerHost == nil {
95 return nil, coreiface.ErrOffline
96 }
@@ -93,7 +107,10 @@ func (api *SwarmAPI) KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, er
107 return addrs, nil
108 }
109
96 -func (api *SwarmAPI) LocalAddrs(context.Context) ([]ma.Multiaddr, error) {
110 +func (api *SwarmAPI) LocalAddrs(ctx context.Context) ([]ma.Multiaddr, error) {
111 + _, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "LocalAddrs")
112 + defer span.End()
113 +
114 if api.peerHost == nil {
115 return nil, coreiface.ErrOffline
116 }
@@ -101,7 +118,10 @@ func (api *SwarmAPI) LocalAddrs(context.Context) ([]ma.Multiaddr, error) {
118 return api.peerHost.Addrs(), nil
119 }
120
104 -func (api *SwarmAPI) ListenAddrs(context.Context) ([]ma.Multiaddr, error) {
121 +func (api *SwarmAPI) ListenAddrs(ctx context.Context) ([]ma.Multiaddr, error) {
122 + _, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "ListenAddrs")
123 + defer span.End()
124 +
125 if api.peerHost == nil {
126 return nil, coreiface.ErrOffline
127 }
@@ -109,7 +129,10 @@ func (api *SwarmAPI) ListenAddrs(context.Context) ([]ma.Multiaddr, error) {
129 return api.peerHost.Network().InterfaceListenAddresses()
130 }
131
112 -func (api *SwarmAPI) Peers(context.Context) ([]coreiface.ConnectionInfo, error) {
132 +func (api *SwarmAPI) Peers(ctx context.Context) ([]coreiface.ConnectionInfo, error) {
133 + _, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "Peers")
134 + defer span.End()
135 +
136 if api.peerHost == nil {
137 return nil, coreiface.ErrOffline
138 }
core/coreapi/unixfs.go
+37
@@ -6,6 +6,9 @@ import (
6 "sync"
7
8 "github.com/ipfs/go-ipfs/core"
9 + "github.com/ipfs/go-ipfs/tracing"
10 + "go.opentelemetry.io/otel/attribute"
11 + "go.opentelemetry.io/otel/trace"
12
13 "github.com/ipfs/go-ipfs/core/coreunix"
14
@@ -55,11 +58,30 @@ func getOrCreateNilNode() (*core.IpfsNode, error) {
58 // Add builds a merkledag node from a reader, adds it to the blockstore,
59 // and returns the key representing that node.
60 func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options.UnixfsAddOption) (path.Resolved, error) {
61 + ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "Add")
62 + defer span.End()
63 +
64 settings, prefix, err := options.UnixfsAddOptions(opts...)
65 if err != nil {
66 return nil, err
67 }
68
69 + span.SetAttributes(
70 + attribute.String("chunker", settings.Chunker),
71 + attribute.Int("cidversion", settings.CidVersion),
72 + attribute.Bool("inline", settings.Inline),
73 + attribute.Int("inlinelimit", settings.InlineLimit),
74 + attribute.Bool("rawleaves", settings.RawLeaves),
75 + attribute.Bool("rawleavesset", settings.RawLeavesSet),
76 + attribute.Int("layout", int(settings.Layout)),
77 + attribute.Bool("pin", settings.Pin),
78 + attribute.Bool("onlyhash", settings.OnlyHash),
79 + attribute.Bool("fscache", settings.FsCache),
80 + attribute.Bool("nocopy", settings.NoCopy),
81 + attribute.Bool("silent", settings.Silent),
82 + attribute.Bool("progress", settings.Progress),
83 + )
84 +
85 cfg, err := api.repo.Config()
86 if err != nil {
87 return nil, err
@@ -179,6 +201,9 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options
201 }
202
203 func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error) {
204 + ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "Get", trace.WithAttributes(attribute.String("path", p.String())))
205 + defer span.End()
206 +
207 ses := api.core().getSession(ctx)
208
209 nd, err := ses.ResolveNode(ctx, p)
@@ -192,11 +217,16 @@ func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error)
217 // Ls returns the contents of an IPFS or IPNS object(s) at path p, with the format:
218 // `<link base58 hash> <link size in bytes> <link name>`
219 func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...options.UnixfsLsOption) (<-chan coreiface.DirEntry, error) {
220 + ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "Ls", trace.WithAttributes(attribute.String("path", p.String())))
221 + defer span.End()
222 +
223 settings, err := options.UnixfsLsOptions(opts...)
224 if err != nil {
225 return nil, err
226 }
227
228 + span.SetAttributes(attribute.Bool("resolvechildren", settings.ResolveChildren))
229 +
230 ses := api.core().getSession(ctx)
231 uses := (*UnixfsAPI)(ses)
232
@@ -217,6 +247,13 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...options.Unixf
247 }
248
249 func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, settings *options.UnixfsLsSettings) coreiface.DirEntry {
250 + ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "ProcessLink")
251 + defer span.End()
252 + if linkres.Link != nil {
253 + span.SetAttributes(attribute.String("linkname", linkres.Link.Name), attribute.String("cid", linkres.Link.Cid.String()))
254 +
255 + }
256 +
257 if linkres.Err != nil {
258 return coreiface.DirEntry{Err: linkres.Err}
259 }
core/corehttp/gateway.go
+4 -1
@@ -9,6 +9,7 @@ import (
9 version "github.com/ipfs/go-ipfs"
10 core "github.com/ipfs/go-ipfs/core"
11 coreapi "github.com/ipfs/go-ipfs/core/coreapi"
12 + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
13
14 options "github.com/ipfs/interface-go-ipfs-core/options"
15 id "github.com/libp2p/go-libp2p/p2p/protocol/identify"
@@ -87,12 +88,14 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
88 "X-Stream-Output",
89 }, headers[ACEHeadersName]...))
90
90 - gateway := newGatewayHandler(GatewayConfig{
91 + var gateway http.Handler = newGatewayHandler(GatewayConfig{
92 Headers: headers,
93 Writable: writable,
94 PathPrefixes: cfg.Gateway.PathPrefixes,
95 }, api)
96
97 + gateway = otelhttp.NewHandler(gateway, "Gateway.Request")
98 +
99 for _, p := range paths {
100 mux.Handle(p+"/", gateway)
101 }
core/corehttp/gateway_handler.go
+6 -2
@@ -26,6 +26,8 @@ import (
26 ipath "github.com/ipfs/interface-go-ipfs-core/path"
27 routing "github.com/libp2p/go-libp2p-core/routing"
28 prometheus "github.com/prometheus/client_golang/prometheus"
29 + "go.opentelemetry.io/otel/attribute"
30 + "go.opentelemetry.io/otel/trace"
31 )
32
33 const (
@@ -354,6 +356,8 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
356 webError(w, "error while processing the Accept header", err, http.StatusBadRequest)
357 return
358 }
359 + trace.SpanFromContext(r.Context()).SetAttributes(attribute.String("ResponseFormat", responseFormat))
360 + trace.SpanFromContext(r.Context()).SetAttributes(attribute.String("ResolvedPath", resolvedPath.String()))
361
362 // Finish early if client already has matching Etag
363 if r.Header.Get("If-None-Match") == getEtag(r, resolvedPath.Cid()) {
@@ -392,12 +396,12 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
396 return
397 case "application/vnd.ipld.raw":
398 logger.Debugw("serving raw block", "path", contentPath)
395 - i.serveRawBlock(w, r, resolvedPath.Cid(), contentPath, begin)
399 + i.serveRawBlock(w, r, resolvedPath, contentPath, begin)
400 return
401 case "application/vnd.ipld.car":
402 logger.Debugw("serving car stream", "path", contentPath)
403 carVersion := formatParams["version"]
400 - i.serveCar(w, r, resolvedPath.Cid(), contentPath, carVersion, begin)
404 + i.serveCar(w, r, resolvedPath, contentPath, carVersion, begin)
405 return
406 default: // catch-all for unsuported application/vnd.*
407 err := fmt.Errorf("unsupported format %q", responseFormat)
core/corehttp/gateway_handler_block.go
+8 -3
@@ -6,13 +6,18 @@ import (
6 "net/http"
7 "time"
8
9 - cid "github.com/ipfs/go-cid"
9 + "github.com/ipfs/go-ipfs/tracing"
10 ipath "github.com/ipfs/interface-go-ipfs-core/path"
11 + "go.opentelemetry.io/otel/attribute"
12 + "go.opentelemetry.io/otel/trace"
13 )
14
15 // serveRawBlock returns bytes behind a raw block
14 -func (i *gatewayHandler) serveRawBlock(w http.ResponseWriter, r *http.Request, blockCid cid.Cid, contentPath ipath.Path, begin time.Time) {
15 - blockReader, err := i.api.Block().Get(r.Context(), contentPath)
16 +func (i *gatewayHandler) serveRawBlock(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time) {
17 + ctx, span := tracing.Span(r.Context(), "Gateway", "ServeRawBlock", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
18 + defer span.End()
19 + blockCid := resolvedPath.Cid()
20 + blockReader, err := i.api.Block().Get(ctx, resolvedPath)
21 if err != nil {
22 webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError)
23 return
core/corehttp/gateway_handler_car.go
+8 -2
@@ -8,15 +8,20 @@ import (
8
9 blocks "github.com/ipfs/go-block-format"
10 cid "github.com/ipfs/go-cid"
11 + "github.com/ipfs/go-ipfs/tracing"
12 coreiface "github.com/ipfs/interface-go-ipfs-core"
13 ipath "github.com/ipfs/interface-go-ipfs-core/path"
14 gocar "github.com/ipld/go-car"
15 selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse"
16 + "go.opentelemetry.io/otel/attribute"
17 + "go.opentelemetry.io/otel/trace"
18 )
19
20 // serveCar returns a CAR stream for specific DAG+selector
18 -func (i *gatewayHandler) serveCar(w http.ResponseWriter, r *http.Request, rootCid cid.Cid, contentPath ipath.Path, carVersion string, begin time.Time) {
19 - ctx, cancel := context.WithCancel(r.Context())
21 +func (i *gatewayHandler) serveCar(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, carVersion string, begin time.Time) {
22 + ctx, span := tracing.Span(r.Context(), "Gateway", "ServeCar", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
23 + defer span.End()
24 + ctx, cancel := context.WithCancel(ctx)
25 defer cancel()
26
27 switch carVersion {
@@ -27,6 +32,7 @@ func (i *gatewayHandler) serveCar(w http.ResponseWriter, r *http.Request, rootCi
32 webError(w, "unsupported CAR version", err, http.StatusBadRequest)
33 return
34 }
35 + rootCid := resolvedPath.Cid()
36
37 // Set Content-Disposition
38 name := rootCid.String() + ".car"
core/corehttp/gateway_handler_unixfs.go
+7 -2
@@ -7,13 +7,18 @@ import (
7 "time"
8
9 files "github.com/ipfs/go-ipfs-files"
10 + "github.com/ipfs/go-ipfs/tracing"
11 ipath "github.com/ipfs/interface-go-ipfs-core/path"
12 + "go.opentelemetry.io/otel/attribute"
13 + "go.opentelemetry.io/otel/trace"
14 "go.uber.org/zap"
15 )
16
17 func (i *gatewayHandler) serveUnixFs(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) {
18 + ctx, span := tracing.Span(r.Context(), "Gateway", "ServeUnixFs", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
19 + defer span.End()
20 // Handling UnixFS
16 - dr, err := i.api.Unixfs().Get(r.Context(), resolvedPath)
21 + dr, err := i.api.Unixfs().Get(ctx, resolvedPath)
22 if err != nil {
23 webError(w, "ipfs cat "+html.EscapeString(contentPath.String()), err, http.StatusNotFound)
24 return
@@ -23,7 +28,7 @@ func (i *gatewayHandler) serveUnixFs(w http.ResponseWriter, r *http.Request, res
28 // Handling Unixfs file
29 if f, ok := dr.(files.File); ok {
30 logger.Debugw("serving unixfs file", "path", contentPath)
26 - i.serveFile(w, r, contentPath, resolvedPath.Cid(), f, begin)
31 + i.serveFile(w, r, resolvedPath, contentPath, f, begin)
32 return
33 }
34
core/corehttp/gateway_handler_unixfs_dir.go
+8 -3
@@ -10,9 +10,12 @@ import (
10 "github.com/dustin/go-humanize"
11 files "github.com/ipfs/go-ipfs-files"
12 "github.com/ipfs/go-ipfs/assets"
13 + "github.com/ipfs/go-ipfs/tracing"
14 path "github.com/ipfs/go-path"
15 "github.com/ipfs/go-path/resolver"
16 ipath "github.com/ipfs/interface-go-ipfs-core/path"
17 + "go.opentelemetry.io/otel/attribute"
18 + "go.opentelemetry.io/otel/trace"
19 "go.uber.org/zap"
20 )
21
@@ -20,6 +23,8 @@ import (
23 //
24 // It will return index.html if present, or generate directory listing otherwise.
25 func (i *gatewayHandler) serveDirectory(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, dir files.Directory, begin time.Time, logger *zap.SugaredLogger) {
26 + ctx, span := tracing.Span(r.Context(), "Gateway", "ServeDirectory", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
27 + defer span.End()
28
29 // HostnameOption might have constructed an IPNS/IPFS path using the Host header.
30 // In this case, we need the original path for constructing redirects
@@ -35,7 +40,7 @@ func (i *gatewayHandler) serveDirectory(w http.ResponseWriter, r *http.Request,
40
41 // Check if directory has index.html, if so, serveFile
42 idxPath := ipath.Join(resolvedPath, "index.html")
38 - idx, err := i.api.Unixfs().Get(r.Context(), idxPath)
43 + idx, err := i.api.Unixfs().Get(ctx, idxPath)
44 switch err.(type) {
45 case nil:
46 cpath := contentPath.String()
@@ -63,7 +68,7 @@ func (i *gatewayHandler) serveDirectory(w http.ResponseWriter, r *http.Request,
68
69 logger.Debugw("serving index.html file", "path", idxPath)
70 // write to request
66 - i.serveFile(w, r, idxPath, resolvedPath.Cid(), f, begin)
71 + i.serveFile(w, r, resolvedPath, idxPath, f, begin)
72 return
73 case resolver.ErrNoLink:
74 logger.Debugw("no index.html; noop", "path", idxPath)
@@ -111,7 +116,7 @@ func (i *gatewayHandler) serveDirectory(w http.ResponseWriter, r *http.Request,
116 size = humanize.Bytes(uint64(s))
117 }
118
114 - resolved, err := i.api.ResolvePath(r.Context(), ipath.Join(resolvedPath, dirit.Name()))
119 + resolved, err := i.api.ResolvePath(ctx, ipath.Join(resolvedPath, dirit.Name()))
120 if err != nil {
121 internalWebError(w, err)
122 return
core/corehttp/gateway_handler_unixfs_file.go
+7 -3
@@ -10,17 +10,21 @@ import (
10 "time"
11
12 "github.com/gabriel-vasile/mimetype"
13 - cid "github.com/ipfs/go-cid"
13 files "github.com/ipfs/go-ipfs-files"
14 + "github.com/ipfs/go-ipfs/tracing"
15 ipath "github.com/ipfs/interface-go-ipfs-core/path"
16 + "go.opentelemetry.io/otel/attribute"
17 + "go.opentelemetry.io/otel/trace"
18 )
19
20 // serveFile returns data behind a file along with HTTP headers based on
21 // the file itself, its CID and the contentPath used for accessing it.
20 -func (i *gatewayHandler) serveFile(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, fileCid cid.Cid, file files.File, begin time.Time) {
22 +func (i *gatewayHandler) serveFile(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, file files.File, begin time.Time) {
23 + _, span := tracing.Span(r.Context(), "Gateway", "ServeFile", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
24 + defer span.End()
25
26 // Set Cache-Control and read optional Last-Modified time
23 - modtime := addCacheControlHeaders(w, r, contentPath, fileCid)
27 + modtime := addCacheControlHeaders(w, r, contentPath, resolvedPath.Cid())
28
29 // Set Content-Disposition
30 name := addContentDispositionHeader(w, r, contentPath)
core/coreunix/add.go
+19 -6
@@ -14,6 +14,7 @@ import (
14 files "github.com/ipfs/go-ipfs-files"
15 pin "github.com/ipfs/go-ipfs-pinner"
16 posinfo "github.com/ipfs/go-ipfs-posinfo"
17 + "github.com/ipfs/go-ipfs/tracing"
18 ipld "github.com/ipfs/go-ipld-format"
19 logging "github.com/ipfs/go-log"
20 dag "github.com/ipfs/go-merkledag"
@@ -158,20 +159,23 @@ func (adder *Adder) curRootNode() (ipld.Node, error) {
159
160 // Recursively pins the root node of Adder and
161 // writes the pin state to the backing datastore.
161 -func (adder *Adder) PinRoot(root ipld.Node) error {
162 +func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error {
163 + ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "PinRoot")
164 + defer span.End()
165 +
166 if !adder.Pin {
167 return nil
168 }
169
170 rnk := root.Cid()
171
168 - err := adder.dagService.Add(adder.ctx, root)
172 + err := adder.dagService.Add(ctx, root)
173 if err != nil {
174 return err
175 }
176
177 if adder.tempRoot.Defined() {
174 - err := adder.pinning.Unpin(adder.ctx, adder.tempRoot, true)
178 + err := adder.pinning.Unpin(ctx, adder.tempRoot, true)
179 if err != nil {
180 return err
181 }
@@ -179,7 +183,7 @@ func (adder *Adder) PinRoot(root ipld.Node) error {
183 }
184
185 adder.pinning.PinWithMode(rnk, pin.Recursive)
182 - return adder.pinning.Flush(adder.ctx)
186 + return adder.pinning.Flush(ctx)
187 }
188
189 func (adder *Adder) outputDirs(path string, fsn mfs.FSNode) error {
@@ -255,6 +259,9 @@ func (adder *Adder) addNode(node ipld.Node, path string) error {
259
260 // AddAllAndPin adds the given request's files and pin them.
261 func (adder *Adder) AddAllAndPin(ctx context.Context, file files.Node) (ipld.Node, error) {
262 + ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "AddAllAndPin")
263 + defer span.End()
264 +
265 if adder.Pin {
266 adder.unlocker = adder.gcLocker.PinLock(ctx)
267 }
@@ -330,10 +337,13 @@ func (adder *Adder) AddAllAndPin(ctx context.Context, file files.Node) (ipld.Nod
337 if !adder.Pin {
338 return nd, nil
339 }
333 - return nd, adder.PinRoot(nd)
340 + return nd, adder.PinRoot(ctx, nd)
341 }
342
343 func (adder *Adder) addFileNode(ctx context.Context, path string, file files.Node, toplevel bool) error {
344 + ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "AddFileNode")
345 + defer span.End()
346 +
347 defer file.Close()
348
349 err := adder.maybePauseForGC(ctx)
@@ -436,13 +446,16 @@ func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory
446 }
447
448 func (adder *Adder) maybePauseForGC(ctx context.Context) error {
449 + ctx, span := tracing.Span(ctx, "CoreUnix.Adder", "MaybePauseForGC")
450 + defer span.End()
451 +
452 if adder.unlocker != nil && adder.gcLocker.GCRequested(ctx) {
453 rn, err := adder.curRootNode()
454 if err != nil {
455 return err
456 }
457
445 - err = adder.PinRoot(rn)
458 + err = adder.PinRoot(ctx, rn)
459 if err != nil {
460 return err
461 }
docs/debug-guide.md
+6
@@ -7,6 +7,7 @@ This is a document for helping debug go-ipfs. Please add to it if you can!
7 - [Analyzing the stack dump](#analyzing-the-stack-dump)
8 - [Analyzing the CPU Profile](#analyzing-the-cpu-profile)
9 - [Analyzing vars and memory statistics](#analyzing-vars-and-memory-statistics)
10 +- [Tracing](#tracing)
11 - [Other](#other)
12
13 ### Beginning
@@ -95,6 +96,11 @@ the quickest way to easily point out where the hot spots in the code are.
96
97 The output is JSON formatted and includes badger store statistics, the command line run, and the output from Go's [runtime.ReadMemStats](https://golang.org/pkg/runtime/#ReadMemStats). The [MemStats](https://golang.org/pkg/runtime/#MemStats) has useful information about memory allocation and garbage collection.
98
99 +### Tracing
100 +
101 +Experimental tracing via OpenTelemetry suite of tools is available.
102 +See `tracing/doc.go` for more details.
103 +
104 ### Other
105
106 If you have any questions, or want us to analyze some weird go-ipfs behaviour,
docs/environment-variables.md
+70
@@ -102,3 +102,73 @@ Deprecated: Use the `Swarm.Transports.Multiplexers` config field.
102 Tells go-ipfs which multiplexers to use in which order.
103
104 Default: "/yamux/1.0.0 /mplex/6.7.0"
105 +
106 +# Tracing
107 +**NOTE** Tracing support is experimental--releases may contain tracing-related breaking changes.
108 +
109 +## `IPFS_TRACING`
110 +Enables OpenTelemetry tracing.
111 +
112 +Default: false
113 +
114 +## `IPFS_TRACING_JAEGER`
115 +Enables the Jaeger exporter for OpenTelemetry.
116 +
117 +For additional Jaeger exporter configuration, see: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#jaeger-exporter
118 +
119 +Default: false
120 +
121 +### How to use Jaeger UI
122 +
123 +One can use the `jaegertracing/all-in-one` Docker image to run a full Jaeger
124 +stack and configure go-ipfs to publish traces to it (here, in an ephemeral
125 +container):
126 +
127 +```console
128 +$ docker run --rm -it --name jaeger \
129 + -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
130 + -p 5775:5775/udp \
131 + -p 6831:6831/udp \
132 + -p 6832:6832/udp \
133 + -p 5778:5778 \
134 + -p 16686:16686 \
135 + -p 14268:14268 \
136 + -p 14250:14250 \
137 + -p 9411:9411 \
138 + jaegertracing/all-in-one
139 +```
140 +
141 +Then, in other terminal, start go-ipfs with Jaeger tracing enabled:
142 +```
143 +$ IPFS_TRACING=1 IPFS_TRACING_JAEGER=1 ipfs daemon
144 +```
145 +
146 +Finally, the [Jaeger UI](https://github.com/jaegertracing/jaeger-ui#readme) is available at http://localhost:16686
147 +
148 +
149 +## `IPFS_TRACING_OTLP_HTTP`
150 +Enables the OTLP HTTP exporter for OpenTelemetry.
151 +
152 +For additional exporter configuration, see: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
153 +
154 +Default: false
155 +
156 +## `IPFS_TRACING_OTLP_GRPC`
157 +Enables the OTLP gRPC exporter for OpenTelemetry.
158 +
159 +For additional exporter configuration, see: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md
160 +
161 +Default: false
162 +
163 +## `IPFS_TRACING_FILE`
164 +Enables the file exporter for OpenTelemetry, writing traces to the given file in JSON format.
165 +
166 +Example: "/var/log/ipfs-traces.json"
167 +
168 +Default: "" (disabled)
169 +
170 +## `IPFS_TRACING_RATIO`
171 +The ratio of traces to export, as a floating point value in the interval [0, 1].
172 +
173 +Deault: 1.0 (export all traces)
174 +
go.mod
+8
@@ -107,6 +107,14 @@ require (
107 go.uber.org/dig v1.14.0
108 go.uber.org/fx v1.16.0
109 go.uber.org/zap v1.21.0
110 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0
111 + go.opentelemetry.io/otel v1.2.0
112 + go.opentelemetry.io/otel/exporters/jaeger v1.2.0
113 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0
114 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0
115 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0
116 + go.opentelemetry.io/otel/sdk v1.2.0
117 + go.opentelemetry.io/otel/trace v1.2.0
118 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
119 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
120 golang.org/x/sys v0.0.0-20211025112917-711f33c9992c
go.sum
+39 -5
@@ -118,6 +118,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7
118 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
119 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
120 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
121 +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
122 +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
123 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
124 github.com/ceramicnetwork/go-dag-jose v0.1.0 h1:yJ/HVlfKpnD3LdYP03AHyTvbm3BpPiz2oZiOeReJRdU=
125 github.com/ceramicnetwork/go-dag-jose v0.1.0/go.mod h1:qYA1nYt0X8u4XoMAVoOV3upUVKtrxy/I670Dg5F0wjI=
@@ -136,7 +138,11 @@ github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4
138 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
139 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
140 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
141 +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
142 github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
143 +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
144 +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
145 +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
146 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
147 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
148 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -195,12 +201,15 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
201 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
202 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
203 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
204 +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
205 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
206 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=
207 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=
208 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
209 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
210 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
211 +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
212 +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
213 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
214 github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
215 github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
@@ -345,6 +354,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
354 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
355 github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
356 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
357 +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
358 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
359 github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
360 github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
@@ -1326,6 +1336,7 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3
1336 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
1337 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
1338 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1339 +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
1340 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1341 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
1342 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -1400,15 +1411,35 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
1411 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
1412 go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
1413 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
1403 -go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
1414 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 h1:0BgiNWjN7rUWO9HdjF4L12r8OW86QkVQcYmCjnayJLo=
1415 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0/go.mod h1:bdvm3YpMxWAgEfQhtTBaVR8ceXPRuRBSQrvOBnIlHxc=
1416 go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
1405 -go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
1417 +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ=
1418 +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
1419 +go.opentelemetry.io/otel/exporters/jaeger v1.2.0 h1:C/5Egj3MJBXRJi22cSl07suqPqtZLnLFmH//OxETUEc=
1420 +go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E=
1421 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 h1:xzbcGykysUh776gzD1LUPsNNHKWN0kQWDnJhn1ddUuk=
1422 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0/go.mod h1:14T5gr+Y6s2AgHPqBMgnGwp04csUjQmYXFWPeiBoq5s=
1423 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 h1:VsgsSCDwOSuO8eMVh63Cd4nACMqgjpmAeJSIvVNneD0=
1424 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0/go.mod h1:9mLBBnPRf3sf+ASVH2p9xREXVBvwib02FxcKnavtExg=
1425 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0 h1:j/jXNzS6Dy0DFgO/oyCvin4H7vTQBg2Vdi6idIzWhCI=
1426 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.2.0/go.mod h1:k5GnE4m4Jyy2DNh6UAzG6Nml51nuqQyszV7O1ksQAnE=
1427 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0 h1:OiYdrCq1Ctwnovp6EofSPwlp5aGy4LgKNbkg7PtEUw8=
1428 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.2.0/go.mod h1:DUFCmFkXr0VtAHl5Zq2JRx24G6ze5CAq8YfdD36RdX8=
1429 +go.opentelemetry.io/otel/internal/metric v0.25.0 h1:w/7RXe16WdPylaIXDgcYM6t/q0K5lXgSdZOEbIEyliE=
1430 +go.opentelemetry.io/otel/internal/metric v0.25.0/go.mod h1:Nhuw26QSX7d6n4duoqAFi5KOQR4AuzyMcl5eXOgwxtc=
1431 go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
1407 -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw=
1432 +go.opentelemetry.io/otel/metric v0.25.0 h1:7cXOnCADUsR3+EOqxPaSKwhEuNu0gz/56dRN1hpIdKw=
1433 +go.opentelemetry.io/otel/metric v0.25.0/go.mod h1:E884FSpQfnJOMMUaq+05IWlJ4rjZpk2s/F1Ju+TEEm8=
1434 go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
1409 -go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
1435 +go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo=
1436 +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U=
1437 go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
1438 +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0=
1439 +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0=
1440 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
1441 +go.opentelemetry.io/proto/otlp v0.10.0 h1:n7brgtEbDvXEgGyKKo8SobKT1e9FewlDtXzkVP5djoE=
1442 +go.opentelemetry.io/proto/otlp v0.10.0/go.mod h1:zG20xCK0szZ1xdokeSOwEcmlXu+x9kkdRe6N1DhKcfU=
1443 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
1444 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
1445 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -1663,6 +1694,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
1694 golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1695 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1696 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1697 +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1698 golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1699 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1700 golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1841,8 +1873,10 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
1873 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
1874 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
1875 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
1844 -google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
1876 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
1877 +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
1878 +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
1879 +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
1880 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
1881 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
1882 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
mk/golang.mk
+1 -1
@@ -70,7 +70,7 @@ test_go_fmt:
70 TEST_GO += test_go_fmt
71
72 test_go_lint: test/bin/golangci-lint
73 - golangci-lint run ./...
73 + golangci-lint run --timeout=3m ./...
74 .PHONY: test_go_lint
75
76 test_go: $(TEST_GO)
plugin/loader/loader.go
+2
@@ -241,6 +241,7 @@ func (loader *PluginLoader) Inject() error {
241
242 for _, pl := range loader.plugins {
243 if pl, ok := pl.(plugin.PluginIPLD); ok {
244 +
245 err := injectIPLDPlugin(pl)
246 if err != nil {
247 loader.state = loaderFailed
@@ -338,6 +339,7 @@ func injectIPLDPlugin(pl plugin.PluginIPLD) error {
339 }
340
341 func injectTracerPlugin(pl plugin.PluginTracer) error {
342 + log.Warn("Tracer plugins are deprecated, it's recommended to configure an OpenTelemetry collector instead.")
343 tracer, err := pl.InitTracer()
344 if err != nil {
345 return err
test/sharness/t0310-tracing.sh new
+57
@@ -0,0 +1,57 @@
1 +#!/usr/bin/env bash
2 +#
3 +# Copyright (c) 2022 Protocol Labs
4 +# MIT/Apache-2.0 Licensed; see the LICENSE file in this repository.
5 +#
6 +
7 +test_description="Test tracing"
8 +
9 +. lib/test-lib.sh
10 +
11 +test_init_ipfs
12 +
13 +export IPFS_TRACING=1
14 +export IPFS_TRACING_OTLP_GRPC=1
15 +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
16 +
17 +cat <<EOF > collector-config.yaml
18 +receivers:
19 + otlp:
20 + protocols:
21 + grpc:
22 +
23 +processors:
24 + batch:
25 +
26 +exporters:
27 + file:
28 + path: /traces/traces.json
29 +
30 +service:
31 + pipelines:
32 + traces:
33 + receivers: [otlp]
34 + processors: [batch]
35 + exporters: [file]
36 +EOF
37 +
38 +# touch traces.json and give it 777 perms, in case docker runs as a different user
39 +rm -rf traces.json && touch traces.json && chmod 777 traces.json
40 +
41 +test_expect_success "run opentelemetry collector" '
42 + docker run --rm -d -v "$PWD/collector-config.yaml":/config.yaml -v "$PWD":/traces --net=host --name=ipfs-test-otel-collector otel/opentelemetry-collector-contrib:0.48.0 --config /config.yaml
43 +'
44 +
45 +test_launch_ipfs_daemon
46 +
47 +test_expect_success "check that a swarm span eventually appears in exported traces" '
48 + until cat traces.json | grep CoreAPI.SwarmAPI >/dev/null; do sleep 0.1; done
49 +'
50 +
51 +test_expect_success "kill docker container" '
52 + docker kill ipfs-test-otel-collector
53 +'
54 +
55 +test_kill_ipfs_daemon
56 +
57 +test_done
tracing/doc.go new
+66
@@ -0,0 +1,66 @@
1 +// Package tracing contains the tracing logic for go-ipfs, including configuring the tracer and
2 +// helping keep consistent naming conventions across the stack.
3 +//
4 +// NOTE: Tracing is currently experimental. Span names may change unexpectedly, spans may be removed,
5 +// and backwards-incompatible changes may be made to tracing configuration, options, and defaults.
6 +//
7 +// go-ipfs uses OpenTelemetry as its tracing API, and when possible, standard OpenTelemetry environment
8 +// variables can be used to configure it. Multiple exporters can also be installed simultaneously,
9 +// including one that writes traces to a JSON file on disk.
10 +//
11 +// In general, tracing is configured through environment variables. The IPFS-specific environment variables are:
12 +//
13 +// - IPFS_TRACING: enable tracing in go-ipfs
14 +// - IPFS_TRACING_JAEGER: enable the Jaeger exporter
15 +// - IPFS_TRACING_RATIO: the ratio of traces to export, defaults to 1 (export everything)
16 +// - IPFS_TRACING_FILE: write traces to the given filename
17 +// - IPFS_TRACING_OTLP_HTTP: enable the OTLP HTTP exporter
18 +// - IPFS_TRACING_OTLP_GRPC: enable the OTLP gRPC exporter
19 +//
20 +// Different exporters have their own set of environment variables, depending on the exporter. These are typically
21 +// standard environment variables. Some common ones:
22 +//
23 +// Jaeger:
24 +//
25 +// - OTEL_EXPORTER_JAEGER_AGENT_HOST
26 +// - OTEL_EXPORTER_JAEGER_AGENT_PORT
27 +// - OTEL_EXPORTER_JAEGER_ENDPOINT
28 +// - OTEL_EXPORTER_JAEGER_USER
29 +// - OTEL_EXPORTER_JAEGER_PASSWORD
30 +//
31 +// OTLP HTTP/gRPC:
32 +//
33 +// - OTEL_EXPORTER_OTLP_ENDPOINT
34 +// - OTEL_EXPORTER_OTLP_CERTIFICATE
35 +// - OTEL_EXPORTER_OTLP_HEADERS
36 +// - OTEL_EXPORTER_OTLP_COMPRESSION
37 +// - OTEL_EXPORTER_OTLP_TIMEOUT
38 +//
39 +// For example, if you run a local IPFS daemon, you can use the jaegertracing/all-in-one Docker image to run
40 +// a full Jaeger stack and configure go-ipfs to publish traces to it:
41 +//
42 +// docker run -d --name jaeger \
43 +// -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
44 +// -p 5775:5775/udp \
45 +// -p 6831:6831/udp \
46 +// -p 6832:6832/udp \
47 +// -p 5778:5778 \
48 +// -p 16686:16686 \
49 +// -p 14268:14268 \
50 +// -p 14250:14250 \
51 +// -p 9411:9411 \
52 +// jaegertracing/all-in-one
53 +// IPFS_TRACING=1 IPFS_TRACING_JAEGER=1 ipfs daemon
54 +//
55 +// In this example the Jaeger UI is available at http://localhost:16686.
56 +//
57 +//
58 +// Implementer Notes
59 +//
60 +// Span names follow a convention of <Component>.<Span>, some examples:
61 +//
62 +// - component=Gateway + span=Request -> Gateway.Request
63 +// - component=CoreAPI.PinAPI + span=Verify.CheckPin -> CoreAPI.PinAPI.Verify.CheckPin
64 +//
65 +// We follow the OpenTelemetry convention of using whatever TracerProvider is registered globally.
66 +package tracing
tracing/tracing.go new
+136
@@ -0,0 +1,136 @@
1 +package tracing
2 +
3 +import (
4 + "context"
5 + "fmt"
6 + "os"
7 + "strconv"
8 +
9 + version "github.com/ipfs/go-ipfs"
10 + "go.opentelemetry.io/otel"
11 + "go.opentelemetry.io/otel/exporters/jaeger"
12 + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
13 + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
14 + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
15 + "go.opentelemetry.io/otel/sdk/resource"
16 + "go.opentelemetry.io/otel/sdk/trace"
17 + semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
18 + traceapi "go.opentelemetry.io/otel/trace"
19 +)
20 +
21 +var exporterBuilders = map[string]func(context.Context, string) (trace.SpanExporter, error){
22 + "IPFS_TRACING_JAEGER": func(ctx context.Context, s string) (trace.SpanExporter, error) {
23 + return jaeger.New(jaeger.WithCollectorEndpoint())
24 + },
25 + "IPFS_TRACING_FILE": func(ctx context.Context, s string) (trace.SpanExporter, error) {
26 + return newFileExporter(s)
27 + },
28 + "IPFS_TRACING_OTLP_HTTP": func(ctx context.Context, s string) (trace.SpanExporter, error) {
29 + return otlptracehttp.New(ctx)
30 + },
31 + "IPFS_TRACING_OTLP_GRPC": func(ctx context.Context, s string) (trace.SpanExporter, error) {
32 + return otlptracegrpc.New(ctx)
33 + },
34 +}
35 +
36 +// fileExporter wraps a file-writing exporter and closes the file when the exporter is shutdown.
37 +type fileExporter struct {
38 + file *os.File
39 + writerExporter *stdouttrace.Exporter
40 +}
41 +
42 +var _ trace.SpanExporter = &fileExporter{}
43 +
44 +func newFileExporter(file string) (*fileExporter, error) {
45 + f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
46 + if err != nil {
47 + return nil, fmt.Errorf("opening %s: %w", file, err)
48 + }
49 + stdoutExporter, err := stdouttrace.New(stdouttrace.WithWriter(f))
50 + if err != nil {
51 + return nil, err
52 + }
53 + return &fileExporter{
54 + writerExporter: stdoutExporter,
55 + file: f,
56 + }, nil
57 +}
58 +
59 +func (e *fileExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
60 + return e.writerExporter.ExportSpans(ctx, spans)
61 +}
62 +
63 +func (e *fileExporter) Shutdown(ctx context.Context) error {
64 + if err := e.writerExporter.Shutdown(ctx); err != nil {
65 + return err
66 + }
67 + if err := e.file.Close(); err != nil {
68 + return fmt.Errorf("closing trace file: %w", err)
69 + }
70 + return nil
71 +}
72 +
73 +// noopShutdownTracerProvider wraps a TracerProvider with a no-op Shutdown method.
74 +type noopShutdownTracerProvider struct {
75 + tp traceapi.TracerProvider
76 +}
77 +
78 +func (n *noopShutdownTracerProvider) Shutdown(ctx context.Context) error {
79 + return nil
80 +}
81 +func (n *noopShutdownTracerProvider) Tracer(instrumentationName string, opts ...traceapi.TracerOption) traceapi.Tracer {
82 + return n.tp.Tracer(instrumentationName, opts...)
83 +}
84 +
85 +type ShutdownTracerProvider interface {
86 + traceapi.TracerProvider
87 + Shutdown(ctx context.Context) error
88 +}
89 +
90 +// NewTracerProvider creates and configures a TracerProvider.
91 +func NewTracerProvider(ctx context.Context) (ShutdownTracerProvider, error) {
92 + if os.Getenv("IPFS_TRACING") == "" {
93 + return &noopShutdownTracerProvider{tp: traceapi.NewNoopTracerProvider()}, nil
94 + }
95 +
96 + options := []trace.TracerProviderOption{}
97 +
98 + traceRatio := 1.0
99 + if envRatio := os.Getenv("IPFS_TRACING_RATIO"); envRatio != "" {
100 + r, err := strconv.ParseFloat(envRatio, 64)
101 + if err == nil {
102 + traceRatio = r
103 + }
104 + }
105 + options = append(options, trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(traceRatio))))
106 +
107 + r, err := resource.Merge(
108 + resource.Default(),
109 + resource.NewWithAttributes(
110 + semconv.SchemaURL,
111 + semconv.ServiceNameKey.String("go-ipfs"),
112 + semconv.ServiceVersionKey.String(version.CurrentVersionNumber),
113 + ),
114 + )
115 + if err != nil {
116 + return nil, err
117 + }
118 + options = append(options, trace.WithResource(r))
119 +
120 + for envVar, builder := range exporterBuilders {
121 + if val := os.Getenv(envVar); val != "" {
122 + exporter, err := builder(ctx, val)
123 + if err != nil {
124 + return nil, err
125 + }
126 + options = append(options, trace.WithBatcher(exporter))
127 + }
128 + }
129 +
130 + return trace.NewTracerProvider(options...), nil
131 +}
132 +
133 +// Span starts a new span using the standard IPFS tracing conventions.
134 +func Span(ctx context.Context, componentName string, spanName string, opts ...traceapi.SpanStartOption) (context.Context, traceapi.Span) {
135 + return otel.Tracer("go-ipfs").Start(ctx, fmt.Sprintf("%s.%s", componentName, spanName), opts...)
136 +}