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
 
 [![Awesome 
Go](https://fortio.org/mentioned-badge.svg)](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

Reply via email to