kubo/tracing/tracing.go
Gus Eggert f855bfe6ef
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>
2022-04-04 19:24:05 +02:00

137 lines
4.1 KiB
Go

package tracing
import (
"context"
"fmt"
"os"
"strconv"
version "github.com/ipfs/go-ipfs"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
traceapi "go.opentelemetry.io/otel/trace"
)
var exporterBuilders = map[string]func(context.Context, string) (trace.SpanExporter, error){
"IPFS_TRACING_JAEGER": func(ctx context.Context, s string) (trace.SpanExporter, error) {
return jaeger.New(jaeger.WithCollectorEndpoint())
},
"IPFS_TRACING_FILE": func(ctx context.Context, s string) (trace.SpanExporter, error) {
return newFileExporter(s)
},
"IPFS_TRACING_OTLP_HTTP": func(ctx context.Context, s string) (trace.SpanExporter, error) {
return otlptracehttp.New(ctx)
},
"IPFS_TRACING_OTLP_GRPC": func(ctx context.Context, s string) (trace.SpanExporter, error) {
return otlptracegrpc.New(ctx)
},
}
// fileExporter wraps a file-writing exporter and closes the file when the exporter is shutdown.
type fileExporter struct {
file *os.File
writerExporter *stdouttrace.Exporter
}
var _ trace.SpanExporter = &fileExporter{}
func newFileExporter(file string) (*fileExporter, error) {
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("opening %s: %w", file, err)
}
stdoutExporter, err := stdouttrace.New(stdouttrace.WithWriter(f))
if err != nil {
return nil, err
}
return &fileExporter{
writerExporter: stdoutExporter,
file: f,
}, nil
}
func (e *fileExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
return e.writerExporter.ExportSpans(ctx, spans)
}
func (e *fileExporter) Shutdown(ctx context.Context) error {
if err := e.writerExporter.Shutdown(ctx); err != nil {
return err
}
if err := e.file.Close(); err != nil {
return fmt.Errorf("closing trace file: %w", err)
}
return nil
}
// noopShutdownTracerProvider wraps a TracerProvider with a no-op Shutdown method.
type noopShutdownTracerProvider struct {
tp traceapi.TracerProvider
}
func (n *noopShutdownTracerProvider) Shutdown(ctx context.Context) error {
return nil
}
func (n *noopShutdownTracerProvider) Tracer(instrumentationName string, opts ...traceapi.TracerOption) traceapi.Tracer {
return n.tp.Tracer(instrumentationName, opts...)
}
type ShutdownTracerProvider interface {
traceapi.TracerProvider
Shutdown(ctx context.Context) error
}
// NewTracerProvider creates and configures a TracerProvider.
func NewTracerProvider(ctx context.Context) (ShutdownTracerProvider, error) {
if os.Getenv("IPFS_TRACING") == "" {
return &noopShutdownTracerProvider{tp: traceapi.NewNoopTracerProvider()}, nil
}
options := []trace.TracerProviderOption{}
traceRatio := 1.0
if envRatio := os.Getenv("IPFS_TRACING_RATIO"); envRatio != "" {
r, err := strconv.ParseFloat(envRatio, 64)
if err == nil {
traceRatio = r
}
}
options = append(options, trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(traceRatio))))
r, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("go-ipfs"),
semconv.ServiceVersionKey.String(version.CurrentVersionNumber),
),
)
if err != nil {
return nil, err
}
options = append(options, trace.WithResource(r))
for envVar, builder := range exporterBuilders {
if val := os.Getenv(envVar); val != "" {
exporter, err := builder(ctx, val)
if err != nil {
return nil, err
}
options = append(options, trace.WithBatcher(exporter))
}
}
return trace.NewTracerProvider(options...), nil
}
// Span starts a new span using the standard IPFS tracing conventions.
func Span(ctx context.Context, componentName string, spanName string, opts ...traceapi.SpanStartOption) (context.Context, traceapi.Span) {
return otel.Tracer("go-ipfs").Start(ctx, fmt.Sprintf("%s.%s", componentName, spanName), opts...)
}