Compare commits
1
Commits
main
..
808832cb8e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
808832cb8e |
No files matched your search
@@ -19,7 +19,7 @@ jobs:
|
||||
go-version: stable
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.15'
|
||||
python-version: '3.14'
|
||||
- name: Install goimports
|
||||
run: go install golang.org/x/tools/cmd/goimports@latest
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
@@ -23,14 +23,14 @@ repos:
|
||||
- id: go-imports
|
||||
args:
|
||||
- -local
|
||||
- git.unbound.se/shiny/otelsetup
|
||||
- gitea.unbound.se/shiny/otelsetup
|
||||
- repo: https://github.com/lietu/go-pre-commit
|
||||
rev: v1.0.0
|
||||
hooks:
|
||||
- id: go-test
|
||||
- id: gofumpt
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v2.15.0
|
||||
rev: v2.14.0
|
||||
hooks:
|
||||
- id: golangci-lint-full
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
|
||||
@@ -2,41 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.8.0] - 2026-10-17
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- Move module path to git.unbound.se (#212)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(deps)* Update module github.com/vektah/gqlparser/v2 to v2.5.62 (#209)
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(renovate)* Drop the OpenTelemetry group, now in the central config (#208)
|
||||
|
||||
## [0.7.0] - 2026-10-16
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- Add gqlgen Recover that logs panics as one structured record (#206)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(deps)* Update module codeberg.org/eventsourced/pg/v2 to v2.2.0 (#186)
|
||||
- *(deps)* Update module codeberg.org/eventsourced/pg/v2 to v2.2.1 (#188)
|
||||
- *(deps)* Update module codeberg.org/eventsourced/pg/v2 to v2.3.0 (#200)
|
||||
- *(deps)* Update opentelemetry (#198)
|
||||
- *(deps)* Update golang.org/x/net to v0.60.0 and golang.org/x/tools to v0.51.0 (#203)
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(deps)* Update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.27.0 (#190)
|
||||
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.14.0 (#192)
|
||||
- *(renovate)* Group OpenTelemetry, otelsetup and logging updates (#196)
|
||||
- *(deps)* Update dependency python to 3.15 (#204)
|
||||
|
||||
## [0.6.1] - 2026-09-19
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
@@ -17,7 +17,7 @@ Provides standardized OpenTelemetry configuration for tracing, metrics, and logg
|
||||
### Usage
|
||||
|
||||
```go
|
||||
import "git.unbound.se/shiny/otelsetup"
|
||||
import "gitea.unbound.se/shiny/otelsetup"
|
||||
|
||||
// Bootstrap OpenTelemetry SDK
|
||||
shutdown, err := otelsetup.SetupOTelSDK(ctx, enabled, serviceName, buildVersion, environment)
|
||||
@@ -28,12 +28,6 @@ defer shutdown(ctx)
|
||||
|
||||
// Wrap HTTP handlers with tracing
|
||||
http.Handle("/", otelsetup.Handler(myHandler))
|
||||
|
||||
// gqlgen server hooks
|
||||
srv.AroundOperations(otelsetup.AroundOperations)
|
||||
srv.AroundRootFields(otelsetup.AroundRootFields)
|
||||
srv.AroundFields(otelsetup.AroundFields)
|
||||
srv.SetRecoverFunc(otelsetup.Recover)
|
||||
```
|
||||
|
||||
### Features
|
||||
@@ -43,8 +37,6 @@ srv.SetRecoverFunc(otelsetup.Recover)
|
||||
- **Logging** - Stdout log exporter
|
||||
- **Propagation** - TraceContext and Baggage propagation
|
||||
- **HTTP Handler** - Middleware for automatic span creation
|
||||
- **gqlgen hooks** - Spans per operation, root field and resolver field
|
||||
- **gqlgen recover** - `Recover` logs a resolver panic as one ERROR record via `slog.Default()` (attributes `panic`, `stack`, `path`), records it on the active span with status Error, and returns the generic "internal system error" to the client (gqlgen's default prints the stack to stderr line by line)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
module git.unbound.se/shiny/otelsetup
|
||||
module gitea.unbound.se/shiny/otelsetup
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
codeberg.org/eventsourced/eventsourced v1.24.0
|
||||
codeberg.org/eventsourced/pg/v2 v2.4.0
|
||||
codeberg.org/eventsourced/pg/v2 v2.2.1
|
||||
github.com/99designs/gqlgen v0.17.95
|
||||
github.com/vektah/gqlparser/v2 v2.5.63
|
||||
go.opentelemetry.io/otel v1.47.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0
|
||||
go.opentelemetry.io/otel/log v1.47.0
|
||||
go.opentelemetry.io/otel/metric v1.47.0
|
||||
go.opentelemetry.io/otel/sdk v1.47.0
|
||||
go.opentelemetry.io/otel/sdk/log v1.47.0
|
||||
@@ -28,20 +28,20 @@ require (
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||
github.com/pressly/goose/v3 v3.29.0 // indirect
|
||||
github.com/sethvargo/go-retry v0.5.0 // indirect
|
||||
github.com/pressly/goose/v3 v3.28.0 // indirect
|
||||
github.com/sethvargo/go-retry v0.4.0 // indirect
|
||||
github.com/sosodev/duration v1.4.0 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.37 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect
|
||||
go.opentelemetry.io/otel/log v1.47.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/net v0.61.0 // indirect
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
golang.org/x/sys v0.49.0 // indirect
|
||||
golang.org/x/text v0.43.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 // indirect
|
||||
google.golang.org/grpc v1.84.0 // indirect
|
||||
golang.org/x/net v0.59.0 // indirect
|
||||
golang.org/x/sync v0.23.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
codeberg.org/eventsourced/eventsourced v1.24.0 h1:yNB+74JNB/NHo7D8ZR/yQcTVDYrWnjFf0xUKLgViqRc=
|
||||
codeberg.org/eventsourced/eventsourced v1.24.0/go.mod h1:uy0HxejDdNFpI+Yp7VOEvjfEUFJQX1VkCZ4e4JyDp7Q=
|
||||
codeberg.org/eventsourced/pg/v2 v2.4.0 h1:LMtYi4k08PrWMbJlAU/UbRkIis4m+StWYW6SYd4+D5E=
|
||||
codeberg.org/eventsourced/pg/v2 v2.4.0/go.mod h1:Sh++7RJSHIYJrscWs1kRpAKLIBU90EDiOuTte2C8xfY=
|
||||
codeberg.org/eventsourced/pg/v2 v2.2.1 h1:WAZg40cX+lxwy3/NvJpxjG3am9mpQucywMmi9IcdOF4=
|
||||
codeberg.org/eventsourced/pg/v2 v2.2.1/go.mod h1:qPxl3382m7lbhbrEaxwSZ9PrAPoODj2+TqnV3tIKWzU=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
@@ -15,16 +15,16 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg=
|
||||
github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-sql-driver/mysql v1.10.1 h1:arlSnNLq6a5yxGxV7qg9lF4j0C+KwD6NbQyKr9QL6ME=
|
||||
github.com/go-sql-driver/mysql v1.10.1/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
@@ -44,22 +44,22 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||
github.com/ncruces/go-strftime v1.1.0 h1:vtPmPWMkb539RGV9WpWT9EwJGvrOCKvMNI4obWL47Hk=
|
||||
github.com/ncruces/go-strftime v1.1.0/go.mod h1:yVaq7iwCN09vHRrKBrrflXT0M9tTMG2KmZ9Fj7xeG/g=
|
||||
github.com/pressly/goose/v3 v3.29.0 h1:dtEnBCT7CqkHIUCdgrXsrmeisaN8gFkRU4Y5OEwif4o=
|
||||
github.com/pressly/goose/v3 v3.29.0/go.mod h1:plA4aP4+u+Q3FMTgbFbhF8TuAWpAdfWpoFGkJsIFWh0=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pressly/goose/v3 v3.28.0 h1:D2M+iL31GmpZxSHOhX8mqyqAT3CXnokUmm0eKoSP+Vc=
|
||||
github.com/pressly/goose/v3 v3.28.0/go.mod h1:v26MOuB8bL3kzzrt3Vqhb3R0PRVsl8hFQKdrht/L6Rk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
|
||||
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sethvargo/go-retry v0.5.0 h1:JYfFKmVJ8ER06GReN1efzc5cm52BrVsFVzmcHiKJwfY=
|
||||
github.com/sethvargo/go-retry v0.5.0/go.mod h1:tvsjdKG6xfiCx4LSiUZ06kcv38xvdVQwv8R6/VnnVWg=
|
||||
github.com/sethvargo/go-retry v0.4.0 h1:9qy1OoIAxBL+gBYnkTnTnWle5wlfsXQlwRzIbbpdqPw=
|
||||
github.com/sethvargo/go-retry v0.4.0/go.mod h1:tvsjdKG6xfiCx4LSiUZ06kcv38xvdVQwv8R6/VnnVWg=
|
||||
github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE=
|
||||
github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/vektah/gqlparser/v2 v2.5.63 h1:vzHg/+/z7kZA3N4FhbGtF1pRo99KUpPzwXahk1bQtYk=
|
||||
github.com/vektah/gqlparser/v2 v2.5.63/go.mod h1:JNK+plRwKdXLsF/qPFPe5tE0z4s1WeroD9S5LR8um/Q=
|
||||
github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y=
|
||||
github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM=
|
||||
@@ -96,29 +96,29 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
|
||||
golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
||||
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 h1:cFbkNigDInw+wVTXc+CyhUsoHGJCw0BR6mlNoRVfre0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8/go.mod h1:QK6p7IgD7IZEZJBhhSUCib5X2vMOzlrGCGUw2XyVDh0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 h1:0SXBqli5dnuCKMjI/X3FM226zEaEa0GUcSCJtN1vL2o=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
|
||||
google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0=
|
||||
google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc h1:rZHRz0ogq4Pid1IKtA/ivHi8XONVF5tqUQXA2TtgpkE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:K/n0XLazCJHjD73YuMyNJsN38BPPy1kfFnKxUXaThFU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc h1:4bNTbnb44EqGVy9HaQxSY2jnafSUdgV3qHtedvNpDKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
|
||||
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
|
||||
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
modernc.org/libc v1.78.0 h1:Lpm+e9K6ruVYEGwWfuBO1xXtYEaXDouFOG1SuWjFDP4=
|
||||
modernc.org/libc v1.78.0/go.mod h1:wvxR55nOF2/66NkJoNV7/coXBTCvT3HR22rcqW6lOuA=
|
||||
modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
|
||||
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/sqlite v1.60.1 h1:/blz53O951KWFOso4QQvEs/Fq6cDBKLtMVrYNSeJVKw=
|
||||
modernc.org/sqlite v1.60.1/go.mod h1:1dIoEagfDE72QytD5scH1lxARtaUgKgHC/NuApA27r0=
|
||||
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
|
||||
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
// eventsourcedMeterName is the instrumentation scope for the event-sourcing
|
||||
// metrics emitted by the adapter returned from NewEventsourcedMetrics.
|
||||
// It keeps the old module path on purpose: renaming it breaks queries on otel_scope_name.
|
||||
const eventsourcedMeterName = "gitea.unbound.se/shiny/otelsetup/eventsourced"
|
||||
|
||||
// durationBucketsSeconds are explicit histogram boundaries tuned for
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
|
||||
"go.opentelemetry.io/otel/log/global"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
@@ -85,7 +86,7 @@ func SetupOTelSDK(ctx context.Context, enabled bool, serviceName, buildVersion,
|
||||
processor := log.NewSimpleProcessor(logExporter)
|
||||
logProvider := log.NewLoggerProvider(log.WithProcessor(processor))
|
||||
|
||||
otel.SetLoggerProvider(logProvider)
|
||||
global.SetLoggerProvider(logProvider)
|
||||
shutdownFuncs = append(shutdownFuncs, logProvider.Shutdown)
|
||||
|
||||
exp, err := otlpmetrichttp.New(ctx, otlpmetrichttp.WithHTTPClient(otlpHTTPClient()))
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package otelsetup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// Recover is a gqlgen recover function, installed with srv.SetRecoverFunc(otelsetup.Recover).
|
||||
// Unlike graphql.DefaultRecover, which prints the stack to stderr line by line, it logs the
|
||||
// panic as a single structured ERROR record (via slog.Default) with the stack as an attribute,
|
||||
// records it on the active span and marks the span as failed. The client gets the same generic
|
||||
// "internal system error" as with the default, without the panic text.
|
||||
func Recover(ctx context.Context, err any) error {
|
||||
stack := string(debug.Stack())
|
||||
panicErr, ok := err.(error)
|
||||
if !ok {
|
||||
panicErr = fmt.Errorf("%v", err)
|
||||
}
|
||||
|
||||
attrs := []any{
|
||||
slog.String("panic", panicErr.Error()),
|
||||
slog.String("stack", stack),
|
||||
}
|
||||
if fc := graphql.GetFieldContext(ctx); fc != nil {
|
||||
attrs = append(attrs, slog.String("path", fc.Path().String()))
|
||||
}
|
||||
slog.Default().ErrorContext(ctx, "recovered panic in graphql resolver", attrs...)
|
||||
|
||||
span := trace.SpanFromContext(ctx)
|
||||
span.RecordError(panicErr, trace.WithStackTrace(true))
|
||||
span.SetStatus(codes.Error, "panic: "+panicErr.Error())
|
||||
|
||||
return gqlerror.Errorf("internal system error")
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
package otelsetup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
||||
)
|
||||
|
||||
type recordingHandler struct {
|
||||
mu sync.Mutex
|
||||
records []slog.Record
|
||||
}
|
||||
|
||||
func (h *recordingHandler) Enabled(context.Context, slog.Level) bool { return true }
|
||||
|
||||
func (h *recordingHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.records = append(h.records, r.Clone())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *recordingHandler) WithAttrs([]slog.Attr) slog.Handler { return h }
|
||||
func (h *recordingHandler) WithGroup(string) slog.Handler { return h }
|
||||
|
||||
func TestRecover(t *testing.T) {
|
||||
var _ graphql.RecoverFunc = Recover
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
panicVal any
|
||||
wantPanic string
|
||||
}{
|
||||
{name: "error value", panicVal: errors.New("runtime error: invalid memory address or nil pointer dereference"), wantPanic: "runtime error: invalid memory address or nil pointer dereference"},
|
||||
{name: "string value", panicVal: "boom", wantPanic: "boom"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := &recordingHandler{}
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(slog.New(h))
|
||||
t.Cleanup(func() { slog.SetDefault(prev) })
|
||||
|
||||
sr := tracetest.NewSpanRecorder()
|
||||
tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))
|
||||
ctx, span := tp.Tracer("test").Start(context.Background(), "resolver")
|
||||
|
||||
err := Recover(ctx, tt.panicVal)
|
||||
span.End()
|
||||
|
||||
var gqlErr *gqlerror.Error
|
||||
if !errors.As(err, &gqlErr) || gqlErr.Message != "internal system error" {
|
||||
t.Fatalf("Recover() error = %v, want gqlerror \"internal system error\"", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), tt.wantPanic) {
|
||||
t.Errorf("Recover() error leaks panic text: %v", err)
|
||||
}
|
||||
|
||||
if len(h.records) != 1 {
|
||||
t.Fatalf("got %d log records, want exactly 1", len(h.records))
|
||||
}
|
||||
rec := h.records[0]
|
||||
if rec.Level != slog.LevelError {
|
||||
t.Errorf("log level = %v, want ERROR", rec.Level)
|
||||
}
|
||||
got := map[string]string{}
|
||||
rec.Attrs(func(a slog.Attr) bool {
|
||||
got[a.Key] = a.Value.String()
|
||||
return true
|
||||
})
|
||||
if got["panic"] != tt.wantPanic {
|
||||
t.Errorf("panic attr = %q, want %q", got["panic"], tt.wantPanic)
|
||||
}
|
||||
if !strings.Contains(got["stack"], "goroutine") {
|
||||
t.Errorf("stack attr missing stack trace: %q", got["stack"])
|
||||
}
|
||||
|
||||
spans := sr.Ended()
|
||||
if len(spans) != 1 {
|
||||
t.Fatalf("got %d spans, want 1", len(spans))
|
||||
}
|
||||
s := spans[0]
|
||||
if s.Status().Code != codes.Error {
|
||||
t.Errorf("span status = %v, want Error", s.Status().Code)
|
||||
}
|
||||
var exception bool
|
||||
for _, ev := range s.Events() {
|
||||
if ev.Name != "exception" {
|
||||
continue
|
||||
}
|
||||
exception = true
|
||||
attrs := map[string]string{}
|
||||
for _, kv := range ev.Attributes {
|
||||
attrs[string(kv.Key)] = kv.Value.String()
|
||||
}
|
||||
if attrs["exception.message"] != tt.wantPanic {
|
||||
t.Errorf("exception.message = %q, want %q", attrs["exception.message"], tt.wantPanic)
|
||||
}
|
||||
if attrs["exception.stacktrace"] == "" {
|
||||
t.Error("exception event has no stacktrace")
|
||||
}
|
||||
}
|
||||
if !exception {
|
||||
t.Error("span has no exception event")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,15 @@
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"groupName": "OpenTelemetry",
|
||||
"matchPackageNames": [
|
||||
"go.opentelemetry.io/**",
|
||||
"gitea.unbound.se/shiny/otelsetup",
|
||||
"gitea.unbound.se/shiny/logging"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
// subscriptionMeterName is the instrumentation scope for the cross-service
|
||||
// subscription push metrics emitted by SubscriptionMetrics.
|
||||
// It keeps the old module path on purpose: renaming it breaks queries on otel_scope_name.
|
||||
const subscriptionMeterName = "gitea.unbound.se/shiny/otelsetup/subscriptions"
|
||||
|
||||
// SubscriptionMetrics records cross-service subscription push outcomes (the
|
||||
@@ -18,7 +17,7 @@ const subscriptionMeterName = "gitea.unbound.se/shiny/otelsetup/subscriptions"
|
||||
// counters.
|
||||
//
|
||||
// Its method set (Pushed/PushSkipped/Dropped/ChannelFull) satisfies the
|
||||
// Observer interface of git.unbound.se/shiny/subscriptions *structurally* —
|
||||
// Observer interface of gitea.unbound.se/shiny/subscriptions *structurally* —
|
||||
// otelsetup does not import that library. A service constructs it and passes it
|
||||
// to the registry:
|
||||
//
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
||||
)
|
||||
|
||||
// observerShape mirrors git.unbound.se/shiny/subscriptions.Observer. This
|
||||
// observerShape mirrors gitea.unbound.se/shiny/subscriptions.Observer. This
|
||||
// compile-time assertion guards that SubscriptionMetrics still satisfies that
|
||||
// interface structurally, without otelsetup importing the library.
|
||||
//
|
||||
|
||||
Reference in new issue
Block a user