Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add `nilerror` hook to detect nil structs wrapped in non-nil error interfaces. [PR #25](https://github.com/riverqueue/rivercontrib/pull/25).

## [0.4.0] - 2025-04-18

### Added

- Added `otelriver` option `EnableWorkSpanJobKindSuffix` which appends the job kind a suffix to work spans so they look like `river.work/my_job` instead of `river.work`.
- Added `otelriver` option `EnableWorkSpanJobKindSuffix` which appends the job kind a suffix to work spans so they look like `river.work/my_job` instead of `river.work`. [PR #23](https://github.com/riverqueue/rivercontrib/pull/23).

## [0.3.0] - 2025-04-14

Expand Down
16 changes: 9 additions & 7 deletions datadogriver/go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
module github.com/riverqueue/rivercontrib/datadogriver

go 1.22.0
go 1.23.0

toolchain go1.24.2

require (
github.com/DataDog/dd-trace-go/v2 v2.0.0-rc.4
github.com/riverqueue/river v0.19.0
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.19.0
github.com/riverqueue/river/rivershared v0.19.0
github.com/riverqueue/river/rivertype v0.19.0
github.com/riverqueue/river v0.21.0
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0
github.com/riverqueue/river/rivershared v0.21.0
github.com/riverqueue/river/rivertype v0.21.0
github.com/riverqueue/rivercontrib/otelriver v0.4.0
go.opentelemetry.io/otel v1.35.0
)
Expand Down Expand Up @@ -47,7 +49,7 @@ require (
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.2 // indirect
github.com/jackc/pgx/v5 v5.7.4 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
Expand All @@ -61,7 +63,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/riverqueue/river/riverdriver v0.19.0 // indirect
github.com/riverqueue/river/riverdriver v0.21.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
Expand Down
20 changes: 16 additions & 4 deletions datadogriver/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down Expand Up @@ -141,18 +143,28 @@ github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVO
github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI=
github.com/riverqueue/river v0.19.0 h1:WRh/NXhp+WEEY0HpCYgr4wSRllugYBt30HtyQ3jlz08=
github.com/riverqueue/river v0.19.0/go.mod h1:YJ7LA2uBdqFHQJzKyYc+X6S04KJeiwsS1yU5a1rynlk=
github.com/riverqueue/river v0.21.0 h1:8bTEI664KIOFF18yFb2DX5ReyKb6YJ8MZIJArmbToNI=
github.com/riverqueue/river v0.21.0/go.mod h1:I5Fk3PvBl9i+fSjGLwLbVOWx7oTkrho5X6de67zQonA=
github.com/riverqueue/river/riverdriver v0.19.0 h1:NyHz5DfB13paT2lvaO0CKmwy4SFLbA7n6MFRGRtwii4=
github.com/riverqueue/river/riverdriver v0.19.0/go.mod h1:Soxi08hHkEvopExAp6ADG2437r4coSiB4QpuIL5E28k=
github.com/riverqueue/river/riverdriver v0.21.0 h1:DgluUSBiTcNOMNCJ3U7LHqAqYO5uQl1I5lupiXddYwY=
github.com/riverqueue/river/riverdriver v0.21.0/go.mod h1:Sa4sKc8e0InTH4MNS0foltRpkiiAvY5ZocknmLbI9/Q=
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.19.0 h1:ytdPnueiv7ANxJcntBtYenrYZZLY5P0mXoDV0l4WsLk=
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.19.0/go.mod h1:5Fahb3n+m1V0RAb0JlOIpzimoTlkOgudMfxSSCTcmFk=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.19.0 h1:QWg7VTDDXbtTF6srr7Y1C888PiNzqv379yQuNSnH2hg=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.19.0/go.mod h1:uvF1YS+iSQavCIHtaB/Y6O8A6Dnn38ctVQCpCpmHDZE=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0 h1:k4hzqeH/ntaOmEheKdCvFbB7SU+e1emct/SG0n2nrPM=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0/go.mod h1:dWTf+MUtkgfSmaCUaJmB//VE/uXHOOCJsEgor92nENw=
github.com/riverqueue/river/rivershared v0.19.0 h1:TZvFM6CC+QgwQQUMQ5Ueuhx25ptgqcKqZQGsdLJnFeE=
github.com/riverqueue/river/rivershared v0.19.0/go.mod h1:JAvmohuC5lounVk8e3zXZIs07Da3klzEeJo1qDQIbjw=
github.com/riverqueue/river/rivershared v0.21.0 h1:yRdCFfrVwVdi07m6NLjCdNePjwPt6wh7T8Fi1RbAncA=
github.com/riverqueue/river/rivershared v0.21.0/go.mod h1:eb61dWdwdXWaFa2fJ7BOiUe0hxe463f3g+oRDstw6/8=
github.com/riverqueue/river/rivertype v0.19.0 h1:5rwgdh21pVcU9WjrHIIO9qC2dOMdRrrZ/HZZOE0JRyY=
github.com/riverqueue/river/rivertype v0.19.0/go.mod h1:DETcejveWlq6bAb8tHkbgJqmXWVLiFhTiEm8j7co1bE=
github.com/riverqueue/rivercontrib/otelriver v0.2.0 h1:dYAwoh6+wNeSttiRmFS53nFRDGsXrN9e7GCMdhq5dsA=
github.com/riverqueue/rivercontrib/otelriver v0.2.0/go.mod h1:UvPxRkGbCfIgvVQ4sgYbYZaGS0UYmweaV/QD2L4B+rU=
github.com/riverqueue/river/rivertype v0.21.0 h1:4BhZ+T2pdVP3qqJYz9SJX4tR855B3UulRsjMUBXrywg=
github.com/riverqueue/river/rivertype v0.21.0/go.mod h1:lmdl3vLNDfchDWbYdW2uAocIuwIN+ZaXqAukdSCFqWs=
github.com/riverqueue/rivercontrib/otelriver v0.4.0 h1:UaiDCPc/rn+q6UECl5xfhAb/NhHzYU2vRjGyj2ZWAF4=
github.com/riverqueue/rivercontrib/otelriver v0.4.0/go.mod h1:PRm8MJ3BFKvpXfbaE2r1QgUb2RfhgwZF6YawAa4/c0s=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
Expand Down Expand Up @@ -242,8 +254,8 @@ go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
Expand Down
3 changes: 2 additions & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
go 1.24.1
go 1.24.2

use (
./datadogriver
./otelriver
./nilerror
)
19 changes: 19 additions & 0 deletions nilerror/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# nilerror [![Build Status](https://github.com/riverqueue/rivercontrib/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/riverqueue/rivercontrib/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/riverqueue/rivercontrib.svg)](https://pkg.go.dev/github.com/riverqueue/rivercontrib/nilerror)

Provides a River hook for detecting a common Go error where a nil struct value is wrapped in a non-nil interface value. This commonly causes trouble with the error interface, where an unintentional nil error is returned.

See https://go.dev/doc/faq#nil_error.

See [`example_hook_test.go`](./example_hook_test.go) for usage details.

## Options

The hook supports these options:

``` go
hook := nilerror.NewHook(&HookConfig{
Suppress: true,
})
```

* `Suppress`: Causes the hook to suppress detected nil struct values wrapped in non-nil error interface values and produce warning logging instead.
31 changes: 31 additions & 0 deletions nilerror/example_hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nilerror_test

import (
"log/slog"

"github.com/rivercontrib/nilerror"
"github.com/riverqueue/river"
"github.com/riverqueue/river/riverdriver/riverpgxv5"
"github.com/riverqueue/river/rivershared/util/slogutil"
"github.com/riverqueue/river/rivertype"
)

func ExampleHook() {
_, err := river.NewClient(riverpgxv5.New(nil), &river.Config{
Hooks: []rivertype.Hook{
// Install a nilerror check that will return an error when it
// detects a nil struct wrapped in a non-nil error interface.
nilerror.NewHook(nil),

// Alternatively, suppress errors and produce warning logging.
nilerror.NewHook(&nilerror.HookConfig{Suppress: true}),
},
Logger: slog.New(&slogutil.SlogMessageOnlyHandler{Level: slog.LevelWarn}),
TestOnly: true, // suitable only for use in tests; remove for live environments
})
if err != nil {
panic(err)
}

// Output:
}
30 changes: 30 additions & 0 deletions nilerror/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module github.com/rivercontrib/nilerror

go 1.24.2

require (
github.com/riverqueue/river v0.21.0
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0
github.com/riverqueue/river/rivershared v0.21.0
github.com/riverqueue/river/rivertype v0.21.0
github.com/stretchr/testify v1.10.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.4 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/riverqueue/river/riverdriver v0.21.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
go.uber.org/goleak v1.3.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
66 changes: 66 additions & 0 deletions nilerror/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/riverqueue/river v0.21.0 h1:8bTEI664KIOFF18yFb2DX5ReyKb6YJ8MZIJArmbToNI=
github.com/riverqueue/river v0.21.0/go.mod h1:I5Fk3PvBl9i+fSjGLwLbVOWx7oTkrho5X6de67zQonA=
github.com/riverqueue/river/riverdriver v0.21.0 h1:DgluUSBiTcNOMNCJ3U7LHqAqYO5uQl1I5lupiXddYwY=
github.com/riverqueue/river/riverdriver v0.21.0/go.mod h1:Sa4sKc8e0InTH4MNS0foltRpkiiAvY5ZocknmLbI9/Q=
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.21.0 h1:9hrg15avu0nPCD35OAFhOyEr6/MU+Xg66+Y5VY1PQtg=
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.21.0/go.mod h1:vU1MlD7jzAeR0LsJ2kpGTLultgHi03IlEMAp3Uxz1bM=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0 h1:k4hzqeH/ntaOmEheKdCvFbB7SU+e1emct/SG0n2nrPM=
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.21.0/go.mod h1:dWTf+MUtkgfSmaCUaJmB//VE/uXHOOCJsEgor92nENw=
github.com/riverqueue/river/rivershared v0.21.0 h1:yRdCFfrVwVdi07m6NLjCdNePjwPt6wh7T8Fi1RbAncA=
github.com/riverqueue/river/rivershared v0.21.0/go.mod h1:eb61dWdwdXWaFa2fJ7BOiUe0hxe463f3g+oRDstw6/8=
github.com/riverqueue/river/rivertype v0.21.0 h1:4BhZ+T2pdVP3qqJYz9SJX4tR855B3UulRsjMUBXrywg=
github.com/riverqueue/river/rivertype v0.21.0/go.mod h1:lmdl3vLNDfchDWbYdW2uAocIuwIN+ZaXqAukdSCFqWs=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
76 changes: 76 additions & 0 deletions nilerror/hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Package nilerror provides a River hook for detecting a common Go error where
// a nil struct value is wrapped in a non-nil interface value. This commonly
// causes trouble with the error interface, where an unintentional nil error is
// returned.
//
// See: https://go.dev/doc/faq#nil_error.
//
// The package must use reflection to work, and therefore necessitates some
// overhead from doing so. Its recommended use is to only the hook in test
// environments and detect non-nil nil errors via thorough testing, but it can
// only be used in production environments and configured to return an error on
// a detected problem or log a warning.
package nilerror

import (
"context"
"errors"
"fmt"
"reflect"
"strings"

"github.com/riverqueue/river/rivershared/baseservice"
"github.com/riverqueue/river/rivertype"
)

// HookConfig is configuration for the nilerror hook.
type HookConfig struct {
// Suppress causes the hook to suppress detected nil struct values wrapped
// in non-nil error interface values and produce warning logging instead.
Suppress bool
}

// Hook is a River hook that detects nil error structs accidentally wrapped in a
// non-nil error interface, and either returns an error or logs a warning.
//
// See: https://go.dev/doc/faq#nil_error.
type Hook struct {
baseservice.BaseService
rivertype.Hook
config *HookConfig
}

// NewHook initializes a new River nilerror hook.
//
// config may be nil.
func NewHook(config *HookConfig) *Hook {
if config == nil {
config = &HookConfig{}
}
return &Hook{config: config}
}

func (h *Hook) WorkEnd(ctx context.Context, err error) error {
if err != nil {
errVal := reflect.ValueOf(err)
if errVal.IsNil() {
var (
nonPtrType = errVal.Type().Elem()
packagePath = nonPtrType.PkgPath()
lastSlash = strings.LastIndex(packagePath, "/")
packageName = packagePath[lastSlash+1:]
nilPtrName = fmt.Sprintf("(*%s.%s)(<nil>)", packageName, nonPtrType.Name())
message = fmt.Sprintf("non-nil error containing nil internal value (see: https://go.dev/doc/faq#nil_error); probably a bug: %s", nilPtrName)
)

if h.config.Suppress {
h.Logger.WarnContext(ctx, h.Name+": Got "+message)
return nil
}

return errors.New(message)
}
}

return err
}
Loading
Loading