Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor printing logic #315

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
orbs:
codecov: codecov/codecov@5
jobs:
build:
working_directory: ~/repo
Expand All @@ -21,7 +23,8 @@ jobs:
name: Run tests
command: |
mkdir -p /tmp/test-reports
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -json -race ./...
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -json -race -coverprofile=coverage.txt -covermode=atomic ./...
- codecov/upload
- store_artifacts:
path: /tmp/test-reports
- store_test_results:
Expand Down
26 changes: 0 additions & 26 deletions .github/workflows/go.coverage.yml

This file was deleted.

4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,13 @@ func Execute() {
end := time.Now()

if err != nil {
printutils.ErrPrint(os.Stderr, "There was an error while starting benchmark: %s\n", err.Error())
printutils.ErrFprintf(os.Stderr, "There was an error while starting benchmark: %s\n", err.Error())
close(sigsInt)
os.Exit(1)
}

if err := reporter.PrintReport(&benchmark, res, start, end.Sub(start)); err != nil {
printutils.ErrPrint(os.Stderr, "There was an error while printing report: %s\n", err.Error())
printutils.ErrFprintf(os.Stderr, "There was an error while printing report: %s\n", err.Error())
close(sigsInt)
os.Exit(1)
}
Expand Down
12 changes: 7 additions & 5 deletions pkg/dnsbench/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (b *Benchmark) Run(ctx context.Context) ([]*ResultStats, error) {
}

if !b.Silent && !b.JSON {
fmt.Fprintf(b.Writer, "Using %s hostnames\n", printutils.HighlightStr(len(questions)))
printutils.NeutralFprintf(b.Writer, "Using %s hostnames\n", printutils.HighlightSprint(len(questions)))
}

var qTypes []uint16
Expand All @@ -334,18 +334,20 @@ func (b *Benchmark) Run(ctx context.Context) ([]*ResultStats, error) {
if b.Rate > 0 {
limit = ratelimit.New(b.Rate)
if b.RateLimitWorker == 0 {
limits = fmt.Sprintf("(limited to %s QPS overall)", printutils.HighlightStr(b.Rate))
limits = fmt.Sprintf("(limited to %s QPS overall)", printutils.HighlightSprint(b.Rate))
} else {
limits = fmt.Sprintf("(limited to %s QPS overall and %s QPS per concurrent worker)", printutils.HighlightStr(b.Rate), printutils.HighlightStr(b.RateLimitWorker))
limits = fmt.Sprintf("(limited to %s QPS overall and %s QPS per concurrent worker)",
printutils.HighlightSprint(b.Rate), printutils.HighlightSprint(b.RateLimitWorker))
}
}
if b.Rate == 0 && b.RateLimitWorker > 0 {
limits = fmt.Sprintf("(limited to %s QPS per concurrent worker)", printutils.HighlightStr(b.RateLimitWorker))
limits = fmt.Sprintf("(limited to %s QPS per concurrent worker)", printutils.HighlightSprint(b.RateLimitWorker))
}

if !b.Silent && !b.JSON {
network := b.network()
fmt.Fprintf(b.Writer, "Benchmarking %s via %s with %s concurrent requests %s\n", printutils.HighlightStr(b.Server), printutils.HighlightStr(network), printutils.HighlightStr(b.Concurrency), limits)
printutils.NeutralFprintf(b.Writer, "Benchmarking %s via %s with %s concurrent requests %s\n",
printutils.HighlightSprint(b.Server), printutils.HighlightSprint(network), printutils.HighlightSprint(b.Concurrency), limits)
}

var bar *progressbar.ProgressBar
Expand Down
62 changes: 62 additions & 0 deletions pkg/dnsbench/benchmark_plaindns_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_global_ratelimit() {
})
defer s.Close()

buf := bytes.Buffer{}
bench := dnsbench.Benchmark{
Queries: []string{"example.org"},
Types: []string{"A"},
Expand All @@ -502,6 +503,7 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_global_ratelimit() {
RequestTimeout: 5 * time.Second,
Rcodes: true,
Recurse: true,
Writer: &buf,
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand All @@ -513,6 +515,10 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_global_ratelimit() {
// assert that total queries is 5 with +-1 precision, because benchmark cancellation based on duration is not that precise
// and one worker can start the resolution before cancelling
suite.InDelta(int64(5), rs[0].Counters.Total+rs[1].Counters.Total, 1.0)
suite.Equal(
fmt.Sprintf("Using 1 hostnames\nBenchmarking %s via udp with 2 concurrent requests (limited to 1 QPS overall)\n",
s.Addr), buf.String(),
)
}

func (suite *PlainDNSTestSuite) TestBenchmark_Run_worker_ratelimit() {
Expand All @@ -528,6 +534,7 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_worker_ratelimit() {
})
defer s.Close()

buf := bytes.Buffer{}
bench := dnsbench.Benchmark{
Queries: []string{"example.org"},
Types: []string{"A"},
Expand All @@ -543,6 +550,7 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_worker_ratelimit() {
RequestTimeout: 5 * time.Second,
Rcodes: true,
Recurse: true,
Writer: &buf,
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand All @@ -556,6 +564,60 @@ func (suite *PlainDNSTestSuite) TestBenchmark_Run_worker_ratelimit() {
// because benchmark cancellation based on duration is not that precise
// and each worker can start the resolution before cancelling
suite.InDelta(int64(10), rs[0].Counters.Total+rs[1].Counters.Total, 2.0)
suite.Equal(
fmt.Sprintf("Using 1 hostnames\nBenchmarking %s via udp with 2 concurrent requests (limited to 1 QPS per concurrent worker)\n",
s.Addr), buf.String(),
)
}

func (suite *PlainDNSTestSuite) TestBenchmark_Run_global_ratelimit_precendence() {
s := NewServer(dnsbench.UDPTransport, nil, func(w dns.ResponseWriter, r *dns.Msg) {
ret := new(dns.Msg)
ret.SetReply(r)
ret.Answer = append(ret.Answer, A("example.org. IN A 127.0.0.1"))

// wait some time to actually have some observable duration
time.Sleep(time.Millisecond * 100)

w.WriteMsg(ret)
})
defer s.Close()

buf := bytes.Buffer{}
bench := dnsbench.Benchmark{
Queries: []string{"example.org"},
Types: []string{"A"},
Server: s.Addr,
TCP: false,
Concurrency: 2,
Duration: 5 * time.Second,
RateLimitWorker: 2,
Rate: 1,
Probability: 1,
WriteTimeout: 1 * time.Second,
ReadTimeout: 3 * time.Second,
ConnectTimeout: 1 * time.Second,
RequestTimeout: 5 * time.Second,
Rcodes: true,
Recurse: true,
Writer: &buf,
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
rs, err := bench.Run(ctx)

suite.Require().NoError(err, "expected no error from benchmark run")
suite.Require().Len(rs, 2, "expected results from two workers")

// assert that total queries is 10 with +-2 precision,
// because benchmark cancellation based on duration is not that precise
// and each worker can start the resolution before cancelling
suite.InDelta(int64(5), rs[0].Counters.Total+rs[1].Counters.Total, 1.0)
suite.Equal(
fmt.Sprintf("Using 1 hostnames\nBenchmarking %s via udp with 2 concurrent requests (limited to 1 QPS overall and 2 QPS per concurrent worker)\n",
s.Addr), buf.String(),
)
}

func (suite *PlainDNSTestSuite) TestBenchmark_Run_error() {
Expand Down
22 changes: 15 additions & 7 deletions pkg/printutils/printutils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package printutils

import "github.com/fatih/color"
import (
"github.com/fatih/color"
)

var (
// ErrPrint is a wrapper for printing colored errors.
ErrPrint = color.New(color.FgRed).FprintfFunc()
// SuccessPrint is a wrapper for printing colored successes.
SuccessPrint = color.New(color.FgGreen).FprintfFunc()
// HighlightStr is a wrapper for highlighting strings with color.
HighlightStr = color.New(color.FgYellow).SprintFunc()
// ErrFprintf is a wrapper for printing colored errors.
ErrFprintf = color.New(color.FgRed).FprintfFunc()
// SuccessFprintf is a wrapper for printing colored successes.
SuccessFprintf = color.New(color.FgGreen).FprintfFunc()
// NeutralFprintf is a wrapper for printing neutral information.
NeutralFprintf = color.New().FprintfFunc()

highlightColor = color.New(color.FgYellow)
// HighlightSprintf is a wrapper for highlighting formatted strings with color.
HighlightSprintf = highlightColor.SprintfFunc()
// HighlightSprint is a wrapper for highlighting strings with color.
HighlightSprint = highlightColor.SprintFunc()
)
82 changes: 40 additions & 42 deletions pkg/reporter/stdreporter.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package reporter

import (
"fmt"
"io"
"sort"
"strconv"
Expand All @@ -21,12 +20,14 @@ func (s *standardReporter) print(params reportParameters) error {
printProgress(params.outputWriter, params.totalCounters)

if len(params.codeTotals) > 0 {
fmt.Fprintln(params.outputWriter)
fmt.Fprintln(params.outputWriter, "DNS response codes:")
printutils.NeutralFprintf(params.outputWriter, "\nDNS response codes:\n")
for i := dns.RcodeSuccess; i <= dns.RcodeBadCookie; i++ {
printFn := printutils.ErrPrint
printFn := printutils.ErrFprintf
if i == dns.RcodeSuccess {
printFn = printutils.SuccessPrint
printFn = printutils.SuccessFprintf
}
if i == dns.RcodeNameError {
printFn = printutils.NeutralFprintf
}
if c, ok := params.codeTotals[i]; ok {
printFn(params.outputWriter, "\t%s:\t%d\n", dns.RcodeToString[i], c)
Expand All @@ -41,35 +42,33 @@ func (s *standardReporter) print(params reportParameters) error {
sort.Ints(dohResponseStatuses)

if len(params.dohResponseStatusesTotals) > 0 {
fmt.Fprintln(params.outputWriter)
fmt.Fprintln(params.outputWriter, "DoH HTTP response status codes:")
printutils.NeutralFprintf(params.outputWriter, "\nDoH HTTP response status codes:\n")
for _, st := range dohResponseStatuses {
if st == 200 {
printutils.SuccessPrint(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
printutils.SuccessFprintf(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
} else {
printutils.ErrPrint(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
printutils.ErrFprintf(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
}
}
}

if len(params.qtypeTotals) > 0 {
fmt.Fprintln(params.outputWriter)
fmt.Fprintln(params.outputWriter, "DNS question types:")
printutils.NeutralFprintf(params.outputWriter, "\nDNS question types:\n")
for k, v := range params.qtypeTotals {
printutils.SuccessPrint(params.outputWriter, "\t%s:\t%d\n", k, v)
printutils.SuccessFprintf(params.outputWriter, "\t%s:\t%d\n", k, v)
}
}

if params.benchmark.DNSSEC {
fmt.Fprintln(params.outputWriter)
fmt.Fprintln(params.outputWriter, "Number of domains secured using DNSSEC:", printutils.HighlightStr(len(params.authenticatedDomains)))
printutils.NeutralFprintf(params.outputWriter,
"\nNumber of domains secured using DNSSEC: %s\n", printutils.HighlightSprint(len(params.authenticatedDomains)))
}

fmt.Fprintln(params.outputWriter)
printutils.NeutralFprintf(params.outputWriter, "\nTime taken for tests:\t%s\n",
printutils.HighlightSprint(roundDuration(params.benchmarkDuration)))
printutils.NeutralFprintf(params.outputWriter, "Questions per second:\t%s\n",
printutils.HighlightSprintf("%0.1f", float64(params.totalCounters.Total)/params.benchmarkDuration.Seconds()))

fmt.Fprintln(params.outputWriter, "Time taken for tests:\t", printutils.HighlightStr(roundDuration(params.benchmarkDuration).String()))
fmt.Fprintf(params.outputWriter, "Questions per second:\t %s", printutils.HighlightStr(fmt.Sprintf("%0.1f", float64(params.totalCounters.Total)/params.benchmarkDuration.Seconds())))
fmt.Fprintln(params.outputWriter)
min := time.Duration(params.hist.Min())
mean := time.Duration(params.hist.Mean())
sd := time.Duration(params.hist.StdDev())
Expand All @@ -81,22 +80,20 @@ func (s *standardReporter) print(params reportParameters) error {
p50 := time.Duration(params.hist.ValueAtQuantile(50))

if tc := params.hist.TotalCount(); tc > 0 {
fmt.Fprintln(params.outputWriter, "DNS timings,", printutils.HighlightStr(tc), "datapoints")
fmt.Fprintln(params.outputWriter, "\t min:\t\t", printutils.HighlightStr(roundDuration(min)))
fmt.Fprintln(params.outputWriter, "\t mean:\t\t", printutils.HighlightStr(roundDuration(mean)))
fmt.Fprintln(params.outputWriter, "\t [+/-sd]:\t", printutils.HighlightStr(roundDuration(sd)))
fmt.Fprintln(params.outputWriter, "\t max:\t\t", printutils.HighlightStr(roundDuration(max)))
fmt.Fprintln(params.outputWriter, "\t p99:\t\t", printutils.HighlightStr(roundDuration(p99)))
fmt.Fprintln(params.outputWriter, "\t p95:\t\t", printutils.HighlightStr(roundDuration(p95)))
fmt.Fprintln(params.outputWriter, "\t p90:\t\t", printutils.HighlightStr(roundDuration(p90)))
fmt.Fprintln(params.outputWriter, "\t p75:\t\t", printutils.HighlightStr(roundDuration(p75)))
fmt.Fprintln(params.outputWriter, "\t p50:\t\t", printutils.HighlightStr(roundDuration(p50)))
printutils.NeutralFprintf(params.outputWriter, "DNS timings, %s datapoints\n", printutils.HighlightSprint(tc))
printutils.NeutralFprintf(params.outputWriter, "\t min:\t\t%s\n", printutils.HighlightSprint(roundDuration(min)))
printutils.NeutralFprintf(params.outputWriter, "\t mean:\t\t%s\n", printutils.HighlightSprint(roundDuration(mean)))
printutils.NeutralFprintf(params.outputWriter, "\t [+/-sd]:\t%s\n", printutils.HighlightSprint(roundDuration(sd)))
printutils.NeutralFprintf(params.outputWriter, "\t max:\t\t%s\n", printutils.HighlightSprint(roundDuration(max)))
printutils.NeutralFprintf(params.outputWriter, "\t p99:\t\t%s\n", printutils.HighlightSprint(roundDuration(p99)))
printutils.NeutralFprintf(params.outputWriter, "\t p95:\t\t%s\n", printutils.HighlightSprint(roundDuration(p95)))
printutils.NeutralFprintf(params.outputWriter, "\t p90:\t\t%s\n", printutils.HighlightSprint(roundDuration(p90)))
printutils.NeutralFprintf(params.outputWriter, "\t p75:\t\t%s\n", printutils.HighlightSprint(roundDuration(p75)))
printutils.NeutralFprintf(params.outputWriter, "\t p50:\t\t%s\n", printutils.HighlightSprint(roundDuration(p50)))

dist := params.hist.Distribution()
if params.benchmark.HistDisplay && tc > 1 {
fmt.Fprintln(params.outputWriter)
fmt.Fprintln(params.outputWriter, "DNS distribution,", printutils.HighlightStr(tc), "datapoints")

printutils.NeutralFprintf(params.outputWriter, "\nDNS distribution, %s datapoints\n", printutils.HighlightSprint(tc))
printBars(params.outputWriter, dist)
}
}
Expand All @@ -107,39 +104,40 @@ func (s *standardReporter) print(params reportParameters) error {
}

if len(params.topErrs.m) > 0 {
printutils.ErrPrint(params.outputWriter, "\nTotal Errors: %d\n", sumerrs)
printutils.ErrPrint(params.outputWriter, "Top errors:\n")
printutils.ErrFprintf(params.outputWriter, "\nTotal Errors: %d\n", sumerrs)
printutils.ErrFprintf(params.outputWriter, "Top errors:\n")
for _, err := range params.topErrs.order {
printutils.ErrPrint(params.outputWriter, "%s\t%d (%.2f)%%\n", err, params.topErrs.m[err], (float64(params.topErrs.m[err])/float64(sumerrs))*100)
printutils.ErrFprintf(params.outputWriter, "%s\t%d (%.2f)%%\n", err, params.topErrs.m[err],
(float64(params.topErrs.m[err])/float64(sumerrs))*100)
}
}

return nil
}

func printProgress(w io.Writer, c dnsbench.Counters) {
fmt.Fprintf(w, "\nTotal requests:\t\t%s\n", printutils.HighlightStr(c.Total))
printutils.NeutralFprintf(w, "\nTotal requests:\t\t%s\n", printutils.HighlightSprint(c.Total))

if c.IOError > 0 {
printutils.ErrPrint(w, "Read/Write errors:\t%d\n", c.IOError)
printutils.ErrFprintf(w, "Read/Write errors:\t%d\n", c.IOError)
}

if c.IDmismatch > 0 {
printutils.ErrPrint(w, "ID mismatch errors:\t%d\n", c.IDmismatch)
printutils.ErrFprintf(w, "ID mismatch errors:\t%d\n", c.IDmismatch)
}

if c.Success > 0 {
printutils.SuccessPrint(w, "DNS success responses:\t%d\n", c.Success)
printutils.SuccessFprintf(w, "DNS success responses:\t%d\n", c.Success)
}
if c.Negative > 0 {
fmt.Fprintf(w, "DNS negative responses:\t%d\n", c.Negative)
printutils.NeutralFprintf(w, "DNS negative responses:\t%d\n", c.Negative)
}
if c.Error > 0 {
printutils.ErrPrint(w, "DNS error responses:\t%d\n", c.Error)
printutils.ErrFprintf(w, "DNS error responses:\t%d\n", c.Error)
}

if c.Truncated > 0 {
printutils.ErrPrint(w, "Truncated responses:\t%d\n", c.Truncated)
printutils.ErrFprintf(w, "Truncated responses:\t%d\n", c.Truncated)
}
}

Expand Down Expand Up @@ -184,7 +182,7 @@ func makeBar(c int64, max int64) string {
return ""
}
t := int((43 * float64(c) / float64(max)) + 0.5)
return strings.Repeat(printutils.HighlightStr("▄"), t)
return strings.Repeat(printutils.HighlightSprint("▄"), t)
}

func roundDuration(dur time.Duration) time.Duration {
Expand Down
Loading
Loading