Skip to content

Commit 105ce68

Browse files
authoredJul 12, 2024··
Merge pull request #267 from form3tech-oss/nvloff-user-logger
feat: support user provided loggers
2 parents 263e174 + 94941ac commit 105ce68

File tree

4 files changed

+84
-10
lines changed

4 files changed

+84
-10
lines changed
 

‎internal/ui/output.go

+7
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,10 @@ func NewDefaultOutput(logLevel slog.Level, jsonFormat bool) *Output {
5858

5959
return NewOutput(logger, printer, interactive, true)
6060
}
61+
62+
func NewDefaultOutputWithLogger(logger *slog.Logger) *Output {
63+
printer := NewDefaultPrinter()
64+
interactive := isatty.IsTerminal(os.Stdin.Fd())
65+
66+
return NewOutput(logger, printer, interactive, true)
67+
}

‎pkg/f1/f1.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"log/slog"
78
"os"
89
"os/signal"
910
"syscall"
@@ -32,7 +33,7 @@ type F1 struct {
3233
settings envsettings.Settings
3334
}
3435

35-
// Instantiates a new instance of an F1 CLI.
36+
// New instantiates a new instance of an F1 CLI.
3637
func New() *F1 {
3738
settings := envsettings.Get()
3839

@@ -44,6 +45,17 @@ func New() *F1 {
4445
}
4546
}
4647

48+
// WithLogger allows specifying logger to be used for all internal and scenario logs
49+
//
50+
// This will disable the LOG_LEVEL and LOG_FORMAT options, as they only relate to the built-in
51+
// logger.
52+
//
53+
// The logger will be used for non-interactive output, file logs or when `--verbose` is specified.
54+
func (f *F1) WithLogger(logger *slog.Logger) *F1 {
55+
f.output = ui.NewDefaultOutputWithLogger(logger)
56+
return f
57+
}
58+
4759
// Registers a new test scenario with the given name. This is the name used when running
4860
// load test scenarios. For example, calling the function with the following arguments:
4961
//

‎pkg/f1/f1_stage_test.go

+47-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package f1_test
22

33
import (
4+
"bytes"
5+
"fmt"
46
"os"
7+
"strings"
58
"sync/atomic"
69
"syscall"
710
"testing"
@@ -11,21 +14,21 @@ import (
1114
"github.com/stretchr/testify/require"
1215
"go.uber.org/goleak"
1316

17+
"github.com/form3tech-oss/f1/v2/internal/log"
1418
"github.com/form3tech-oss/f1/v2/pkg/f1"
1519
f1_testing "github.com/form3tech-oss/f1/v2/pkg/f1/testing"
1620
)
1721

1822
type f1Stage struct {
19-
t *testing.T
20-
assert *assert.Assertions
21-
require *require.Assertions
22-
23-
f1 *f1.F1
24-
errCh chan error
25-
scenario string
26-
runCount atomic.Uint32
27-
2823
executeErr error
24+
t *testing.T
25+
assert *assert.Assertions
26+
require *require.Assertions
27+
f1 *f1.F1
28+
errCh chan error
29+
scenario string
30+
logOutput bytes.Buffer
31+
runCount atomic.Uint32
2932
}
3033

3134
func newF1Stage(t *testing.T) (*f1Stage, *f1Stage, *f1Stage) {
@@ -46,6 +49,13 @@ func (s *f1Stage) and() *f1Stage {
4649
return s
4750
}
4851

52+
func (s *f1Stage) a_custom_logger_is_configured_with_attr(key, value string) *f1Stage {
53+
logger := log.NewTestLogger(&s.logOutput).With(key, value)
54+
s.f1 = f1.New().WithLogger(logger)
55+
56+
return s
57+
}
58+
4959
func (s *f1Stage) after_duration_signal_will_be_sent(duration time.Duration, signal syscall.Signal) *f1Stage {
5060
go func() {
5161
time.Sleep(duration)
@@ -74,6 +84,20 @@ func (s *f1Stage) a_scenario_where_each_iteration_takes(duration time.Duration)
7484
return s
7585
}
7686

87+
func (s *f1Stage) a_scenario_that_logs() *f1Stage {
88+
s.scenario = "logging_scenario"
89+
s.f1.Add(s.scenario, func(sceanrioT *f1_testing.T) f1_testing.RunFn {
90+
sceanrioT.Log("scenario")
91+
92+
return func(*f1_testing.T) {
93+
sceanrioT.Log("iteration")
94+
sceanrioT.Logger().Info("iteration")
95+
}
96+
})
97+
98+
return s
99+
}
100+
77101
func (s *f1Stage) the_f1_scenario_is_executed_with_constant_rate_and_args(args ...string) *f1Stage {
78102
err := s.f1.ExecuteWithArgs(append([]string{
79103
"run", "constant", s.scenario,
@@ -115,3 +139,17 @@ func (s *f1Stage) expect_no_goroutines_to_run() *f1Stage {
115139

116140
return s
117141
}
142+
143+
func (s *f1Stage) expect_all_log_lines_to_contain_attr(key, value string) *f1Stage {
144+
lines := strings.Split(s.logOutput.String(), "\n")
145+
146+
s.require.Len(lines, 7)
147+
148+
for _, line := range lines {
149+
if line != "" {
150+
s.require.Contains(line, fmt.Sprintf(" %s=%s ", key, value))
151+
}
152+
}
153+
154+
return s
155+
}

‎pkg/f1/f1_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,20 @@ func TestMissingScenario(t *testing.T) {
4545
then.
4646
the_execute_command_returns_an_error("scenario not defined: unknownScenario")
4747
}
48+
49+
func TestWithCustomLogger(t *testing.T) {
50+
given, when, then := newF1Stage(t)
51+
52+
given.
53+
a_custom_logger_is_configured_with_attr("custom", "value").and().
54+
a_scenario_that_logs()
55+
56+
when.
57+
the_f1_scenario_is_executed_with_constant_rate_and_args(
58+
"--rate", "1/1s",
59+
"--max-duration", "2s",
60+
)
61+
62+
then.
63+
expect_all_log_lines_to_contain_attr("custom", "value")
64+
}

0 commit comments

Comments
 (0)
Please sign in to comment.