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
+}