Skip to content

Commit

Permalink
[tracegen] Switch from Jaeger Client to OTEL SDK (#4189)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
- Part of migrating away from deprecated Jaeger Go SDK

## Short description of the changes
- switch tracegen from Jaeger Client to OTEL SDK
- a breaking change in some respect:
  - default exporter changed to Jaeger HTTP
  - different environment variables are used to configure exporters
- debug and firehose flags are no longer set in the trace context, but
as span attributes
  - no more metrics from tracegen

Signed-off-by: Yuri Shkuro <[email protected]>
  • Loading branch information
yurishkuro authored Jan 29, 2023
1 parent fb1d67f commit 74d6a12
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 104 deletions.
14 changes: 7 additions & 7 deletions cmd/tracegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ The binary is available from the Releases page, as well as a Docker image:
$ docker run jaegertracing/jaeger-tracegen -service abcd -traces 10
```

Notice, however, that by default the generator uses the UDP exporter of `jaeger-client-go`,
which sends data to `localhost`, i.e. inside the networking namespace of the container itself,
which obviously doesn't go anywhere. You can use the environment variables supported by
[jaeger-client-go][env] to instruct the SDK where to send the data, for example to switch
to HTTP by setting `JAEGER_ENDPOINT`.
The generator can be configured to export traces in different formats, via `-exporter` flag.
By default, the exporters send data to `localhost`. If running in a container, this refers
to the networking namespace of the container itself, so to export to another container
(like Jaeger Collector), the exporters need to be provided with appropriate location.
Exporters accept configuration via environment variables:
* Jaeger exporter: see https://github.com/open-telemetry/opentelemetry-go/blob/main/exporters/jaeger/README.md
* OTLP exporter: see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md

See example in the included [docker-compose](./docker-compose.yml) file.

[env]: https://github.com/jaegertracing/jaeger-client-go#environment-variables
7 changes: 5 additions & 2 deletions cmd/tracegen/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ version: '2'
services:
jaeger:
image: jaegertracing/all-in-one:latest
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- '16686:16686'
- '4318:4318'

tracegen:
image: jaegertracing/jaeger-tracegen:latest
environment:
- JAEGER_AGENT_HOST=jaeger
- JAEGER_AGENT_PORT=6831
- OTEL_EXPORTER_JAEGER_ENDPOINT=http://jaeger:14268/api/traces
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=jaeger:4318
command: ["-duration", "10s", "-workers", "3", "-pause", "250ms"]
depends_on:
- jaeger
79 changes: 44 additions & 35 deletions cmd/tracegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@
package main

import (
"context"
"flag"
"net/http"
"time"
"fmt"

"github.com/opentracing/opentracing-go"
"github.com/prometheus/client_golang/prometheus/promhttp"
jaegerConfig "github.com/uber/jaeger-client-go/config"
jaegerZap "github.com/uber/jaeger-client-go/log/zap"
"github.com/uber/jaeger-lib/metrics/prometheus"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/internal/tracegen"
Expand All @@ -37,38 +41,43 @@ func main() {
cfg.Flags(fs)
flag.Parse()

metricsFactory := prometheus.New()
traceCfg := &jaegerConfig.Configuration{
ServiceName: cfg.Service,
Sampler: &jaegerConfig.SamplerConfig{
Type: "const",
Param: 1,
},
RPCMetrics: true,
}
traceCfg, err := traceCfg.FromEnv()
if err != nil {
logger.Fatal("failed to read tracer configuration", zap.Error(err))
}
otel.SetTextMapPropagator(propagation.TraceContext{})

tracer, tCloser, err := traceCfg.NewTracer(
jaegerConfig.Metrics(metricsFactory),
jaegerConfig.Logger(jaegerZap.NewLogger(logger)),
)
exp, err := createOtelExporter(cfg.TraceExporter)
if err != nil {
logger.Fatal("failed to create tracer", zap.Error(err))
logger.Sugar().Fatalf("cannot create trace exporter %s: %w", cfg.TraceExporter, err)
}
defer tCloser.Close()
logger.Sugar().Infof("using %s trace exporter", cfg.TraceExporter)

opentracing.InitGlobalTracer(tracer)
logger.Info("Initialized global tracer")

http.Handle("/metrics", promhttp.Handler())
go http.ListenAndServe(":2112", nil)
logger.Info("Initialized Prometheus endpoint at 2112")
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(cfg.Service),
)),
)
defer tp.Shutdown(context.Background())

tracegen.Run(cfg, logger)
tracegen.Run(cfg, tp.Tracer("tracegen"), logger)
}

logger.Info("Waiting 1.5sec for metrics to flush")
time.Sleep(3 * time.Second / 2)
func createOtelExporter(exporterType string) (sdktrace.SpanExporter, error) {
var exporter sdktrace.SpanExporter
var err error
switch exporterType {
case "jaeger":
exporter, err = jaeger.New(
jaeger.WithCollectorEndpoint(),
)
case "otlp":
client := otlptracehttp.NewClient(
otlptracehttp.WithInsecure(),
)
exporter, err = otlptrace.New(context.Background(), client)
case "stdout":
exporter, err = stdouttrace.New()
default:
return nil, fmt.Errorf("unrecognized exporter type %s", exporterType)
}
return exporter, err
}
23 changes: 13 additions & 10 deletions internal/tracegen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,37 @@ import (
"sync/atomic"
"time"

"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)

// Config describes the test scenario.
type Config struct {
Workers int
Traces int
Marshal bool
Debug bool
Firehose bool
Pause time.Duration
Duration time.Duration
Service string
Workers int
Traces int
Marshal bool
Debug bool
Firehose bool
Pause time.Duration
Duration time.Duration
Service string
TraceExporter string
}

// Flags registers config flags.
func (c *Config) Flags(fs *flag.FlagSet) {
fs.IntVar(&c.Workers, "workers", 1, "Number of workers (goroutines) to run")
fs.IntVar(&c.Traces, "traces", 1, "Number of traces to generate in each worker (ignored if duration is provided")
fs.BoolVar(&c.Marshal, "marshal", false, "Whether to marshal trace context via HTTP headers")
fs.BoolVar(&c.Debug, "debug", false, "Whether to set DEBUG flag on the spans to force sampling")
fs.BoolVar(&c.Firehose, "firehose", false, "Whether to set FIREHOSE flag on the spans to skip indexing")
fs.DurationVar(&c.Pause, "pause", time.Microsecond, "How long to pause before finishing trace")
fs.DurationVar(&c.Duration, "duration", 0, "For how long to run the test")
fs.StringVar(&c.Service, "service", "tracegen", "Service name to use")
fs.StringVar(&c.TraceExporter, "trace-exporter", "jaeger", "Trace exporter (jaeger|otlp|stdout). Exporters can be additionally configured via environment variables, see https://github.com/jaegertracing/jaeger/blob/main/cmd/tracegen/README.md")
}

// Run executes the test scenario.
func Run(c *Config, logger *zap.Logger) error {
func Run(c *Config, tracer trace.Tracer, logger *zap.Logger) error {
if c.Duration > 0 {
c.Traces = 0
} else if c.Traces <= 0 {
Expand All @@ -62,6 +64,7 @@ func Run(c *Config, logger *zap.Logger) error {
wg.Add(1)
w := worker{
id: i,
tracer: tracer,
traces: c.Traces,
marshal: c.Marshal,
debug: c.Debug,
Expand Down
97 changes: 47 additions & 50 deletions internal/tracegen/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
package tracegen

import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"

"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/uber/jaeger-client-go"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)

type worker struct {
tracer trace.Tracer
running *uint32 // pointer to shared flag that indicates it's time to stop the test
id int // worker id
traces int // how many traces the worker has to generate (only when duration==0)
Expand All @@ -40,59 +41,13 @@ type worker struct {
}

const (
fakeIP uint32 = 1<<24 | 2<<16 | 3<<8 | 4

fakeSpanDuration = 123 * time.Microsecond
)

func (w worker) simulateTraces() {
tracer := opentracing.GlobalTracer()
var i int
for atomic.LoadUint32(w.running) == 1 {
sp := tracer.StartSpan("lets-go")
ext.SpanKindRPCClient.Set(sp)
ext.PeerHostIPv4.Set(sp, fakeIP)
ext.PeerService.Set(sp, "tracegen-server")
if w.debug {
ext.SamplingPriority.Set(sp, 100)
}

if w.firehose {
jaeger.EnableFirehose(sp.(*jaeger.Span))
}

childCtx := sp.Context()
if w.marshal {
m := make(map[string]string)
c := opentracing.TextMapCarrier(m)
if err := tracer.Inject(sp.Context(), opentracing.TextMap, c); err == nil {
c := opentracing.TextMapCarrier(m)
childCtx, err = tracer.Extract(opentracing.TextMap, c)
if err != nil {
w.logger.Error("cannot extract from TextMap", zap.Error(err))
}
} else {
w.logger.Error("cannot inject span", zap.Error(err))
}
}
child := opentracing.StartSpan(
"okey-dokey",
ext.RPCServerOption(childCtx),
)
ext.PeerHostIPv4.Set(child, fakeIP)
ext.PeerService.Set(child, "tracegen-client")

time.Sleep(w.pause)

if w.pause == 0 {
child.Finish()
sp.Finish()
} else {
opt := opentracing.FinishOptions{FinishTime: time.Now().Add(fakeSpanDuration)}
child.FinishWithOptions(opt)
sp.FinishWithOptions(opt)
}

w.simulateOneTrace()
i++
if w.traces != 0 {
if i >= w.traces {
Expand All @@ -103,3 +58,45 @@ func (w worker) simulateTraces() {
w.logger.Info(fmt.Sprintf("Worker %d generated %d traces", w.id, i))
w.wg.Done()
}

func (w worker) simulateOneTrace() {
ctx := context.Background()
attrs := []attribute.KeyValue{
attribute.String("peer.service", "tracegen-server"),
attribute.String("peer.host.ipv4", "1.1.1.1"),
}
if w.debug {
attrs = append(attrs, attribute.Bool("jaeger.debug", true))
}
if w.firehose {
attrs = append(attrs, attribute.Bool("jaeger.firehose", true))
}
start := time.Now()
ctx, sp := w.tracer.Start(
ctx,
"lets-go",
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(attrs...),
trace.WithTimestamp(start),
)

_, child := w.tracer.Start(
ctx,
"okey-dokey",
trace.WithSpanKind(trace.SpanKindServer),
)

time.Sleep(w.pause)

if w.pause != 0 {
child.End()
sp.End()
} else {
child.End(
trace.WithTimestamp(start.Add(fakeSpanDuration)),
)
sp.End(
trace.WithTimestamp(start.Add(fakeSpanDuration)),
)
}
}

0 comments on commit 74d6a12

Please sign in to comment.