Tracing module based on OpenTelemetry.
go get github.com/ankorstore/yokai/trace
Since The TracerProviderFactory
use
the resource.Default() by default, you can use:
OTEL_SERVICE_NAME
env variable to configure your tracing service nameOTEL_RESOURCE_ATTRIBUTES
env variable to configure your resource attributes
This module provides a TracerProviderFactory, allowing to set up a TracerProvider
easily.
package main
import (
"github.com/ankorstore/yokai/trace"
"go.opentelemetry.io/otel/sdk/resource"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create()
// equivalent to
tp, _ = trace.NewDefaultTracerProviderFactory().Create(
trace.Global(true), // set the tracer provider as global
trace.WithResource(resource.Default()), // use the default resource
trace.WithSampler(trace.NewParentBasedAlwaysOnSampler()), // use parent based always on sampling
trace.WithSpanProcessor(trace.NewNoopSpanProcessor()), // use noop processor (void trace spans)
)
}
See available factory options.
This module provides the CtxTracerProvider()
function that allow to extract the tracer provider from
a context.Context
.
If no tracer provider is found in context, the global tracer provider will be used.
This module also provides the CtxTracer()
function that allow to create a tracer (named yokai
) from the tracer provider got from
a context.Context
.
This modules comes with 4 SpanProcessor
ready to use:
Noop
: to async void traces (default)Stdout
: to async print traces to the standard outputOtlpGrpc
: to async send traces to OTLP/gRPC collectors ( ex: Jaeger, Grafana, etc.)Test
: to sync store traces in memory (for testing assertions)
package main
import (
"context"
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSpanProcessor(trace.NewNoopSpanProcessor()),
)
// voids trace span
_, span := tp.Tracer("default").Start(context.Background(), "my span")
defer span.End()
}
package main
import (
"context"
"github.com/ankorstore/yokai/trace"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSpanProcessor(trace.NewStdoutSpanProcessor(stdouttrace.WithPrettyPrint())),
)
// pretty prints trace span to stdout
_, span := tp.Tracer("default").Start(context.Background(), "my span")
defer span.End()
}
package main
import (
"context"
"github.com/ankorstore/yokai/trace"
)
func main() {
ctx := context.Background()
conn, _ := trace.NewOtlpGrpcClientConnection(ctx, "jaeger:4317")
proc, _ := trace.NewOtlpGrpcSpanProcessor(ctx, conn)
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSpanProcessor(proc),
)
// sends trace span to jaeger:4317
_, span := tp.Tracer("default").Start(ctx, "my span")
defer span.End()
}
package main
import (
"context"
"fmt"
"github.com/ankorstore/yokai/trace"
"github.com/ankorstore/yokai/trace/tracetest"
)
func main() {
ex := tracetest.NewDefaultTestTraceExporter()
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSpanProcessor(trace.NewTestSpanProcessor(ex)),
)
// sends trace span to test exporter
_, span := tp.Tracer("default").Start(context.Background(), "my span")
defer span.End()
// check
fmt.Printf("has span: %v", ex.HasSpan("my span")) // has span: true
}
You can use the provided test assertion helpers in your tests:
AssertHasTraceSpan
: to assert on exact name and exact attributes matchAssertHasNotTraceSpan
: to assert on exact name and exact attributes non matchAssertContainTraceSpan
: to assert on exact name and partial attributes matchAssertContainNotTraceSpan
: to assert on exact name and partial attributes non match
and use Dump()
to print the current content of the test span processor.
package main_test
import (
"context"
"testing"
"github.com/ankorstore/yokai/trace"
"github.com/ankorstore/yokai/trace/tracetest"
"go.opentelemetry.io/otel/attribute"
)
func TestTracer(t *testing.T) {
ex := tracetest.NewDefaultTestTraceExporter()
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSpanProcessor(trace.NewTestSpanProcessor(ex)),
)
// sends trace span to test exporter
_, span := tp.Tracer("default").Start(
context.Background(),
"my span",
attribute.String("string attr name", "string attr value"),
attribute.Int("int attr name", 42),
)
span.End()
// dump spans
ex.Dump()
// assertion success
tracetest.AssertHasTraceSpan(
t,
ex,
"my span",
attribute.String("string attr name", "string attr value"),
attribute.Int("int attr name", 42),
)
// assertion success
tracetest.AssertHasNotTraceSpan(
t,
ex,
"my span",
attribute.String("string attr name", "string attr value"),
attribute.Int("int attr name", 24),
)
// assertion success
tracetest.AssertContainTraceSpan(
t,
ex,
"my span",
attribute.String("string attr name", "attr value"),
attribute.Int("int attr name", 42),
)
// assertion success
tracetest.AssertContainNotTraceSpan(
t,
ex,
"my span",
attribute.String("string attr name", "attr value"),
attribute.Int("int attr name", 24),
)
}
This modules comes with 6 Samplers
ready to use:
ParentBasedAlwaysOn
: always on depending on parent (default)ParentBasedAlwaysOff
: always off depending on parentParentBasedTraceIdRatio
: trace id ratio based depending on parentAlwaysOn
: always onAlwaysOff
: always offTraceIdRatio
: trace id ratio based
Note: parent based samplers returns a composite sampler which behaves differently, based on the parent of the span:
- if the span has no parent, the embedded sampler is used to make sampling decision
- if the span has a parent, it depends on whether the parent is remote and whether it is sampled
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewParentBasedAlwaysOnSampler()),
)
}
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewParentBasedAlwaysOffSampler()),
)
}
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewParentBasedTraceIdRatioSampler(0.5)),
)
}
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewAlwaysOnSampler()),
)
}
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewAlwaysOffSampler()),
)
}
package main
import (
"github.com/ankorstore/yokai/trace"
)
func main() {
tp, _ := trace.NewDefaultTracerProviderFactory().Create(
trace.WithSampler(trace.NewTraceIdRatioSampler(0.5)),
)
}