From c146a160fb2a3768ed9ab628772848d1dd95e9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20P=2E=20Barazzutti?= Date: Mon, 21 Aug 2023 12:59:32 +0200 Subject: [PATCH] all writes are passing thru cobra --- app/consolelogger.go | 50 +++++++++++++++++++++++++++++++++++++++++ app/httpping.go | 24 +++++++++----------- app/httpping_test.go | 11 ++++++++- app/pinger.go | 5 ++--- app/pinger_test.go | 2 +- app/pinglogger.go | 22 +++++++++--------- app/tput.go | 1 + app/webclient_test.go | 4 ++-- app/webclientbuilder.go | 7 +++--- app/webclientimpl.go | 10 +++++---- cmd/root.go | 9 ++++---- cmd/root_test.go | 2 +- 12 files changed, 101 insertions(+), 46 deletions(-) create mode 100644 app/consolelogger.go diff --git a/app/consolelogger.go b/app/consolelogger.go new file mode 100644 index 0000000..33fa119 --- /dev/null +++ b/app/consolelogger.go @@ -0,0 +1,50 @@ +// Copyright 2022 Raphaël P. Barazzutti +// +// 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. +// +// SPDX-License-Identifier: Apache-2.0 + +package app + +import ( + "fmt" + "github.com/spf13/cobra" +) + +type ConsoleLogger interface { + Printf(format string, a ...any) (int, error) +} + +func NewConsoleBasicLogger() ConsoleLogger { + return &consoleLoggerImpl{} +} + +type consoleLoggerImpl struct { +} + +func (logger *consoleLoggerImpl) Printf(format string, a ...any) (int, error) { + return fmt.Printf(format, a...) +} + +func NewConsoleCobraLogger(command *cobra.Command) ConsoleLogger { + return &consoleLoggerCmdImpl{cmd: command} +} + +type consoleLoggerCmdImpl struct { + cmd *cobra.Command +} + +func (logger *consoleLoggerCmdImpl) Printf(format string, a ...any) (int, error) { + logger.cmd.Printf(format, a...) + return 0, nil +} diff --git a/app/httpping.go b/app/httpping.go index b38672f..3631a6a 100644 --- a/app/httpping.go +++ b/app/httpping.go @@ -18,8 +18,6 @@ package app import ( "fever.ch/http-ping/stats" - "fmt" - "io" "os" "os/signal" "time" @@ -32,9 +30,8 @@ type HTTPPing interface { type httpPingImpl struct { config *Config - stdout io.Writer pinger Pinger - logger logger + logger PingLogger } type httpPingTestVersion struct { @@ -78,7 +75,7 @@ func (h *httpPingTestVersion) checkHttp(prep func(*Config)) <-chan string { prep(&configCopy) rc := RuntimeConfig{} - wc, _ := newWebClient(&configCopy, &rc) + wc, _ := newWebClient(&configCopy, &rc, h.logger) m := wc.DoMeasure(false) http3Advertisement := "" @@ -98,40 +95,39 @@ func (h *httpPingTestVersion) checkHttp(prep func(*Config)) <-chan string { } // NewHTTPPing builds a new instance of HTTPPing or error if something goes wrong -func NewHTTPPing(config *Config, stdout io.Writer) (HTTPPing, error) { +func NewHTTPPing(config *Config, consoleLogger ConsoleLogger) (HTTPPing, error) { runtimeConfig := &RuntimeConfig{ RedirectCallBack: func(url string) { - _, _ = fmt.Fprintf(stdout, " ─→ redirected to %s\n", url) + _, _ = consoleLogger.Printf(" ─→ redirected to %s\n", url) }, } - pinger, err := NewPinger(config, runtimeConfig) + pinger, err := NewPinger(config, runtimeConfig, consoleLogger) if err != nil { return nil, err } - var logger logger + var logger PingLogger if config.LogLevel == 0 { - logger = newQuietLogger(config, stdout, pinger) + logger = newQuietLogger(config, consoleLogger, pinger) } else if config.LogLevel == 2 { - logger = newVerboseLogger(config, stdout, pinger) + logger = newVerboseLogger(config, consoleLogger, pinger) } else { - logger = newStandardLogger(config, stdout, pinger) + logger = newStandardLogger(config, consoleLogger, pinger) } if config.TestVersion { return &httpPingTestVersion{ baseConfig: config, - logger: newStandardLogger(config, stdout, pinger), + logger: newStandardLogger(config, consoleLogger, pinger), }, nil } return &httpPingImpl{ config: config, - stdout: stdout, pinger: pinger, logger: logger, }, nil diff --git a/app/httpping_test.go b/app/httpping_test.go index 7525b80..540a815 100644 --- a/app/httpping_test.go +++ b/app/httpping_test.go @@ -19,16 +19,25 @@ package app import ( "bytes" "fever.ch/http-ping/stats" + "fmt" "io" "strings" "testing" ) +type consoleLoggerMock struct { + b io.Writer +} + +func (logger *consoleLoggerMock) Printf(format string, a ...any) (int, error) { + return fmt.Fprintf(logger.b, format, a...) +} + type PingerMock struct{} func TestHTTPPing(t *testing.T) { b := bytes.NewBufferString("") - instance, _ := NewHTTPPing(&Config{Count: 10}, b) + instance, _ := NewHTTPPing(&Config{Count: 10}, &consoleLoggerMock{b: b}) instance.(*httpPingImpl).pinger = &PingerMock{} _ = instance.Run() diff --git a/app/pinger.go b/app/pinger.go index 8481ca2..bf8898a 100644 --- a/app/pinger.go +++ b/app/pinger.go @@ -59,13 +59,13 @@ type pingerImpl struct { } // NewPinger builds a new pingerImpl -func NewPinger(config *Config, runtimeConfig *RuntimeConfig) (Pinger, error) { +func NewPinger(config *Config, runtimeConfig *RuntimeConfig, logger ConsoleLogger) (Pinger, error) { pinger := pingerImpl{} pinger.config = config - client, err := NewWebClientBuilder(config, runtimeConfig) + client, err := NewWebClientBuilder(config, runtimeConfig, logger) if err != nil { return nil, fmt.Errorf("%s (%s)", err, config.IPProtocol) } @@ -89,7 +89,6 @@ func (pinger *pingerImpl) Ping() <-chan *HTTPMeasure { i := pinger.clientBuilder.NewInstance() i.DoMeasure(true) pinger.clientBuilder.SetURL(i.GetURL()) - } for i := 0; i < pinger.config.Workers; i++ { diff --git a/app/pinger_test.go b/app/pinger_test.go index aa21d78..a2c577b 100644 --- a/app/pinger_test.go +++ b/app/pinger_test.go @@ -27,7 +27,7 @@ type webClientBuilderMock struct{} func TestPinger(t *testing.T) { wanted := 123 - pinger, _ := NewPinger(&Config{Workers: 1, Count: int64(wanted)}, &RuntimeConfig{}) + pinger, _ := NewPinger(&Config{Workers: 1, Count: int64(wanted)}, &RuntimeConfig{}, nil) pinger.(*pingerImpl).clientBuilder = &webClientBuilderMock{} ch := pinger.Ping() diff --git a/app/pinglogger.go b/app/pinglogger.go index ba3ea1b..fd9005a 100644 --- a/app/pinglogger.go +++ b/app/pinglogger.go @@ -18,13 +18,11 @@ package app import ( "fever.ch/http-ping/stats" - "fmt" - "io" "strings" "time" ) -type logger interface { +type PingLogger interface { onMeasure(httpMeasure *HTTPMeasure) onTick(measure throughputMeasure) onClose() @@ -41,18 +39,18 @@ type measures struct { type quietLogger struct { config *Config - stdout io.Writer + consoleLogger ConsoleLogger pinger Pinger measures measures throughputMeasures []throughputMeasure } -func newQuietLogger(config *Config, stdout io.Writer, pinger Pinger) logger { - return &quietLogger{config: config, stdout: stdout, pinger: pinger} +func newQuietLogger(config *Config, consoleLogger ConsoleLogger, pinger Pinger) PingLogger { + return &quietLogger{config: config, consoleLogger: consoleLogger, pinger: pinger} } func (logger *quietLogger) Printf(format string, a ...any) (int, error) { - return fmt.Fprintf(logger.stdout,format, a...) + return logger.consoleLogger.Printf(format, a...) } func (logger *quietLogger) onMeasure(m *HTTPMeasure) { @@ -121,8 +119,8 @@ type standardLogger struct { quietLogger } -func newStandardLogger(config *Config, stdout io.Writer, pinger Pinger) *standardLogger { - return &standardLogger{quietLogger{config: config, stdout: stdout, pinger: pinger}} +func newStandardLogger(config *Config, consoleLogger ConsoleLogger, pinger Pinger) *standardLogger { + return &standardLogger{quietLogger{config: config, consoleLogger: consoleLogger, pinger: pinger}} } func (logger *standardLogger) onMeasure(measure *HTTPMeasure) { @@ -140,7 +138,7 @@ func (logger *standardLogger) onMeasure(measure *HTTPMeasure) { func (logger *standardLogger) onTick(m throughputMeasure) { logger.quietLogger.onTick(m) - fmt.Printf(" throughput: %s queries/sec, average latency: %.1f ms\n", m.String(), m.queriesDuration.ToFloat(time.Microsecond)/float64(1000*m.count)) + logger.Printf(" throughput: %s queries/sec, average latency: %.1f ms\n", m.String(), m.queriesDuration.ToFloat(time.Microsecond)/float64(1000*m.count)) } func (logger *standardLogger) onClose() { @@ -160,9 +158,9 @@ func (logger *quietLogger) bell() { } } -func newVerboseLogger(config *Config, stdout io.Writer, pinger Pinger) logger { +func newVerboseLogger(config *Config, consoleLogger ConsoleLogger, pinger Pinger) PingLogger { return &verboseLogger{ - standardLogger: *newStandardLogger(config, stdout, pinger), + standardLogger: *newStandardLogger(config, consoleLogger, pinger), measureSum: &HTTPMeasure{ MeasuresCollection: stats.NewMeasureRegistry(), }, diff --git a/app/tput.go b/app/tput.go index b1e9bc6..de43191 100644 --- a/app/tput.go +++ b/app/tput.go @@ -26,6 +26,7 @@ type throughputMeasurer struct { ts time.Time counter uint64 duration stats.Measure + logger *PingLogger } type throughputMeasure struct { diff --git a/app/webclient_test.go b/app/webclient_test.go index 69c077e..21584c5 100644 --- a/app/webclient_test.go +++ b/app/webclient_test.go @@ -42,14 +42,14 @@ func TestWithEmbeddedWebServer(t *testing.T) { var webClient WebClientBuilder var measure *HTTPMeasure - webClient, _ = NewWebClientBuilder(&Config{Target: fmt.Sprintf("%s/500", url), NoCheckCertificate: true}, &RuntimeConfig{}) + webClient, _ = NewWebClientBuilder(&Config{Target: fmt.Sprintf("%s/500", url), NoCheckCertificate: true}, &RuntimeConfig{}, nil) measure = webClient.NewInstance().DoMeasure(false) if !measure.IsFailure || measure.StatusCode != 500 { t.Errorf("Request to server should have failed, 500") } - webClient, _ = NewWebClientBuilder(&Config{Target: fmt.Sprintf("%s/200", url), NoCheckCertificate: true}, &RuntimeConfig{}) + webClient, _ = NewWebClientBuilder(&Config{Target: fmt.Sprintf("%s/200", url), NoCheckCertificate: true}, &RuntimeConfig{}, nil) measure = webClient.NewInstance().DoMeasure(false) if measure.IsFailure || measure.StatusCode != 200 { diff --git a/app/webclientbuilder.go b/app/webclientbuilder.go index 0b54246..8db1312 100644 --- a/app/webclientbuilder.go +++ b/app/webclientbuilder.go @@ -37,11 +37,12 @@ type webClientBuilderImpl struct { runtimeConfig *RuntimeConfig url *url.URL resolver *resolver + logger ConsoleLogger } // NewWebClientBuilder builds a new instance of webClientImpl which will provides functions for Http-Ping -func NewWebClientBuilder(config *Config, runtimeConfig *RuntimeConfig) (WebClientBuilder, error) { - webClient := webClientBuilderImpl{config: config, runtimeConfig: runtimeConfig} +func NewWebClientBuilder(config *Config, runtimeConfig *RuntimeConfig, logger ConsoleLogger) (WebClientBuilder, error) { + webClient := webClientBuilderImpl{config: config, runtimeConfig: runtimeConfig, logger: logger} parsedURL, err := url.Parse(config.Target) if err != nil { return nil, err @@ -66,7 +67,7 @@ func (webClientBuilder *webClientBuilderImpl) GetURL() *url.URL { } func (webClientBuilder *webClientBuilderImpl) NewInstance() WebClient { - w, _ := newWebClient(webClientBuilder.config, webClientBuilder.runtimeConfig) + w, _ := newWebClient(webClientBuilder.config, webClientBuilder.runtimeConfig, webClientBuilder.logger) w.url = webClientBuilder.url w.updateConnTarget() diff --git a/app/webclientimpl.go b/app/webclientimpl.go index 42dc719..50029a1 100644 --- a/app/webclientimpl.go +++ b/app/webclientimpl.go @@ -40,6 +40,7 @@ type webClientImpl struct { runtimeConfig *RuntimeConfig url *url.URL resolver *resolver + logger ConsoleLogger writes int64 reads int64 @@ -69,7 +70,7 @@ func (webClient *webClientImpl) updateConnTarget() { func newHttp2RoundTripper(config *Config, runtimeConfig *RuntimeConfig, w *webClientImpl) (http.RoundTripper, error) { - webClient := webClientImpl{config: config, runtimeConfig: runtimeConfig} + webClient := webClientImpl{config: config, runtimeConfig: runtimeConfig, logger: w.logger} parsedURL, err := url.Parse(config.Target) if err != nil { return nil, err @@ -144,8 +145,8 @@ func newTransport(config *Config, runtimeConfig *RuntimeConfig, w *webClientImpl return newHttp2RoundTripper(config, runtimeConfig, w) } -func newWebClient(config *Config, runtimeConfig *RuntimeConfig) (*webClientImpl, error) { - webClient := &webClientImpl{} +func newWebClient(config *Config, runtimeConfig *RuntimeConfig, logger ConsoleLogger) (*webClientImpl, error) { + webClient := &webClientImpl{logger: logger} err := webClient.update(config, runtimeConfig) @@ -267,7 +268,8 @@ func (webClient *webClientImpl) DoMeasure(followRedirect bool) *HTTPMeasure { altSvcH3 := checkAltSvcH3Header(res.Header) if !strings.HasPrefix(req.RequestURI, "http://") && altSvcH3 != nil && !strings.HasPrefix(res.Proto, "HTTP/3") && !webClient.config.Http1 && !webClient.config.Http2 { - _, _ = fmt.Printf(" ─→ server advertised HTTP/3 endpoint, using HTTP/3\n") + + webClient.logger.Printf(" ─→ server advertised HTTP/3 endpoint, using HTTP/3\n") return webClient.moveToHttp3(*altSvcH3, measureContext.timerRegistry, followRedirect) } diff --git a/cmd/root.go b/cmd/root.go index 16a00a2..b59a0a8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,7 +22,6 @@ import ( "fmt" "github.com/spf13/cobra" "github.com/spf13/pflag" - "io" "math" "net" "net/http" @@ -56,7 +55,7 @@ type extraConfig struct { type runner struct { config *app.Config xp *extraConfig - appLogic func(config *app.Config, stdout io.Writer) (app.HTTPPing, error) + appLogic func(config *app.Config, consoleLogger app.ConsoleLogger) (app.HTTPPing, error) cmd *cobra.Command args []string } @@ -77,7 +76,7 @@ func (runner *runner) run() error { } } - instance, err := runner.appLogic(runner.config, runner.cmd.OutOrStdout()) + instance, err := runner.appLogic(runner.config, app.NewConsoleCobraLogger(runner.cmd)) if err != nil { return err } @@ -210,7 +209,7 @@ func splitPair(str string) (string, string, error) { return "", "", fmt.Errorf("format should be \"key=value\", where key is a non-empty string of alphanumberic characters and value any string, illegal format: \"%s\"", str) } -func runAndError(config *app.Config, xp *extraConfig, appLogic func(config *app.Config, stdout io.Writer) (app.HTTPPing, error)) func(cmd *cobra.Command, args []string) error { +func runAndError(config *app.Config, xp *extraConfig, appLogic func(config *app.Config, consoleLogger app.ConsoleLogger) (app.HTTPPing, error)) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { r := runner{appLogic: appLogic, config: config, xp: xp, cmd: cmd, args: args} @@ -218,7 +217,7 @@ func runAndError(config *app.Config, xp *extraConfig, appLogic func(config *app. } } -func prepareRootCmd(appLogic func(config *app.Config, stdout io.Writer) (app.HTTPPing, error)) *cobra.Command { +func prepareRootCmd(appLogic func(config *app.Config, consoleLogger app.ConsoleLogger) (app.HTTPPing, error)) *cobra.Command { var config = &app.Config{} diff --git a/cmd/root_test.go b/cmd/root_test.go index 40d860f..a56eacf 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -33,7 +33,7 @@ func (httpPingMock *httpPingMock) Run() error { return nil } -func (httpPingMockBuilder *httpPingMockBuilder) newHTTPPingMock(config *app.Config, _ io.Writer) (app.HTTPPing, error) { +func (httpPingMockBuilder *httpPingMockBuilder) newHTTPPingMock(config *app.Config, _ app.ConsoleLogger) (app.HTTPPing, error) { httpPingMockBuilder.config = config return &httpPingMock{}, nil }