From 026384bbdb6f59372cf56d9e637430b4436b28be Mon Sep 17 00:00:00 2001 From: debugtalk Date: Fri, 25 Feb 2022 17:26:02 +0800 Subject: [PATCH] feat: add flag --log-plugin to turn on plugin logging --- boomer.go | 12 ++----- cli/hrp/cmd/run.go | 17 ++++++--- docs/cmd/hrp.md | 2 +- docs/cmd/hrp_boom.md | 2 +- docs/cmd/hrp_har2case.md | 2 +- docs/cmd/hrp_run.md | 7 ++-- docs/cmd/hrp_startproject.md | 2 +- plugin/common/go_plugin_test.go | 2 +- plugin/common/hashicorp_plugin.go | 47 +++++++++++++++++++++--- plugin/common/hashicorp_plugin_test.go | 2 +- plugin/common/init.go | 6 ++-- plugin/host/host.go | 50 -------------------------- runner.go | 37 ++++++++++--------- step_test.go | 2 +- 14 files changed, 92 insertions(+), 98 deletions(-) delete mode 100644 plugin/host/host.go diff --git a/boomer.go b/boomer.go index f67d7248..17090d81 100644 --- a/boomer.go +++ b/boomer.go @@ -16,7 +16,6 @@ func NewBoomer(spawnCount int, spawnRate float64) *HRPBoomer { b := &HRPBoomer{ Boomer: boomer.NewStandaloneBoomer(spawnCount, spawnRate), pluginsMutex: new(sync.RWMutex), - debug: false, } return b } @@ -25,13 +24,6 @@ type HRPBoomer struct { *boomer.Boomer plugins []common.Plugin // each task has its own plugin process pluginsMutex *sync.RWMutex // avoid data race - debug bool -} - -// SetDebug configures whether to log HTTP request and response content. -func (b *HRPBoomer) SetDebug(debug bool) *HRPBoomer { - b.debug = debug - return b } // Run starts to run load test for one or multiple testcases. @@ -75,11 +67,11 @@ func (b *HRPBoomer) Quit() { } func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rendezvous) *boomer.Task { - hrpRunner := NewRunner(nil).SetDebug(b.debug) + hrpRunner := NewRunner(nil) config := testcase.Config // each testcase has its own plugin process - plugin, _ := initPlugin(config.Path) + plugin, _ := initPlugin(config.Path, false) if plugin != nil { b.pluginsMutex.Lock() b.plugins = append(b.plugins, plugin) diff --git a/cli/hrp/cmd/run.go b/cli/hrp/cmd/run.go index 57a03c62..1e5efbc3 100644 --- a/cli/hrp/cmd/run.go +++ b/cli/hrp/cmd/run.go @@ -26,12 +26,17 @@ var runCmd = &cobra.Command{ paths = append(paths, &hrp.TestCasePath{Path: arg}) } runner := hrp.NewRunner(nil). - SetDebug(!silentFlag). SetFailfast(!continueOnFailure). SetSaveTests(saveTests) if genHTMLReport { runner.GenHTMLReport() } + if !requestsLogOff { + runner.SetRequestsLogOn() + } + if pluginLogOn { + runner.SetPluginLogOn() + } if proxyUrl != "" { runner.SetProxyUrl(proxyUrl) } @@ -44,7 +49,8 @@ var runCmd = &cobra.Command{ var ( continueOnFailure bool - silentFlag bool + requestsLogOff bool + pluginLogOn bool proxyUrl string saveTests bool genHTMLReport bool @@ -53,8 +59,9 @@ var ( func init() { rootCmd.AddCommand(runCmd) runCmd.Flags().BoolVarP(&continueOnFailure, "continue-on-failure", "c", false, "continue running next step when failure occurs") - runCmd.Flags().BoolVarP(&silentFlag, "silent", "s", false, "disable logging request & response details") + runCmd.Flags().BoolVar(&requestsLogOff, "disable-log-requests", false, "turn off request & response details logging") + runCmd.Flags().BoolVar(&pluginLogOn, "log-plugin", false, "turn on plugin logging") runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url") - runCmd.Flags().BoolVar(&saveTests, "save-tests", false, "save tests summary") - runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "r", false, "generate html report") + runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary") + runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "g", false, "generate html report") } diff --git a/docs/cmd/hrp.md b/docs/cmd/hrp.md index 04adeda2..e0e66c1d 100644 --- a/docs/cmd/hrp.md +++ b/docs/cmd/hrp.md @@ -33,4 +33,4 @@ Copyright 2021 debugtalk * [hrp run](hrp_run.md) - run API test * [hrp startproject](hrp_startproject.md) - create a scaffold project -###### Auto generated by spf13/cobra on 22-Feb-2022 +###### Auto generated by spf13/cobra on 25-Feb-2022 diff --git a/docs/cmd/hrp_boom.md b/docs/cmd/hrp_boom.md index ba0861dd..bc44844e 100644 --- a/docs/cmd/hrp_boom.md +++ b/docs/cmd/hrp_boom.md @@ -39,4 +39,4 @@ hrp boom [flags] * [hrp](hrp.md) - One-stop solution for HTTP(S) testing. -###### Auto generated by spf13/cobra on 22-Feb-2022 +###### Auto generated by spf13/cobra on 25-Feb-2022 diff --git a/docs/cmd/hrp_har2case.md b/docs/cmd/hrp_har2case.md index 686ed7b1..f9423ae4 100644 --- a/docs/cmd/hrp_har2case.md +++ b/docs/cmd/hrp_har2case.md @@ -23,4 +23,4 @@ hrp har2case $har_path... [flags] * [hrp](hrp.md) - One-stop solution for HTTP(S) testing. -###### Auto generated by spf13/cobra on 22-Feb-2022 +###### Auto generated by spf13/cobra on 25-Feb-2022 diff --git a/docs/cmd/hrp_run.md b/docs/cmd/hrp_run.md index 0cd5715c..4b3adbb3 100644 --- a/docs/cmd/hrp_run.md +++ b/docs/cmd/hrp_run.md @@ -21,9 +21,10 @@ hrp run $path... [flags] ### Options ``` - --continue-on-failure continue running next step when failure occurs - -r, --gen-html-report generate html report + -c, --continue-on-failure continue running next step when failure occurs + -g, --gen-html-report generate html report -h, --help help for run + --plugin-log turn on plugin logging -p, --proxy-url string set proxy url --save-tests save tests summary -s, --silent disable logging request & response details @@ -33,4 +34,4 @@ hrp run $path... [flags] * [hrp](hrp.md) - One-stop solution for HTTP(S) testing. -###### Auto generated by spf13/cobra on 22-Feb-2022 +###### Auto generated by spf13/cobra on 25-Feb-2022 diff --git a/docs/cmd/hrp_startproject.md b/docs/cmd/hrp_startproject.md index b04a4dac..3f9654cb 100644 --- a/docs/cmd/hrp_startproject.md +++ b/docs/cmd/hrp_startproject.md @@ -16,4 +16,4 @@ hrp startproject $project_name [flags] * [hrp](hrp.md) - One-stop solution for HTTP(S) testing. -###### Auto generated by spf13/cobra on 22-Feb-2022 +###### Auto generated by spf13/cobra on 25-Feb-2022 diff --git a/plugin/common/go_plugin_test.go b/plugin/common/go_plugin_test.go index fec89180..6eb1ce90 100644 --- a/plugin/common/go_plugin_test.go +++ b/plugin/common/go_plugin_test.go @@ -70,7 +70,7 @@ func TestCallPluginFunction(t *testing.T) { buildGoPlugin() defer removeGoPlugin() - plugin, err := Init("debugtalk.so") + plugin, err := Init("debugtalk.so", false) if err != nil { t.Fatal(err) } diff --git a/plugin/common/hashicorp_plugin.go b/plugin/common/hashicorp_plugin.go index 199d4af7..c535accb 100644 --- a/plugin/common/hashicorp_plugin.go +++ b/plugin/common/hashicorp_plugin.go @@ -1,25 +1,62 @@ package common import ( - pluginHost "github.com/httprunner/hrp/plugin/host" + "os" + "os/exec" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + "github.com/httprunner/hrp/plugin/shared" pluginShared "github.com/httprunner/hrp/plugin/shared" "github.com/rs/zerolog/log" ) +var client *plugin.Client + // HashicorpPlugin implements hashicorp/go-plugin type HashicorpPlugin struct { + logOn bool // turn on plugin log pluginShared.FuncCaller cachedFunctions map[string]bool // cache loaded functions to improve performance } func (p *HashicorpPlugin) Init(path string) error { + loggerOptions := &hclog.LoggerOptions{ + Name: shared.Name, + Output: os.Stdout, + } + if p.logOn { + loggerOptions.Level = hclog.Debug + } else { + loggerOptions.Level = hclog.Info + } + // launch the plugin process + client = plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: shared.HandshakeConfig, + Plugins: map[string]plugin.Plugin{ + shared.Name: &shared.HashicorpPlugin{}, + }, + Cmd: exec.Command(path), + Logger: hclog.New(loggerOptions), + }) - f, err := pluginHost.Init(path) + // Connect via RPC + rpcClient, err := client.Client() if err != nil { - log.Error().Err(err).Str("path", path).Msg("load go hashicorp plugin failed") + log.Error().Err(err).Msg("connect plugin via RPC failed") return err } - p.FuncCaller = f + + // Request the plugin + raw, err := rpcClient.Dispense(shared.Name) + if err != nil { + log.Error().Err(err).Msg("request plugin failed") + return err + } + + // We should have a Function now! This feels like a normal interface + // implementation but is in fact over an RPC connection. + p.FuncCaller = raw.(shared.FuncCaller) p.cachedFunctions = make(map[string]bool) log.Info().Str("path", path).Msg("load hashicorp go plugin success") @@ -55,6 +92,6 @@ func (p *HashicorpPlugin) Call(funcName string, args ...interface{}) (interface{ func (p *HashicorpPlugin) Quit() error { // kill hashicorp plugin process log.Info().Msg("quit hashicorp plugin process") - pluginHost.Quit() + client.Kill() return nil } diff --git a/plugin/common/hashicorp_plugin_test.go b/plugin/common/hashicorp_plugin_test.go index a0c6a806..2e374041 100644 --- a/plugin/common/hashicorp_plugin_test.go +++ b/plugin/common/hashicorp_plugin_test.go @@ -28,7 +28,7 @@ func TestInitHashicorpPlugin(t *testing.T) { buildHashicorpPlugin() defer removeHashicorpPlugin() - plugin, err := Init("../../examples/debugtalk.bin") + plugin, err := Init("../../examples/debugtalk.bin", false) if err != nil { t.Fatal(err) } diff --git a/plugin/common/init.go b/plugin/common/init.go index 35aa0a1c..c23c4bf3 100644 --- a/plugin/common/init.go +++ b/plugin/common/init.go @@ -23,7 +23,7 @@ type Plugin interface { Quit() error // quit plugin } -func Init(path string) (Plugin, error) { +func Init(path string, logOn bool) (Plugin, error) { if path == "" { return nil, nil } @@ -34,7 +34,9 @@ func Init(path string) (Plugin, error) { pluginPath, err := locateFile(path, hashicorpGoPluginFile) if err == nil { // found hashicorp go plugin file - plugin = &HashicorpPlugin{} + plugin = &HashicorpPlugin{ + logOn: logOn, + } err = plugin.Init(pluginPath) return plugin, err } diff --git a/plugin/host/host.go b/plugin/host/host.go deleted file mode 100644 index e815c7e5..00000000 --- a/plugin/host/host.go +++ /dev/null @@ -1,50 +0,0 @@ -package host - -import ( - "os" - "os/exec" - - hclog "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-plugin" - - "github.com/httprunner/hrp/plugin/shared" -) - -var client *plugin.Client - -func Init(path string) (shared.FuncCaller, error) { - // launch the plugin process - client = plugin.NewClient(&plugin.ClientConfig{ - HandshakeConfig: shared.HandshakeConfig, - Plugins: map[string]plugin.Plugin{ - shared.Name: &shared.HashicorpPlugin{}, - }, - Cmd: exec.Command(path), - Logger: hclog.New(&hclog.LoggerOptions{ - Name: shared.Name, - Output: os.Stdout, - Level: hclog.Info, - }), - }) - - // Connect via RPC - rpcClient, err := client.Client() - if err != nil { - return nil, err - } - - // Request the plugin - raw, err := rpcClient.Dispense(shared.Name) - if err != nil { - return nil, err - } - - // We should have a Function now! This feels like a normal interface - // implementation but is in fact over an RPC connection. - function := raw.(shared.FuncCaller) - return function, nil -} - -func Quit() { - client.Kill() -} diff --git a/runner.go b/runner.go index f9f4a338..2b12761d 100644 --- a/runner.go +++ b/runner.go @@ -43,7 +43,7 @@ const ( // Run starts to run API test with default configs. func Run(testcases ...ITestCase) error { t := &testing.T{} - return NewRunner(t).SetDebug(true).Run(testcases...) + return NewRunner(t).SetRequestsLogOn().Run(testcases...) } // NewRunner constructs a new runner instance. @@ -53,8 +53,7 @@ func NewRunner(t *testing.T) *HRPRunner { } return &HRPRunner{ t: t, - failfast: true, // default to failfast - debug: false, // default to turn off debug + failfast: true, // default to failfast genHTMLReport: false, client: &http.Client{ Transport: &http.Transport{ @@ -68,7 +67,8 @@ func NewRunner(t *testing.T) *HRPRunner { type HRPRunner struct { t *testing.T failfast bool - debug bool + requestsLogOn bool + pluginLogOn bool saveTests bool genHTMLReport bool client *http.Client @@ -81,10 +81,17 @@ func (r *HRPRunner) SetFailfast(failfast bool) *HRPRunner { return r } -// SetDebug configures whether to log HTTP request and response content. -func (r *HRPRunner) SetDebug(debug bool) *HRPRunner { - log.Info().Bool("debug", debug).Msg("[init] SetDebug") - r.debug = debug +// SetRequestsLogOn turns on request & response details logging. +func (r *HRPRunner) SetRequestsLogOn() *HRPRunner { + log.Info().Msg("[init] SetRequestsLogOn") + r.requestsLogOn = true + return r +} + +// SetPluginLogOn turns on plugin logging. +func (r *HRPRunner) SetPluginLogOn() *HRPRunner { + log.Info().Msg("[init] SetPluginLogOn") + r.pluginLogOn = true return r } @@ -221,7 +228,7 @@ func (r *caseRunner) run() error { config := r.TestCase.Config // init plugin var err error - if r.parser.plugin, err = initPlugin(config.Path); err != nil { + if r.parser.plugin, err = initPlugin(config.Path, r.hrpRunner.pluginLogOn); err != nil { return err } defer func() { @@ -247,9 +254,7 @@ func (r *caseRunner) run() error { // merge test case if the step is test case summary, ok := stepDataObj.Data.(*testCaseSummary) if ok { - for _, rc := range summary.Records { - r.summary.Records = append(r.summary.Records, rc) - } + r.summary.Records = append(r.summary.Records, summary.Records...) r.summary.Stat.Total += summary.Stat.Total r.summary.Stat.Successes += summary.Stat.Successes r.summary.Stat.Failures += summary.Stat.Failures @@ -277,8 +282,8 @@ func (r *caseRunner) run() error { return nil } -func initPlugin(path string) (plugin common.Plugin, err error) { - plugin, err = common.Init(path) +func initPlugin(path string, logOn bool) (plugin common.Plugin, err error) { + plugin, err = common.Init(path, logOn) if plugin == nil { return } @@ -836,7 +841,7 @@ func (r *caseRunner) runStepRequest(step *TStep) (stepResult *stepData, err erro } func (r *caseRunner) printRequest(req *http.Request) error { - if !r.hrpRunner.debug { + if !r.hrpRunner.requestsLogOn { return nil } reqContentType := req.Header.Get("Content-Type") @@ -855,7 +860,7 @@ func (r *caseRunner) printRequest(req *http.Request) error { } func (r *caseRunner) printResponse(resp *http.Response) error { - if !r.hrpRunner.debug { + if !r.hrpRunner.requestsLogOn { return nil } fmt.Println("==================== response ===================") diff --git a/step_test.go b/step_test.go index b81b48d3..f180983f 100644 --- a/step_test.go +++ b/step_test.go @@ -77,7 +77,7 @@ func TestRunRequestRun(t *testing.T) { Config: NewConfig("test").SetBaseURL("https://postman-echo.com"), TestSteps: []IStep{stepGET, stepPOSTData}, } - runner := NewRunner(t).SetDebug(true).newCaseRunner(testcase) + runner := NewRunner(t).SetRequestsLogOn().newCaseRunner(testcase) if _, err := runner.runStep(0, testcase.Config); err != nil { t.Fatalf("tStep.Run() error: %s", err) }