Skip to content

Commit

Permalink
Add tracegen utility for generating traces (#1245)
Browse files Browse the repository at this point in the history
Add tracegen utility for generating traces.

Signed-off-by: Yuri Shkuro <[email protected]>
  • Loading branch information
yurishkuro authored Jan 17, 2019
1 parent bda811b commit 48b034c
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
61 changes: 61 additions & 0 deletions cmd/tracegen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2018 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"flag"
"time"

"github.com/opentracing/opentracing-go"
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.uber.org/zap"

"github.com/jaegertracing/jaeger/internal/tracegen"
)

var logger, _ = zap.NewDevelopment()

func main() {
fs := flag.CommandLine
cfg := new(tracegen.Config)
cfg.Flags(fs)
flag.Parse()

metricsFactory := prometheus.New()
tracer, tCloser, err := jaegerConfig.Configuration{
ServiceName: "tracegen",
Sampler: &jaegerConfig.SamplerConfig{
Type: "const",
Param: 1,
},
}.NewTracer(
jaegerConfig.Metrics(metricsFactory),
jaegerConfig.Logger(jaegerZap.NewLogger(logger)),
)
if err != nil {
logger.Fatal("failed to create tracer", zap.Error(err))
}
defer tCloser.Close()

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

tracegen.Run(cfg, logger)

logger.Info("Waiting 1.5sec for metrics to flush")
time.Sleep(3 * time.Second / 2)
}
1 change: 1 addition & 0 deletions internal/tracegen/.nocover
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test utility
79 changes: 79 additions & 0 deletions internal/tracegen/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2018 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tracegen

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

"go.uber.org/zap"
)

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

// 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.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")
}

// Run executes the test scenario.
func Run(c *Config, logger *zap.Logger) error {
if c.Duration > 0 {
c.Traces = 0
} else if c.Traces <= 0 {
return fmt.Errorf("Either `traces` or `duration` must be greater than 0")
}

wg := sync.WaitGroup{}
var running uint32 = 1
for i := 0; i < c.Workers; i++ {
wg.Add(1)
w := worker{
id: i,
traces: c.Traces,
marshal: c.Marshal,
debug: c.Debug,
pause: c.Pause,
duration: c.Duration,
running: &running,
wg: &wg,
logger: logger.With(zap.Int("worker", i)),
}

go w.simulateTraces()
}
if c.Duration > 0 {
time.Sleep(c.Duration)
atomic.StoreUint32(&running, 0)
}
wg.Wait()
return nil
}
99 changes: 99 additions & 0 deletions internal/tracegen/worker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) 2018 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tracegen

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

"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"go.uber.org/zap"
)

type worker struct {
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)
marshal bool // whether the worker needs to marshal trace context via HTTP headers
debug bool // whether to set DEBUG flag on the spans
duration time.Duration // how long to run the test for (overrides `traces`)
pause time.Duration // how long to pause before finishing the trace
wg *sync.WaitGroup // notify when done
logger *zap.Logger
}

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

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

i++
if w.traces != 0 {
if i >= w.traces {
break
}
}
}
w.logger.Info(fmt.Sprintf("Worker %d generated %d traces", w.id, i))
w.wg.Done()
}

0 comments on commit 48b034c

Please sign in to comment.