Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package fortio for openSUSE:Factory checked in at 2023-07-25 11:51:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fortio (Old) and /work/SRC/openSUSE:Factory/.fortio.new.1467 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "fortio" Tue Jul 25 11:51:32 2023 rev:45 rq:1100521 version:1.57.4 Changes: -------- --- /work/SRC/openSUSE:Factory/fortio/fortio.changes 2023-07-13 17:18:40.697163226 +0200 +++ /work/SRC/openSUSE:Factory/.fortio.new.1467/fortio.changes 2023-07-25 11:53:27.094277464 +0200 @@ -1,0 +2,8 @@ +Tue Jul 25 05:27:58 UTC 2023 - ka...@b1-systems.de + +- Update to version 1.57.4: + * Save the async result file even on init error (like invalid + host/target) (#797) + * Bump docker/setup-buildx-action from 2.9.0 to 2.9.1 (#795) + +------------------------------------------------------------------- Old: ---- fortio-1.57.3.obscpio New: ---- fortio-1.57.4.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fortio.spec ++++++ --- /var/tmp/diff_new_pack.yJU0ar/_old 2023-07-25 11:53:27.698281001 +0200 +++ /var/tmp/diff_new_pack.yJU0ar/_new 2023-07-25 11:53:27.706281048 +0200 @@ -19,7 +19,7 @@ %define __arch_install_post export NO_BRP_STRIP_DEBUG=true Name: fortio -Version: 1.57.3 +Version: 1.57.4 Release: 0 Summary: Load testing library, command line tool, advanced echo server and web UI License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.yJU0ar/_old 2023-07-25 11:53:27.742281258 +0200 +++ /var/tmp/diff_new_pack.yJU0ar/_new 2023-07-25 11:53:27.746281281 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/fortio/fortio</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v1.57.3</param> + <param name="revision">v1.57.4</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.yJU0ar/_old 2023-07-25 11:53:27.770281422 +0200 +++ /var/tmp/diff_new_pack.yJU0ar/_new 2023-07-25 11:53:27.774281446 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/fortio/fortio</param> - <param name="changesrevision">c1c5e58906de3bb19779c1545d3a70bd649b3e63</param></service></servicedata> + <param name="changesrevision">67b7744c810d8520599076765f037009164c9f8f</param></service></servicedata> (No newline at EOF) ++++++ fortio-1.57.3.obscpio -> fortio-1.57.4.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/.circleci/config.yml new/fortio-1.57.4/.circleci/config.yml --- old/fortio-1.57.3/.circleci/config.yml 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/.circleci/config.yml 2023-07-25 06:29:26.000000000 +0200 @@ -32,13 +32,13 @@ - checkout # If ran with default we get random errors because OOM killer kills some linters - run: make local-lint DEBUG_LINTERS="--concurrency=2" - codecov: - <<: *defaultEnv - steps: - - checkout - - run: make coverage - # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass - resource_class: large + # codecov: + # <<: *defaultEnv + # steps: + # - checkout + # - run: make coverage + # # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass + # resource_class: large workflows: version: 2 @@ -46,5 +46,5 @@ jobs: - unit-tests - release-tests - - codecov +# - codecov - linters diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/.circleci/coverage.sh new/fortio-1.57.4/.circleci/coverage.sh --- old/fortio-1.57.3/.circleci/coverage.sh 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/.circleci/coverage.sh 2023-07-25 06:29:26.000000000 +0200 @@ -2,8 +2,10 @@ set -e echo "" > coverage.txt +rm -f profile.out for d in $(go list ./... | grep -v vendor); do + echo "### Working on package coverage $d" go test -coverprofile=profile.out -covermode=atomic $d if [ -f profile.out ]; then cat profile.out >> coverage.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/.github/workflows/codecov.yml new/fortio-1.57.4/.github/workflows/codecov.yml --- old/fortio-1.57.3/.github/workflows/codecov.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/fortio-1.57.4/.github/workflows/codecov.yml 2023-07-25 06:29:26.000000000 +0200 @@ -0,0 +1,25 @@ +name: Test Coverage + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3 + with: + fetch-depth: 2 + - name: Set up Go + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # pin@v4 + with: + go-version: '1.20' + check-latest: true + - name: Run coverage + run: make coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # pin@v3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/.github/workflows/main.yml new/fortio-1.57.4/.github/workflows/main.yml --- old/fortio-1.57.3/.github/workflows/main.yml 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/.github/workflows/main.yml 2023-07-25 06:29:26.000000000 +0200 @@ -29,7 +29,7 @@ - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@2a1a44ac4aa01993040736bd95bb470da1a38365 # pin@v2 + uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # pin@v2 - name: Available platforms run: echo ${{ steps.buildx.outputs.platforms }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/.github/workflows/manual-build.yml new/fortio-1.57.4/.github/workflows/manual-build.yml --- old/fortio-1.57.3/.github/workflows/manual-build.yml 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/.github/workflows/manual-build.yml 2023-07-25 06:29:26.000000000 +0200 @@ -30,7 +30,7 @@ - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@2a1a44ac4aa01993040736bd95bb470da1a38365 # pin@v2 + uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # pin@v2 - name: Build id: build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/Makefile new/fortio-1.57.4/Makefile --- old/fortio-1.57.3/Makefile 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/Makefile 2023-07-25 06:29:26.000000000 +0200 @@ -86,7 +86,7 @@ coverage: dependencies ./.circleci/coverage.sh - curl -s https://codecov.io/bash | bash +# curl -s https://codecov.io/bash | bash # Short cut for pulling/updating to latest of the current branch pull: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/README.md new/fortio-1.57.4/README.md --- old/fortio-1.57.3/README.md 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/README.md 2023-07-25 06:29:26.000000000 +0200 @@ -1,4 +1,4 @@ -<!-- 1.57.3 --> +<!-- 1.57.4 --> # Fortio [](https://github.com/avelino/awesome-go#networking) @@ -60,13 +60,13 @@ The [releases](https://github.com/fortio/fortio/releases) page has binaries for many OS/architecture combinations (see assets): ```shell -curl -L https://github.com/fortio/fortio/releases/download/v1.57.3/fortio-linux_amd64-1.57.3.tgz \ +curl -L https://github.com/fortio/fortio/releases/download/v1.57.4/fortio-linux_amd64-1.57.4.tgz \ | sudo tar -C / -xvzpf - # or the debian package -wget https://github.com/fortio/fortio/releases/download/v1.57.3/fortio_1.57.3_amd64.deb -dpkg -i fortio_1.57.3_amd64.deb +wget https://github.com/fortio/fortio/releases/download/v1.57.4/fortio_1.57.4_amd64.deb +dpkg -i fortio_1.57.4_amd64.deb # or the rpm -rpm -i https://github.com/fortio/fortio/releases/download/v1.57.3/fortio-1.57.3-1.x86_64.rpm +rpm -i https://github.com/fortio/fortio/releases/download/v1.57.4/fortio-1.57.4-1.x86_64.rpm # and more, see assets in release page ``` @@ -76,7 +76,7 @@ brew install fortio ``` -On Windows, download https://github.com/fortio/fortio/releases/download/v1.57.3/fortio_win_1.57.3.zip and extract `fortio.exe` to any location, then using the Windows Command Prompt: +On Windows, download https://github.com/fortio/fortio/releases/download/v1.57.4/fortio_win_1.57.4.zip and extract `fortio.exe` to any location, then using the Windows Command Prompt: ``` fortio.exe server ``` @@ -128,7 +128,7 @@ <!-- use release/updateFlags.sh to update this section --> <pre> <!-- USAGE_START --> -ΦοÏÏίο 1.57.3 usage: +ΦοÏÏίο 1.57.4 usage: fortio command [flags] target where command is one of: load (load testing), server (starts ui, rest api, http-echo, redirect, proxies, tcp-echo, udp-echo and grpc ping servers), @@ -256,6 +256,9 @@ disable (default true) -logger-force-color Force color output even if stderr isn't a terminal + -logger-goroutine + GoroutineID emitted in JSON/color logs, use -logger-goroutine=false to disable +(default true) -logger-json Log in JSON format, use -logger-json=false to disable (default true) -logger-no-color diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/fhttp/httprunner.go new/fortio-1.57.4/fhttp/httprunner.go --- old/fortio-1.57.3/fhttp/httprunner.go 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/fhttp/httprunner.go 2023-07-25 06:29:26.000000000 +0200 @@ -23,7 +23,9 @@ "sort" "strconv" "sync" + "time" + "fortio.org/fortio/jrpc" "fortio.org/fortio/periodic" "fortio.org/fortio/stats" "fortio.org/log" @@ -83,6 +85,25 @@ AbortOn int } +func NewErrorResult(o *HTTPRunnerOptions, message string, err error) *HTTPRunnerResults { + log.LogVf("New error result %s: %v", message, err) + empty := stats.NewHistogram(0, periodic.DefaultRunnerOptions.Resolution) + empty.Record(0.) + empty.Record(0.001) // 2 points to generate a big red block when visualized in browse UI. + return &HTTPRunnerResults{ + HTTPOptions: o.HTTPOptions, + RunnerResults: periodic.RunnerResults{ + StartTime: time.Now(), + RunType: o.RunType, + RunID: o.RunID, + ID: o.RunnerOptions.ID, + ServerReply: *jrpc.NewErrorReply(message, err), + DurationHistogram: empty.Export(), + ErrorsDurationHistogram: empty.Export(), + }, + } +} + // RunHTTPTest runs an http test and returns the aggregated stats. // //nolint:funlen, gocognit, gocyclo, maintidx @@ -112,6 +133,7 @@ o.HTTPOptions.UniqueID = o.RunnerOptions.RunID o.HTTPOptions.Init(o.URL) out := r.Options().Out // Important as the default value is set from nil to stdout inside NewPeriodicRunner + aborter := r.Options().Stop total := HTTPRunnerResults{ HTTPOptions: o.HTTPOptions, RetCodes: make(map[int]int64), @@ -119,7 +141,7 @@ sizes: stats.NewHistogram(0, 100), headerSizes: stats.NewHistogram(0, 5), AbortOn: o.AbortOn, - aborter: r.Options().Stop, + aborter: aborter, } httpstate := make([]HTTPRunnerResults, numThreads) // First build all the clients sequentially. This ensures we do not have data races when @@ -134,12 +156,15 @@ httpstate[i].client, err = NewClient(&o.HTTPOptions) // nil check on interface doesn't work if err != nil { - return nil, err + aborter.RecordStart() // virtual/fake start so when we use the start chan later to wait it doesn't hang + return NewErrorResult(o, "init error", err), err } if o.SequentialWarmup && o.Exactly <= 0 { code, dataLen, headerSize := httpstate[i].client.StreamFetch(ctx) if !o.AllowInitialErrors && !codeIsOK(code) { - return nil, fmt.Errorf("error %d for %s (%d body bytes)", code, o.URL, dataLen) + codeErr := fmt.Errorf("error %d for %s (%d body bytes)", code, o.URL, dataLen) + aborter.RecordStart() + return NewErrorResult(o, "initial http error", codeErr), codeErr } if i == 0 && log.LogVerbose() { log.LogVf("first hit of url %s: status %03d, headers %d, total %d", o.URL, code, headerSize, dataLen) @@ -168,7 +193,7 @@ }) } if err := warmup.Wait(); err != nil { - return nil, err + return NewErrorResult(o, "warmup error", err), err } } // TODO avoid copy pasta with grpcrunner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/go.mod new/fortio-1.57.4/go.mod --- old/fortio-1.57.3/go.mod 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/go.mod 2023-07-25 06:29:26.000000000 +0200 @@ -6,8 +6,8 @@ fortio.org/assert v1.2.0 fortio.org/cli v1.2.0 fortio.org/dflag v1.5.2 - fortio.org/log v1.7.0 - fortio.org/scli v1.8.0 + fortio.org/log v1.8.1 + fortio.org/scli v1.9.0 fortio.org/testscript v0.3.1 fortio.org/version v1.0.2 github.com/golang/protobuf v1.5.3 @@ -29,7 +29,7 @@ require ( fortio.org/sets v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect golang.org/x/tools v0.8.0 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/go.sum new/fortio-1.57.4/go.sum --- old/fortio-1.57.3/go.sum 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/go.sum 2023-07-25 06:29:26.000000000 +0200 @@ -4,10 +4,10 @@ fortio.org/cli v1.2.0/go.mod h1:zJwHjRnvVqneF7ES+REfElc33xMjhbRDJslSrqIK9Kk= fortio.org/dflag v1.5.2 h1:F9XVRj4Qr2IbJP7BMj7XZc9wB0Q/RZ61Ool+4YPVad8= fortio.org/dflag v1.5.2/go.mod h1:ppb/A8u+KKg+qUUYZNYuvRnXuVb8IsdHb/XGzsmjkN8= -fortio.org/log v1.7.0 h1:4MbU81zqe/3RYuHpXADNgJwd2KEMAwmMUtuF5qtZTug= -fortio.org/log v1.7.0/go.mod h1:u/8/2lyczXq52aT5Nw6reD+3cR6m/EbS2jBiIYhgiTU= -fortio.org/scli v1.8.0 h1:JKxPdHeb1vAXFmaliPkfz0Eq2VI35LVUKileOfxdgks= -fortio.org/scli v1.8.0/go.mod h1:QdIJu32otZ3yGztI3NZoUriMZchXq06u2Ttp+/Tm6u8= +fortio.org/log v1.8.1 h1:Ybtjp+qDXb124ESnZ0XVuXJ6khDrWcom/C55YPupoKg= +fortio.org/log v1.8.1/go.mod h1:u/8/2lyczXq52aT5Nw6reD+3cR6m/EbS2jBiIYhgiTU= +fortio.org/scli v1.9.0 h1:laTn/mACr1+TRKH3ZKaQWw58kTzQ1ntpg2sH02KUJAA= +fortio.org/scli v1.9.0/go.mod h1:Ox3nhp3eJpF2rQsZ8fBKXqdJGQPTQHWEZpLikDnkzjY= fortio.org/sets v1.0.3 h1:HzewdGjH69YmyW06yzplL35lGr+X4OcqQt0qS6jbaO4= fortio.org/sets v1.0.3/go.mod h1:QZVj0r6KP/ZD9ebySW9SgxVNy/NjghUfyHW9NN+WU+4= fortio.org/testscript v0.3.1 h1:MmRO64AsmzaU1KlYMzAbotJIMKRGxD1XXssJnBRiMGQ= @@ -23,8 +23,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/periodic/periodic.go new/fortio-1.57.4/periodic/periodic.go --- old/fortio-1.57.3/periodic/periodic.go 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/periodic/periodic.go 2023-07-25 06:29:26.000000000 +0200 @@ -34,6 +34,7 @@ "sync" "time" + "fortio.org/fortio/jrpc" "fortio.org/fortio/stats" "fortio.org/fortio/version" "fortio.org/log" @@ -90,6 +91,7 @@ stopRequested bool } +// Note this can cause data race if called without holding the lock. TODO: maybe use reentrant lock. but this is for debug only. func (a *Aborter) String() string { return fmt.Sprintf("{Aborter %p stopChan %v startChan %v hasStarted %v stopRequested %v}", a, a.StopChan, a.StartChan, a.hasStarted, a.stopRequested) @@ -110,34 +112,53 @@ a.stopRequested = true started := a.hasStarted if started || !wait { - log.LogVf("ABORT Closing %v", a) + log.LogVf("ABORT Closing already started or not waiting %v", a) close(a.StopChan) a.StopChan = nil a.Unlock() if started { log.LogVf("ABORT reading start channel") - // shouldn't block/hang, just purging/resetting - <-a.StartChan + // shouldn't block/hang, just purging/resetting - but another aborter (line 137 might have consumed it already) + select { + case b := <-a.StartChan: + log.LogVf("ABORT done reading start channel, got %v", b) + default: + log.LogVf("ABORT start channel empty (not quite expected)") + } a.Lock() a.hasStarted = false a.Unlock() } return } + // Wait & not started case: a.Unlock() log.LogVf("ABORT Waiting for start") - <-a.StartChan - log.LogVf("ABORT Done waiting for start") + b := <-a.StartChan + log.LogVf("ABORT Done waiting for start, got %v", b) a.Lock() - a.hasStarted = false if a.StopChan != nil { - log.LogVf("ABORT Closing %+v", a) + log.LogVf("ABORT Closing wasn't started %+v", a) close(a.StopChan) a.StopChan = nil } + a.hasStarted = false a.Unlock() } +// RecordStart records the start of the run. (used by httprunner in error cases to fake start so rapi stop and wait can work). +func (a *Aborter) RecordStart() (chan struct{}, bool) { + a.Lock() + a.hasStarted = true + startedChan := a.StartChan + runnerChan := a.StopChan // need a copy to not race with assignment to nil + shouldAbort := a.stopRequested + log.LogVf("RUNNER starting... can now be Abort()ed, telling %v - %v", a, startedChan) + a.Unlock() + startedChan <- true + return runnerChan, shouldAbort +} + // Reset returns the aborter to original state, for (unit test) reuse. // Note that it doesn't recreate the closed stop chan. func (a *Aborter) Reset() { @@ -239,6 +260,8 @@ AccessLoggerInfo string // Same as RunnerOptions ID: Unique 96 character ID used as reference to saved json file. Created during Normalize(). ID string + // If the run doesn't even start because of for instance an invalid host name, this will be set (all omitted on success) + jrpc.ServerReply } // HasRunnerResult is the interface implictly implemented by HTTPRunnerResults @@ -363,7 +386,7 @@ // to nil under lock so it can be called multiple times and not create panic for // already closed channel. func (r *RunnerOptions) Abort() { - log.LogVf("Abort called for %p %+v", r, r) + log.LogVf("Abort called for %p", r) if r.Stop != nil { r.Stop.Abort(false) } @@ -473,14 +496,7 @@ // Run starts the runner. func (r *periodicRunner) Run() RunnerResults { aborter := r.Stop - aborter.Lock() - runnerChan := aborter.StopChan // need a copy to not race with assignment to nil - startedChan := aborter.StartChan - aborter.hasStarted = true - shouldAbort := aborter.stopRequested - aborter.Unlock() - log.LogVf("RUNNER starting... can now be Abort()ed, telling %v - %v", aborter, startedChan) - startedChan <- true + runnerChan, shouldAbort := aborter.RecordStart() useQPS := (r.QPS > 0) // r.Exactly is > 0 if we use Exactly iterations instead of the duration. useExactly := (r.Exactly > 0) @@ -524,6 +540,7 @@ 0, 0, r.NumThreads, version.Short(), functionDuration.Export().CalcPercentiles(r.Percentiles), errorsDuration.Export().CalcPercentiles(r.Percentiles), r.Exactly, r.Jitter, r.Uniform, r.NoCatchUp, r.RunID, loggerInfo, r.ID, + *jrpc.NewErrorReply("Aborted before even starting", nil), } } if r.NumThreads <= 1 { @@ -589,6 +606,7 @@ actualQPS, elapsed, r.NumThreads, version.Short(), functionDuration.Export().CalcPercentiles(r.Percentiles), errorsDuration.Export().CalcPercentiles(r.Percentiles), r.Exactly, r.Jitter, r.Uniform, r.NoCatchUp, r.RunID, loggerInfo, r.ID, + jrpc.ServerReply{Error: false}, } if log.Log(log.Warning) { result.DurationHistogram.Print(r.Out, "Aggregated Function Time") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/rapi/restHandler.go new/fortio-1.57.4/rapi/restHandler.go --- old/fortio-1.57.3/rapi/restHandler.go 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/rapi/restHandler.go 2023-07-25 06:29:26.000000000 +0200 @@ -331,8 +331,11 @@ } } fhttp.OnBehalfOf(httpopts, r) + // Needed to reply the id, will be reused in Normalize() later as already set + // but we also do it early even for sync case so that the id is available to save JSON + // in case of init error. + ro.GenID() if async { - ro.GenID() // Needed to reply the id, will be reused in Normalize() later as already set reply := AsyncReply{RunID: runid, Count: 1, ResultID: ro.ID, ResultURL: ID2URL(r, ro.ID)} reply.Message = "started" //nolint:goconst err := jrpc.ReplyOk(w, &reply) @@ -413,6 +416,18 @@ aborter.StartChan <- false log.LogVf("REST run %d really done - after channel write", ro.RunID) }() + savedAs := "" + jsonData, jerr := json.MarshalIndent(res, "", " ") + if jerr != nil { + log.Fatalf("Unable to json serialize result: %v", jerr) //nolint:gocritic // gocritic doesn't know fortio's log.Fatalf does pani + } + jsonStr := string(jsonData) + log.LogVf("Serialized to %s", jsonStr) + id := res.Result().ID + doSave := (FormValue(r, jd, "save") == "on") + if doSave && id != "" { + savedAs = SaveJSON(id, jsonData) + } if err != nil { log.Errf("Init error for %s mode with url %s and options %+v : %v", runner, url, ro, err) if !htmlMode { @@ -420,31 +435,19 @@ } return res, "", nil, err } - json, err := json.MarshalIndent(res, "", " ") - if err != nil { - log.Fatalf("Unable to json serialize result: %v", err) //nolint:gocritic // gocritic doesn't know fortio's log.Fatalf does pani - } - jsonStr := string(json) - log.LogVf("Serialized to %s", jsonStr) - id := res.Result().ID - doSave := (FormValue(r, jd, "save") == "on") - savedAs := "" - if doSave { - savedAs = SaveJSON(id, json) - } if w == nil { // async or html but nil w (no json output): no result to output - return res, savedAs, json, nil + return res, savedAs, jsonData, nil } if htmlMode { // Already set in api mode but not in html mode w.Header().Set("Content-Type", "application/json") } - _, err = w.Write(json) + _, err = w.Write(jsonData) if err != nil { log.Errf("Unable to write json output for %v: %v", r.RemoteAddr, err) } - return res, savedAs, json, nil + return res, savedAs, jsonData, nil } // RESTStatusHandler will print the state of the runs. @@ -473,6 +476,7 @@ waitStr := strings.ToLower(r.FormValue("wait")) wait := (waitStr != "" && waitStr != "off" && waitStr != "false") i, rid := StopByRunID(runid, wait) + log.Debugf("REST Stop completed, stopped %d runs, rid %s", i, rid) reply := AsyncReply{RunID: runid, Count: i, ResultID: rid, ResultURL: ID2URL(r, rid)} if wait && i == 1 { reply.Message = StateStopped.String() @@ -511,6 +515,7 @@ } // else: Stop one v, found := runs[runid] + log.Debugf("Interrupting runid %d, found %v : %+v", runid, found, v) if !found { uiRunMapMutex.Unlock() log.Infof("Runid %d not found to interrupt", runid) @@ -528,8 +533,8 @@ v.aborter.Abort(wait) if wait { log.LogVf("REST stop, wait requested, reading additional channel signal") - <-v.aborter.StartChan - log.LogVf("REST stop, received all done signal") + b := <-v.aborter.StartChan + log.LogVf("REST stop, received all done signal got %v", b) } log.LogVf("Returning from Abort %d call with wait %v", runid, wait) return 1, rid diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/rapi/restHandler_test.go new/fortio-1.57.4/rapi/restHandler_test.go --- old/fortio-1.57.3/rapi/restHandler_test.go 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/rapi/restHandler_test.go 2023-07-25 06:29:26.000000000 +0200 @@ -16,6 +16,7 @@ package rapi import ( + "context" "errors" "fmt" "net" @@ -39,8 +40,14 @@ // Generics ftw. func FetchResult[T any](t *testing.T, url string, jsonPayload string) *T { r, err := jrpc.Fetch[T](jrpc.NewDestination(url), []byte(jsonPayload)) + // check if it is a timeout error if err != nil { - t.Errorf("Got unexpected error for URL %s: %v - %v", url, err, r) + if errors.Is(err, context.DeadlineExceeded) { + t.Errorf("Got unexpected timeout error for URL %s: %v", url, err) + panic("debugging timeout error for " + url) + } else { + t.Fatalf("Got unexpected error for URL %s: %v - %v", url, err, r) + } } return r } @@ -68,6 +75,9 @@ if err == nil { t.Errorf("Got unexpected no error for URL %s: %v", url, r) } + if r == nil { + t.Fatalf("Unexpected nil error reply") + } if !r.Error { t.Error("Success field should be false for errors") } @@ -329,7 +339,8 @@ } // add one more with bad url - badURL := fmt.Sprintf("%s?jsonPath=.metadata&qps=1&t=on&url=%s&async=on", restURL, "http://doesnotexist.fortio.org/") + badURL := fmt.Sprintf("%s?jsonPath=.metadata&qps=1&t=on&url=%s&async=on", restURL, + "http://doesnotexist.fortio.org/TestHTTPRunnerRESTApi") asyncObj = GetAsyncResult(t, badURL, jsonData) runID = asyncObj.RunID if asyncObj.Message != "started" || runID <= savedID+5 { // 1+1+3 jobs before this one @@ -513,6 +524,58 @@ } } +// Test the bad host case #796. +func TestHTTPRunnerRESTApiBadHost(t *testing.T) { + log.SetLogLevel(log.Debug) // needed to debug if this test starts failing + // log.Config.ForceColor = true + // log.Config.JSON = true + // log.Config.NoTimestamp = true + // log.SetColorMode() + // otherwise log.SetLogLevel(log.Info) + mux, addr := fhttp.DynamicHTTPServer(false) + uiPath := "/f/" + AddHandlers(nil, mux, "", uiPath, ".") + // Error with bad host + restURL := fmt.Sprintf("http://localhost:%d%s%s", addr.Port, uiPath, RestRunURI) + // sync first: + runURL := fmt.Sprintf("%s?qps=%d&url=%s&t=2s", restURL, 100, "http://doesnotexist.fortio.org/TestHTTPRunnerRESTApiBadHost/foo/bar") + errObj := GetErrorResult(t, runURL, "") + // we get either `lookup doesnotexist.fortio.org: no such host` or `lookup doesnotexist.fortio.org on 127.0.0.11:53: no such host` + // so check just for prefix + if !strings.HasPrefix(errObj.Exception, "lookup doesnotexist.fortio.org") { + t.Errorf("Didn't get the expected dns error, got %+v", errObj) + } + // Same with async: + runURL += "&async=on&save=on" + asyncRes := GetAsyncResult(t, runURL, "") + dataURL := asyncRes.ResultURL + if dataURL == "" { + t.Errorf("Expected a result URL, got %+v", asyncRes) + } + runID := asyncRes.RunID + if runID < 1 { + t.Errorf("Expected a run id, got %+v", asyncRes) + } + // And stop it (with wait to avoid race condition/so data is here when this returns and avoid a sleep) + stopURL := fmt.Sprintf("http://localhost:%d%s%s?runid=%d&wait=true", addr.Port, uiPath, RestStopURI, runID) + // Using go test -test.count=1 -timeout 8s -run "^TestHTTPRunnerRESTApiBadHost$" ./rapi is a better way + // to debug the issue and get all the thread dumps, before the timeout triggers but for functional test + // (immediate response to stop) this would be better: + // prevTimeout := jrpc.SetCallTimeout(1 * time.Second) // Stopping a failed to start run should be almost instant + asyncRes = GetAsyncResult(t, stopURL, "") + // Restore previous one (60s). Note we could also change GetAsyncResult to take a jrpc.Destination + // with timeout but that's more change for just a test. + // jrpc.SetCallTimeout(prevTimeout) + if asyncRes.ResultURL != dataURL { + t.Errorf("Expected same result URL, got %+v", asyncRes) + } + // Fetch the json result: + res := GetResult(t, dataURL, "") + if !strings.HasPrefix(res.Exception, "lookup doesnotexist.fortio.org") { + t.Errorf("Didn't get the expected dns error in result file url %s, got %+v", asyncRes.ResultURL, res) + } +} + // If jsonPayload isn't empty we POST otherwise get the url. func GetGRPCResult(t *testing.T, url string, jsonPayload string) *fgrpc.GRPCRunnerResults { r, err := jrpc.Fetch[fgrpc.GRPCRunnerResults](jrpc.NewDestination(url), []byte(jsonPayload)) @@ -523,14 +586,17 @@ } func TestOtherRunnersRESTApi(t *testing.T) { + log.SetLogLevel(log.Info) iPort := fgrpc.PingServerTCP("0", "bar", 0, &fhttp.TLSOptions{}) iDest := fmt.Sprintf("localhost:%d", iPort) mux, addr := fhttp.DynamicHTTPServer(false) AddHandlers(nil, mux, "", "/fortio/", ".") restURL := fmt.Sprintf("http://localhost:%d/fortio/rest/run", addr.Port) - - runURL := fmt.Sprintf("%s?qps=%d&url=%s&t=2s&runner=grpc", restURL, 10, iDest) + qps := 3.0 + dur := "2s" + c := 1 + runURL := fmt.Sprintf("%s?qps=%f&url=%s&t=%s&c=%d&runner=grpc", restURL, qps, iDest, dur, c) res := FetchResult[fgrpc.GRPCRunnerResults](t, runURL, "") totalReq := res.DurationHistogram.Count @@ -542,19 +608,19 @@ tAddr := fnet.TCPEchoServer("test-echo-runner-tcp", ":0") tDest := fmt.Sprintf("tcp://localhost:%d/", tAddr.(*net.TCPAddr).Port) - runURL = fmt.Sprintf("%s?qps=%d&url=%s&t=2s&c=2", restURL, 10, tDest) + runURL = fmt.Sprintf("%s?qps=%f&url=%s&t=%s&c=%d", restURL, qps, tDest, dur, c) tRes := FetchResult[tcprunner.RunnerResults](t, runURL, "") - if tRes.ActualQPS < 8 || tRes.ActualQPS > 10.1 { + if tRes.ActualQPS < qps*.75 || tRes.ActualQPS > qps*1.25 { t.Errorf("Unexpected tcp qps %f", tRes.ActualQPS) } uAddr := fnet.UDPEchoServer("test-echo-runner-udp", ":0", false) uDest := fmt.Sprintf("udp://localhost:%d/", uAddr.(*net.UDPAddr).Port) - runURL = fmt.Sprintf("%s?qps=%d&url=%s&t=2s&c=1", restURL, 5, uDest) + runURL = fmt.Sprintf("%s?qps=%f&url=%s&t=%s&c=%d", restURL, qps, uDest, dur, c) uRes := FetchResult[udprunner.RunnerResults](t, runURL, "") - if uRes.ActualQPS < 4 || uRes.ActualQPS > 5.1 { + if uRes.ActualQPS < qps*.75 || uRes.ActualQPS > qps*1.25 { t.Errorf("Unexpected udp qps %f", tRes.ActualQPS) } } @@ -572,6 +638,10 @@ t.Errorf("Expected json %s got %s", expected, str) } list := GetAllRuns() + t.Logf("expecting only %d", id) + for _, r := range list { + t.Logf("run %d: %+v", r.RunID, r) + } if len(list) != 1 { t.Errorf("Expected 1 run got %d", len(list)) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fortio-1.57.3/ui/static/js/fortio_chart.js new/fortio-1.57.4/ui/static/js/fortio_chart.js --- old/fortio-1.57.3/ui/static/js/fortio_chart.js 2023-07-13 00:25:10.000000000 +0200 +++ new/fortio-1.57.4/ui/static/js/fortio_chart.js 2023-07-25 06:29:26.000000000 +0200 @@ -110,7 +110,11 @@ percStr += ', max ' + myRound(1000.0 * res.DurationHistogram.Max, 3) + ' ms' const total = res.DurationHistogram.Count let errStr = 'no error' - if (res.ErrorsDurationHistogram != null) { + if (res.error) { + errStr = 'ERROR: ' + res.message + ' - ' + res.exception + title.push(errStr) + return title + } else if (res.ErrorsDurationHistogram != null) { // Newer simpler calculation when we have the ErrorsDurationHistogram: const statusNotOk = res.ErrorsDurationHistogram.Count if (statusNotOk !== 0) { ++++++ fortio.obsinfo ++++++ --- /var/tmp/diff_new_pack.yJU0ar/_old 2023-07-25 11:53:27.934282383 +0200 +++ /var/tmp/diff_new_pack.yJU0ar/_new 2023-07-25 11:53:27.938282406 +0200 @@ -1,5 +1,5 @@ name: fortio -version: 1.57.3 -mtime: 1689200710 -commit: c1c5e58906de3bb19779c1545d3a70bd649b3e63 +version: 1.57.4 +mtime: 1690259366 +commit: 67b7744c810d8520599076765f037009164c9f8f ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/README.md new/vendor/fortio.org/log/README.md --- old/vendor/fortio.org/log/README.md 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/log/README.md 2023-07-25 07:27:59.000000000 +0200 @@ -44,7 +44,9 @@ ``` Which can be converted to JSONEntry but is also a fixed, optimized format (ie ts is always first etc) -The timestamp `ts` is in microseconds since epoch (golang UnixMicro()) +The timestamp `ts` is in seconds.microseconds since epoch (golang UnixMicro() split into seconds part before decimal and microseconds after) + +Since 1.8 the Go Routine ID is present in json (`r` field) or colorized log output (for multi threaded server types). Optional additional `KeyValue` pairs can be added to the base structure using the new `log.S` or passed to `log.LogRequest` using `log.Attr` and `log.Str`. @@ -52,16 +54,16 @@ When output is redirected, JSON output: ```json -{"ts":1688674146.025678,"level":"dbug","file":"levels.go","line":15,"msg":"This is a debug message ending with backslash"} -{"ts":1688674146.025708,"level":"trace","file":"levels.go","line":16,"msg":"This is a verbose message"} -{"ts":1688674146.025714,"level":"info","msg":"This an always printed, file:line omitted message"} -{"ts":1688674146.025717,"level":"info","file":"levels.go","line":18,"msg":"This is an info message with no attributes but with \"quotes\"..."} -{"ts":1688674146.025735,"level":"info","file":"levels.go","line":19,"msg":"This is multi line\n\tstructured info message with 3 attributes","attr1":"value1","attr2":"42","attr3":"\"quoted\nvalue\""} -{"ts":1688674146.025741,"level":"warn","file":"levels.go","line":21,"msg":"This is a warning message"} -{"ts":1688674146.025743,"level":"err","file":"levels.go","line":22,"msg":"This is an error message"} -{"ts":1688674146.025745,"level":"crit","file":"levels.go","line":23,"msg":"This is a critical message"} -{"ts":1688674146.025748,"level":"fatal","file":"levels.go","line":24,"msg":"This is a fatal message"} -This is stdout normal output +{"ts":1689986143.463329,"level":"dbug","r":1,"file":"levels.go","line":16,"msg":"This is a debug message ending with backslash \\"} +{"ts":1689986143.463374,"level":"trace","r":1,"file":"levels.go","line":17,"msg":"This is a verbose message"} +{"ts":1689986143.463378,"level":"info","r":1,"msg":"This an always printed, file:line omitted message"} +{"ts":1689986143.463382,"level":"info","r":1,"file":"levels.go","line":19,"msg":"This is an info message with no attributes but with \"quotes\"..."} +{"ts":1689986143.463389,"level":"info","r":1,"file":"levels.go","line":20,"msg":"This is multi line\n\tstructured info message with 3 attributes","attr1":"value1","attr2":"42","attr3":"\"quoted\nvalue\""} +{"ts":1689986143.463396,"level":"warn","r":1,"file":"levels.go","line":22,"msg":"This is a warning message"} +{"ts":1689986143.4634,"level":"err","r":1,"file":"levels.go","line":23,"msg":"This is an error message"} +{"ts":1689986143.463403,"level":"crit","r":1,"file":"levels.go","line":24,"msg":"This is a critical message"} +{"ts":1689986143.463406,"level":"fatal","r":1,"file":"levels.go","line":25,"msg":"This is a fatal message"} +This is a non json output, will get prefixed with a exclamation point with logc ``` When on console: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/console_logging.go new/vendor/fortio.org/log/console_logging.go --- old/vendor/fortio.org/log/console_logging.go 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/log/console_logging.go 2023-07-25 07:27:59.000000000 +0200 @@ -15,8 +15,11 @@ package log import ( + "fmt" "os" "time" + + "fortio.org/log/goroutine" ) // to avoid making a new package/namespace for colors, we use a struct. @@ -95,3 +98,11 @@ } return time.Now().Format(Colors.DarkGray + "15:04:05.000 ") } + +// Color version of jsonGID(). +func colorGID() string { + if !Config.GoroutineID { + return "" + } + return Colors.Gray + fmt.Sprintf("[%d] ", goroutine.ID()) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/goroutine/LICENSE new/vendor/fortio.org/log/goroutine/LICENSE --- old/vendor/fortio.org/log/goroutine/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/fortio.org/log/goroutine/LICENSE 2023-07-25 07:27:59.000000000 +0200 @@ -0,0 +1,22 @@ +Copyright ©2020 Dan Kortschak. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/goroutine/README.md new/vendor/fortio.org/log/goroutine/README.md --- old/vendor/fortio.org/log/goroutine/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/fortio.org/log/goroutine/README.md 2023-07-25 07:27:59.000000000 +0200 @@ -0,0 +1,18 @@ +If you just want a thread ID / goroutine ID use the original this was imported from: + +https://github.com/kortschak/goroutine/ + +Credits goes to Dan Kortschak and Laevus Dexter + +# goroutine identification + +Package goroutine provides a single function that will return the runtime's +ID number for the calling goroutine. + +The implementation is derived from Laevus Dexter's comment in Gophers' Slack +#darkarts, https://gophers.slack.com/archives/C1C1YSQBT/p1593885226448300 +post which linked to this playground snippet https://play.golang.org/p/CSOp9wyzydP. + +The code here is an exercise in minimalism, doing as little as possible by +deferring nearly all of the logic to runtime functions co-opted via +`//go:linkname` comments. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/goroutine/gid.go new/vendor/fortio.org/log/goroutine/gid.go --- old/vendor/fortio.org/log/goroutine/gid.go 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/fortio.org/log/goroutine/gid.go 2023-07-25 07:27:59.000000000 +0200 @@ -0,0 +1,52 @@ +// Copyright ©2020 Dan Kortschak. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package goroutine provides a single function that will return the runtime's +// ID number for the calling goroutine. +// +// The implementation is derived from Laevus Dexter's comment in Gophers' Slack #darkarts, +// https://gophers.slack.com/archives/C1C1YSQBT/p1593885226448300 post which linked to +// this playground snippet https://play.golang.org/p/CSOp9wyzydP. +package goroutine + +import ( + "reflect" + "unsafe" +) + +// ID returns the runtime ID of the calling goroutine. +func ID() int64 { + return *(*int64)(add(getg(), goidoff)) +} + +//go:nosplit +func getg() unsafe.Pointer { + return *(*unsafe.Pointer)(add(getm(), curgoff)) +} + +//go:linkname add runtime.add +//go:nosplit +func add(p unsafe.Pointer, x uintptr) unsafe.Pointer + +//go:linkname getm runtime.getm +//go:nosplit +func getm() unsafe.Pointer + +var ( + curgoff = offset("*runtime.m", "curg") + goidoff = offset("*runtime.g", "goid") +) + +// offset returns the offset into typ for the given field. +func offset(typ, field string) uintptr { + rt := toType(typesByString(typ)[0]) + f, _ := rt.Elem().FieldByName(field) + return f.Offset +} + +//go:linkname typesByString reflect.typesByString +func typesByString(s string) []unsafe.Pointer + +//go:linkname toType reflect.toType +func toType(t unsafe.Pointer) reflect.Type diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/http_logging.go new/vendor/fortio.org/log/http_logging.go --- old/vendor/fortio.org/log/http_logging.go 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/log/http_logging.go 2023-07-25 07:27:59.000000000 +0200 @@ -57,7 +57,8 @@ } attr := []KeyVal{ Str("method", r.Method), Attr("url", r.URL), Str("proto", r.Proto), - Str("remote_addr", r.RemoteAddr), Str("header.x-forwarded-proto", r.Header.Get("X-Forwarded-Proto")), + Str("remote_addr", r.RemoteAddr), Str("host", r.Host), + Str("header.x-forwarded-proto", r.Header.Get("X-Forwarded-Proto")), Str("header.x-forwarded-for", r.Header.Get("X-Forwarded-For")), Str("user-agent", r.Header.Get("User-Agent")), } @@ -65,7 +66,6 @@ attr = append(attr, extraAttributes...) if LogVerbose() { // Host is removed from headers map and put separately - attr = append(attr, Str("header.host", r.Host)) for name, headers := range r.Header { attr = append(attr, Str("header."+name, strings.Join(headers, ","))) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/log/logger.go new/vendor/fortio.org/log/logger.go --- old/vendor/fortio.org/log/logger.go 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/log/logger.go 2023-07-25 07:27:59.000000000 +0200 @@ -32,6 +32,8 @@ "sync" "sync/atomic" "time" + + "fortio.org/log/goroutine" ) // Level is the level of logging (0 Debug -> 6 Fatal). @@ -61,6 +63,8 @@ // Force color mode even if logger output is not console (useful for CI that recognize ansi colors). // SetColorMode() must be called if this or ConsoleColor are changed. ForceColor bool + // If true, log the goroutine ID (gid) in json. + GoroutineID bool } // DefaultConfig() returns the default initial configuration for the logger, best suited @@ -76,6 +80,7 @@ FatalExit: os.Exit, JSON: true, ConsoleColor: true, + GoroutineID: true, } } @@ -105,6 +110,8 @@ "\"crit\"", "\"fatal\"", } + // Reverse mapping of level string used in JSON to Level. Used by https://github.com/fortio/logc + // to interpret and colorize pre existing JSON logs. JSONStringLevelToLevel map[string]Level ) @@ -118,6 +125,7 @@ Config.FatalPanics = false Config.ConsoleColor = true Config.JSON = false + Config.GoroutineID = false SetColorMode() } @@ -126,6 +134,7 @@ // structure. type JSONEntry struct { TS float64 // In seconds since epoch (unix micros resolution), see TimeToTS(). + R int64 // Goroutine ID (if enabled) Level string File string Line int @@ -328,6 +337,14 @@ return fmt.Sprintf("\"ts\":%.6f,", TimeToTS(time.Now())) } +// Returns the json GoRoutineID if enabled. +func jsonGID() string { + if !Config.GoroutineID { + return "" + } + return fmt.Sprintf("\"r\":%d,", goroutine.ID()) +} + func logPrintf(lvl Level, format string, rest ...interface{}) { if !Log(lvl) { return @@ -340,22 +357,23 @@ _, file, line, _ := runtime.Caller(3) file = file[strings.LastIndex(file, "/")+1:] if Color { - jsonWrite(fmt.Sprintf("%s%s%s %s:%d%s%s%s\n", - colorTimestamp(), LevelToColor[lvl], LevelToStrA[lvl][0:1], + jsonWrite(fmt.Sprintf("%s%s%s%s %s:%d%s%s%s\n", + colorTimestamp(), colorGID(), LevelToColor[lvl], LevelToStrA[lvl][0:1], file, line, Config.LogPrefix, fmt.Sprintf(format, rest...), Colors.Reset)) } else if Config.JSON { - jsonWrite(fmt.Sprintf("{%s\"level\":%s,\"file\":%q,\"line\":%d,\"msg\":%q}\n", - jsonTimestamp(), LevelToJSON[lvl], file, line, fmt.Sprintf(format, rest...))) + jsonWrite(fmt.Sprintf("{%s\"level\":%s,%s\"file\":%q,\"line\":%d,\"msg\":%q}\n", + jsonTimestamp(), LevelToJSON[lvl], jsonGID(), file, line, fmt.Sprintf(format, rest...))) } else { log.Print(LevelToStrA[lvl][0:1], " ", file, ":", line, Config.LogPrefix, fmt.Sprintf(format, rest...)) } } else { if Color { - jsonWrite(fmt.Sprintf("%s%s%s %s%s%s\n", - colorTimestamp(), LevelToColor[lvl], LevelToStrA[lvl][0:1], Config.LogPrefix, fmt.Sprintf(format, rest...), Colors.Reset)) + jsonWrite(fmt.Sprintf("%s%s%s%s %s%s%s\n", + colorTimestamp(), colorGID(), LevelToColor[lvl], LevelToStrA[lvl][0:1], Config.LogPrefix, + fmt.Sprintf(format, rest...), Colors.Reset)) } else if Config.JSON { - jsonWrite(fmt.Sprintf("{%s\"level\":%s,\"msg\":%q}\n", - jsonTimestamp(), LevelToJSON[lvl], fmt.Sprintf(format, rest...))) + jsonWrite(fmt.Sprintf("{%s\"level\":%s,%s\"msg\":%q}\n", + jsonTimestamp(), LevelToJSON[lvl], jsonGID(), fmt.Sprintf(format, rest...))) } else { log.Print(LevelToStrA[lvl][0:1], " ", Config.LogPrefix, fmt.Sprintf(format, rest...)) } @@ -505,6 +523,9 @@ if !Log(lvl) { return } + // extra := "" + // if Config.GoroutineID { + // } buf := strings.Builder{} var format string if Color { @@ -522,18 +543,19 @@ _, file, line, _ := runtime.Caller(1) file = file[strings.LastIndex(file, "/")+1:] if Color { - jsonWrite(fmt.Sprintf("%s%s%s %s:%d%s%s%s%s\n", - colorTimestamp(), LevelToColor[lvl], LevelToStrA[lvl][0:1], file, line, Config.LogPrefix, msg, buf.String(), Colors.Reset)) + jsonWrite(fmt.Sprintf("%s%s%s%s %s:%d%s%s%s%s\n", + colorTimestamp(), colorGID(), LevelToColor[lvl], LevelToStrA[lvl][0:1], + file, line, Config.LogPrefix, msg, buf.String(), Colors.Reset)) } else if Config.JSON { - jsonWrite(fmt.Sprintf("{%s\"level\":%s,\"file\":%q,\"line\":%d,\"msg\":%q%s}\n", - jsonTimestamp(), LevelToJSON[lvl], file, line, msg, buf.String())) + jsonWrite(fmt.Sprintf("{%s\"level\":%s,%s\"file\":%q,\"line\":%d,\"msg\":%q%s}\n", + jsonTimestamp(), LevelToJSON[lvl], jsonGID(), file, line, msg, buf.String())) } else { log.Print(LevelToStrA[lvl][0:1], " ", file, ":", line, Config.LogPrefix, msg, buf.String()) } } else { if Color { - jsonWrite(fmt.Sprintf("%s%s%s %s%s%s%s\n", - colorTimestamp(), LevelToColor[lvl], LevelToStrA[lvl][0:1], Config.LogPrefix, msg, buf.String(), Colors.Reset)) + jsonWrite(fmt.Sprintf("%s%s%s%s %s%s%s%s\n", + colorTimestamp(), colorGID(), LevelToColor[lvl], LevelToStrA[lvl][0:1], Config.LogPrefix, msg, buf.String(), Colors.Reset)) } else if Config.JSON { jsonWrite(fmt.Sprintf("{%s\"level\":%s,\"msg\":%q%s}\n", jsonTimestamp(), LevelToJSON[lvl], msg, buf.String())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/scli/README.md new/vendor/fortio.org/scli/README.md --- old/vendor/fortio.org/scli/README.md 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/scli/README.md 2023-07-25 07:27:59.000000000 +0200 @@ -49,26 +49,26 @@ $ go run . -config-dir ./config -config-port 8888 a b 2>&1 | cat # forces no color because stderr isn't a terminal ``` ```json -{"ts":1686609103.447926,"level":"info","file":"updater.go","line":47,"msg":"Configmap flag value watching on ./config"} -{"ts":1686609103.449381,"level":"info","file":"updater.go","line":156,"msg":"updating loglevel to \"verbose\\n\""} -{"ts":1686609103.449406,"level":"info","file":"logger.go","line":224,"msg":"Log level is now 1 Verbose (was 2 Info)"} -{"ts":1686609103.450125,"level":"info","file":"updater.go","line":97,"msg":"Now watching . and config"} -{"ts":1686609103.450240,"level":"info","file":"updater.go","line":162,"msg":"Background thread watching config now running"} -{"ts":1686609103.450523,"level":"info","file":"scli.go","line":87,"msg":"Fortio scli dev dflag config server listening on [::]:8888"} -{"ts":1686609103.450534,"level":"info","file":"scli.go","line":96,"msg":"Starting sampleServer dev go1.20.5 arm64 darwin"} -{"ts":1686609104.452193,"level":"info","file":"main.go","line":16,"msg":"FD count 1s after start : 14"} +{"ts":1689985712.410200,"level":"info","r":1,"file":"updater.go","line":47,"msg":"Configmap flag value watching on ./config"} +{"ts":1689985712.411816,"level":"info","r":1,"file":"updater.go","line":156,"msg":"updating loglevel to \"verbose\\n\""} +{"ts":1689985712.411841,"level":"info","r":1,"file":"logger.go","line":245,"msg":"Log level is now 1 Verbose (was 2 Info)"} +{"ts":1689985712.412396,"level":"info","r":1,"file":"updater.go","line":97,"msg":"Now watching . and config"} +{"ts":1689985712.412539,"level":"info","r":19,"file":"updater.go","line":162,"msg":"Background thread watching config now running"} +{"ts":1689985712.412901,"level":"info","r":1,"file":"scli.go","line":104,"msg":"Fortio scli dev dflag config server listening on [::]:8888"} +{"ts":1689985712.412911,"level":"info","r":1,"file":"scli.go","line":113,"msg":"Starting sampleServer dev go1.20.6 arm64 darwin"} +{"ts":1689985713.415586,"level":"info","r":1,"file":"main.go","line":16,"msg":"FD count 1s after start : 14"} # list flag (curl localhost:8888) -{"ts":1686609330.309960,"level":"info","file":"http_logging.go","line":73,"msg":"ListFlags","method":"GET","url":"/","proto":"HTTP/1.1","remote_addr":"127.0.0.1:60554","header.x-forwarded-proto":"","header.x-forwarded-for":"","user-agent":"curl/8.0.1","header.host":"localhost:8888","header.User-Agent":"curl/8.0.1","header.Accept":"*/*"} -{"ts":1686609124.453697,"level":"info","file":"main.go","line":18,"msg":"FD count 20s later : 14"} -{"ts":1686609124.454075,"level":"info","file":"main.go","line":21,"msg":"FD count stability check: 14"} -{"ts":1686609124.454411,"level":"info","file":"main.go","line":21,"msg":"FD count stability check: 14"} -{"ts":1686609124.454745,"level":"info","file":"main.go","line":21,"msg":"FD count stability check: 14"} -{"ts":1686609124.455071,"level":"info","file":"main.go","line":21,"msg":"FD count stability check: 14"} -{"ts":1686609124.455462,"level":"info","file":"main.go","line":21,"msg":"FD count stability check: 14"} -{"ts":1686609124.455482,"level":"info","file":"main.go","line":27,"msg":"Running until interrupted (ctrl-c)..."} -# After ^C -{"ts":1686609129.019649,"level":"warn","file":"scli.go","line":107,"msg":"Interrupt received."} -{"ts":1686609129.019703,"level":"info","file":"main.go","line":29,"msg":"Normal exit"} +{"ts":1689985717.703528,"level":"info","r":21,"file":"http_logging.go","line":73,"msg":"ListFlags","method":"GET","url":"/","proto":"HTTP/1.1","remote_addr":"127.0.0.1:57975","host":"localhost:8888","header.x-forwarded-proto":"","header.x-forwarded-for":"","user-agent":"curl/8.0.1","header.User-Agent":"curl/8.0.1","header.Accept":"*/*"} +{"ts":1689985733.418850,"level":"info","r":1,"file":"main.go","line":18,"msg":"FD count 20s later : 14"} +{"ts":1689985733.419088,"level":"info","r":1,"file":"main.go","line":21,"msg":"FD count stability check: 14"} +{"ts":1689985733.419280,"level":"info","r":1,"file":"main.go","line":21,"msg":"FD count stability check: 14"} +{"ts":1689985733.419426,"level":"info","r":1,"file":"main.go","line":21,"msg":"FD count stability check: 14"} +{"ts":1689985733.419565,"level":"info","r":1,"file":"main.go","line":21,"msg":"FD count stability check: 14"} +{"ts":1689985733.419702,"level":"info","r":1,"file":"main.go","line":21,"msg":"FD count stability check: 14"} +{"ts":1689985733.419710,"level":"info","r":1,"file":"main.go","line":27,"msg":"Running until interrupted (ctrl-c)..."} +# Sending INT signal +{"ts":1689985903.623639,"level":"warn","r":1,"file":"scli.go","line":124,"msg":"Interrupt received."} +{"ts":1689985903.623682,"level":"info","r":1,"file":"main.go","line":29,"msg":"Normal exit"} ``` And console output default colorized mode comes from [log](https://github.com/fortio/log#log)'s 1.6.0 or newer: @@ -124,6 +124,8 @@ Filename and line numbers emitted in JSON logs, use -logger-file-line=false to disable (default true) -logger-force-color Force color output even if stderr isn't a terminal + -logger-goroutine + GoroutineID emitted in JSON/color logs, use -logger-goroutine=false to disable (default true) -logger-json Log in JSON format, use -logger-json=false to disable (default true) -logger-no-color @@ -149,3 +151,4 @@ - `-logger-timestamp=false` so the timestamp is removed from the output as that would be different always - `-logger-file-line=false` so code line numbers don't show as diffs either (if comparing different versions/releases) +- `-logger-goroutine=false` so Goroutine IDs don't show (which may or may not be desired) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/fortio.org/scli/scli.go new/vendor/fortio.org/scli/scli.go --- old/vendor/fortio.org/scli/scli.go 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/fortio.org/scli/scli.go 2023-07-25 07:27:59.000000000 +0200 @@ -67,6 +67,12 @@ log.Debugf("Changing log format to JSON file and line %v", newValue) log.Config.LogFileAndLine = newValue }) + dflag.DynBool(flag.CommandLine, "logger-goroutine", true, + "GoroutineID emitted in JSON/color logs, use -logger-goroutine=false to disable").WithSyncNotifier( + func(_ bool, newValue bool) { + log.Debugf("Changing log format to GoroutineID %v", newValue) + log.Config.GoroutineID = newValue + }) cli.ServerMode = true cli.Main() // will call ExitFunction() if there are usage errors if *configDir != "" { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/golang.org/x/exp/slices/slices.go new/vendor/golang.org/x/exp/slices/slices.go --- old/vendor/golang.org/x/exp/slices/slices.go 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/golang.org/x/exp/slices/slices.go 2023-07-25 07:27:59.000000000 +0200 @@ -168,6 +168,30 @@ return append(s[:i], s[j:]...) } +// DeleteFunc removes any elements from s for which del returns true, +// returning the modified slice. +// When DeleteFunc removes m elements, it might not modify the elements +// s[len(s)-m:len(s)]. If those elements contain pointers you might consider +// zeroing those elements so that objects they reference can be garbage +// collected. +func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { + // Don't start copying elements until we find one to delete. + for i, v := range s { + if del(v) { + j := i + for i++; i < len(s); i++ { + v = s[i] + if !del(v) { + s[j] = v + j++ + } + } + return s[:j] + } + } + return s +} + // Replace replaces the elements s[i:j] by the given v, and returns the // modified slice. Replace panics if s[i:j] is not a valid slice of s. func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2023-07-13 06:58:53.000000000 +0200 +++ new/vendor/modules.txt 2023-07-25 07:28:00.000000000 +0200 @@ -10,10 +10,11 @@ fortio.org/dflag/configmap fortio.org/dflag/dynloglevel fortio.org/dflag/endpoint -# fortio.org/log v1.7.0 +# fortio.org/log v1.8.1 ## explicit; go 1.18 fortio.org/log -# fortio.org/scli v1.8.0 +fortio.org/log/goroutine +# fortio.org/scli v1.9.0 ## explicit; go 1.18 fortio.org/scli # fortio.org/sets v1.0.3 @@ -44,7 +45,7 @@ # github.com/google/uuid v1.3.0 ## explicit github.com/google/uuid -# golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 +# golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/slices