Skip to content

Commit 5ed3826

Browse files
authoredJan 2, 2023
feat: implement prometheus metrics (#36)
* feat: bootstrap metrics server * feat: add http metrics * chore: fine tune metrics settings * doc: describe metrics usage
1 parent a4aa705 commit 5ed3826

File tree

7 files changed

+227
-11
lines changed

7 files changed

+227
-11
lines changed
 

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ vet: ## Run go vet against code.
2626

2727
run-server: fmt vet ## Run server.
2828
echo "test" > local_dev.token
29-
go run . serve --db-dsn ./test.sqlite3?_journal_mode=WAL --log-devel --log-level 12 --auth-token-file local_dev.token
29+
go run . serve --db-dsn ./test.sqlite3?_journal_mode=WAL --metrics-addr :8081 --log-devel --log-level 12 --auth-token-file local_dev.token
3030

3131
run-migrate: fmt vet ## Run migration.
3232
go run . migrate --db-dsn ./test.sqlite3?_journal_mode=WAL --log-devel --log-level 12 ./data

‎README.md

+8
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ By default, sqlite-rest exposes **no** tables/views from accessing. To allow acc
148148
--security-allow-table books,authors
149149
```
150150

151+
### Metrics
152+
153+
sqlite-rest exposes metrics via [Prometheus][prometheus] format. By default, these metrics are exposed via `:8081/metrics` endpoint. To change the endpoint, please use `--metrics-addr` flag. To disable metrics, specific `--metrics-addr` to `""`.
154+
155+
Recorded metrics can be found in [metrics.go](metrics.go).
156+
157+
[prometheus]: https://prometheus.io/
158+
151159
### Database Migrations
152160

153161
sqlite-rest supports database migrations via [golang-migrate][golang-migrate].

‎go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/golang-migrate/migrate/v4 v4.15.2
1212
github.com/jmoiron/sqlx v1.3.5
1313
github.com/mattn/go-sqlite3 v1.14.16
14+
github.com/prometheus/client_golang v1.14.0
1415
github.com/spf13/cobra v1.6.1
1516
github.com/spf13/pflag v1.0.5
1617
github.com/stretchr/testify v1.8.1
@@ -20,12 +21,21 @@ require (
2021
)
2122

2223
require (
24+
github.com/beorn7/perks v1.0.1 // indirect
25+
github.com/cespare/xxhash/v2 v2.1.2 // indirect
2326
github.com/davecgh/go-spew v1.1.1 // indirect
27+
github.com/golang/protobuf v1.5.2 // indirect
2428
github.com/hashicorp/errwrap v1.1.0 // indirect
2529
github.com/hashicorp/go-multierror v1.1.1 // indirect
2630
github.com/inconshreveable/mousetrap v1.0.1 // indirect
31+
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
2732
github.com/pmezard/go-difflib v1.0.0 // indirect
33+
github.com/prometheus/client_model v0.3.0 // indirect
34+
github.com/prometheus/common v0.37.0 // indirect
35+
github.com/prometheus/procfs v0.8.0 // indirect
2836
go.uber.org/atomic v1.7.0 // indirect
2937
go.uber.org/multierr v1.6.0 // indirect
38+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
39+
google.golang.org/protobuf v1.28.1 // indirect
3040
gopkg.in/yaml.v3 v3.0.1 // indirect
3141
)

‎go.sum

+24
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx
155155
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
156156
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
157157
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
158+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
158159
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
159160
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
160161
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
@@ -180,6 +181,7 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6
180181
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
181182
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
182183
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
184+
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
183185
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
184186
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
185187
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
@@ -417,10 +419,12 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
417419
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
418420
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
419421
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
422+
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
420423
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
421424
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
422425
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
423426
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
427+
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
424428
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
425429
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
426430
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
@@ -536,6 +540,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
536540
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
537541
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
538542
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
543+
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
539544
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
540545
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
541546
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -557,6 +562,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
557562
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
558563
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
559564
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
565+
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
560566
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
561567
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
562568
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -802,6 +808,7 @@ github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4
802808
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
803809
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
804810
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
811+
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
805812
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
806813
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
807814
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -928,11 +935,16 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
928935
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
929936
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
930937
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
938+
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
939+
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
940+
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
931941
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
932942
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
933943
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
934944
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
935945
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
946+
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
947+
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
936948
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
937949
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
938950
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -941,6 +953,9 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+
941953
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
942954
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
943955
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
956+
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
957+
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
958+
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
944959
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
945960
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
946961
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -953,6 +968,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
953968
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
954969
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
955970
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
971+
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
972+
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
956973
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
957974
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
958975
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -1279,6 +1296,7 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx
12791296
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
12801297
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
12811298
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
1299+
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
12821300
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
12831301
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
12841302
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1298,6 +1316,7 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
12981316
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
12991317
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
13001318
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1319+
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
13011320
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
13021321
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
13031322
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1429,7 +1448,10 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc
14291448
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14301449
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14311450
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1451+
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14321452
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1453+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
1454+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14331455
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
14341456
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
14351457
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1702,6 +1724,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
17021724
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
17031725
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
17041726
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
1727+
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
1728+
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
17051729
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
17061730
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
17071731
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

‎metrics.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"time"
8+
9+
"github.com/go-chi/chi/v5"
10+
"github.com/go-chi/chi/v5/middleware"
11+
"github.com/go-logr/logr"
12+
"github.com/prometheus/client_golang/prometheus"
13+
"github.com/prometheus/client_golang/prometheus/promauto"
14+
"github.com/prometheus/client_golang/prometheus/promhttp"
15+
"github.com/spf13/pflag"
16+
)
17+
18+
const metricsServerDisabledAddr = ""
19+
20+
type MetricsServerOptions struct {
21+
Logger logr.Logger
22+
Addr string
23+
}
24+
25+
func (opts *MetricsServerOptions) bindCLIFlags(fs *pflag.FlagSet) {
26+
fs.StringVar(
27+
&opts.Addr, "metrics-addr", ":8081",
28+
"metrics server listen address. Empty value means disabled.",
29+
)
30+
}
31+
32+
func (opts *MetricsServerOptions) defaults() error {
33+
if opts.Logger.GetSink() == nil {
34+
opts.Logger = logr.Discard()
35+
}
36+
37+
return nil
38+
}
39+
40+
type metricsServer struct {
41+
logger logr.Logger
42+
server *http.Server
43+
}
44+
45+
func NewMetricsServer(opts MetricsServerOptions) (*metricsServer, error) {
46+
if err := opts.defaults(); err != nil {
47+
return nil, err
48+
}
49+
50+
srv := &metricsServer{
51+
logger: opts.Logger,
52+
}
53+
54+
if opts.Addr == metricsServerDisabledAddr {
55+
return srv, nil
56+
}
57+
58+
serverMux := http.NewServeMux()
59+
serverMux.Handle("/metrics", promhttp.Handler())
60+
srv.server = &http.Server{
61+
Addr: opts.Addr,
62+
Handler: serverMux,
63+
}
64+
65+
return srv, nil
66+
}
67+
68+
func (server *metricsServer) Start(done <-chan struct{}) {
69+
if server.server == nil {
70+
server.logger.V(8).Info("metrics server is disabled")
71+
return
72+
}
73+
74+
go server.server.ListenAndServe()
75+
76+
server.logger.Info("metrics server started", "addr", server.server.Addr)
77+
<-done
78+
79+
server.logger.Info("shutting metrics server")
80+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
81+
defer cancel()
82+
server.server.Shutdown(shutdownCtx)
83+
}
84+
85+
const (
86+
metricsNamespace = "sqlite_rest"
87+
metricsLabelTarget = "target" // name of the table/view
88+
metricsLabelTargetOperation = "operation" // name of the operation
89+
metricsLabelHTTPCode = "http_code" // HTTP response code
90+
)
91+
92+
var (
93+
metricsAuthFailedRequestsTotal = promauto.NewCounter(
94+
prometheus.CounterOpts{
95+
Namespace: metricsNamespace,
96+
Name: "auth_failed_requests_total",
97+
Help: "Total number of failed authentication requests",
98+
},
99+
)
100+
101+
metricsAccessCheckFailedRequestsTotal = promauto.NewCounter(
102+
prometheus.CounterOpts{
103+
Namespace: metricsNamespace,
104+
Name: "access_check_failed_requests_total",
105+
Help: "Total number of failed access check requests",
106+
},
107+
)
108+
109+
metricsRequestTotal = promauto.NewCounterVec(
110+
prometheus.CounterOpts{
111+
Namespace: metricsNamespace,
112+
Name: "http_requests_total",
113+
Help: "Total number of HTTP requests",
114+
},
115+
[]string{metricsLabelTarget, metricsLabelTargetOperation, metricsLabelHTTPCode},
116+
)
117+
118+
metricsRequestLatency = promauto.NewHistogramVec(
119+
prometheus.HistogramOpts{
120+
Namespace: metricsNamespace,
121+
Name: "http_request_duration_milliseconds",
122+
Help: "HTTP request latency",
123+
Buckets: []float64{1, 10, 100, 500, 1000},
124+
},
125+
[]string{metricsLabelTarget, metricsLabelTargetOperation, metricsLabelHTTPCode},
126+
)
127+
)
128+
129+
func recordRequestMetrics(op string) func(http.Handler) http.Handler {
130+
return func(next http.Handler) http.Handler {
131+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
132+
start := time.Now()
133+
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
134+
135+
defer func() {
136+
httpCode := fmt.Sprint(ww.Status())
137+
target := chi.URLParam(r, routeVarTableOrView)
138+
metricsRequestTotal.
139+
WithLabelValues(target, op, httpCode).
140+
Inc()
141+
metricsRequestLatency.
142+
WithLabelValues(target, op, httpCode).
143+
Observe(float64(time.Since(start).Milliseconds()))
144+
}()
145+
146+
next.ServeHTTP(ww, r)
147+
})
148+
}
149+
}

‎server.go

+35-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"errors"
77
"fmt"
88
"net/http"
9+
"os"
10+
"os/signal"
11+
"syscall"
912
"time"
1013

1114
"github.com/go-chi/chi/v5"
@@ -31,7 +34,8 @@ type ServerOptions struct {
3134
}
3235

3336
func (opts *ServerOptions) bindCLIFlags(fs *pflag.FlagSet) {
34-
fs.StringVar(&opts.Addr, "http-addr", ":8080", "server listen addr")
37+
fs.StringVar(&opts.Addr, "http-addr", ":8080", "server listen address")
38+
3539
opts.AuthOptions.bindCLIFlags(fs)
3640
opts.SecurityOptions.bindCLIFlags(fs)
3741
}
@@ -97,16 +101,22 @@ func NewServer(opts *ServerOptions) (*dbServer, error) {
97101
{
98102
serverMux.
99103
With(
100-
opts.AuthOptions.createAuthMiddleware(rv.responseError),
101-
opts.SecurityOptions.createTableOrViewAccessCheckMiddleware(rv.responseError, routeVarTableOrView),
104+
opts.AuthOptions.createAuthMiddleware(func(w http.ResponseWriter, err error) {
105+
metricsAuthFailedRequestsTotal.Inc()
106+
rv.responseError(w, err)
107+
}),
108+
opts.SecurityOptions.createTableOrViewAccessCheckMiddleware(func(w http.ResponseWriter, err error) {
109+
metricsAccessCheckFailedRequestsTotal.Inc()
110+
rv.responseError(w, err)
111+
}),
102112
).
103113
Group(func(r chi.Router) {
104114
routePattern := fmt.Sprintf("/{%s:[^/]+}", routeVarTableOrView)
105-
r.Get(routePattern, rv.handleQueryTableOrView)
106-
r.Post(routePattern, rv.handleInsertTable)
107-
r.Patch(routePattern, rv.handleUpdateTable)
108-
r.Put(routePattern, rv.handleUpdateSingleEntity)
109-
r.Delete(routePattern, rv.handleDeleteTable)
115+
r.With(recordRequestMetrics("queryTableOrView")).Get(routePattern, rv.handleQueryTableOrView)
116+
r.With(recordRequestMetrics("insertTable")).Post(routePattern, rv.handleInsertTable)
117+
r.With(recordRequestMetrics("updateTable")).Patch(routePattern, rv.handleUpdateTable)
118+
r.With(recordRequestMetrics("updateSingleEntity")).Put(routePattern, rv.handleUpdateSingleEntity)
119+
r.With(recordRequestMetrics("deleteTable")).Delete(routePattern, rv.handleDeleteTable)
110120
})
111121
}
112122

@@ -342,6 +352,7 @@ func (server *dbServer) handleDeleteTable(
342352

343353
func createServeCmd() *cobra.Command {
344354
serverOpts := new(ServerOptions)
355+
metricsServerOpts := new(MetricsServerOptions)
345356

346357
cmd := &cobra.Command{
347358
Use: "serve",
@@ -372,16 +383,31 @@ func createServeCmd() *cobra.Command {
372383
return err
373384
}
374385

386+
metricsServerOpts.Logger = logger
387+
metricsServer, err := NewMetricsServer(*metricsServerOpts)
388+
if err != nil {
389+
setupLogger.Error(err, "failed to create metrics server")
390+
return err
391+
}
392+
375393
ctx, cancel := context.WithCancel(context.Background())
376394
defer cancel()
377395

378-
server.Start(ctx.Done())
396+
sigs := make(chan os.Signal, 1)
397+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
398+
399+
done := ctx.Done()
400+
401+
go metricsServer.Start(done)
402+
go server.Start(done)
403+
<-sigs
379404

380405
return nil
381406
},
382407
}
383408

384409
serverOpts.bindCLIFlags(cmd.Flags())
410+
metricsServerOpts.bindCLIFlags(cmd.Flags())
385411
bindDBDSNFlag(cmd.Flags())
386412

387413
return cmd

‎server_security.go

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ func (opts *ServerSecurityOptions) defaults() error {
2929

3030
func (opts *ServerSecurityOptions) createTableOrViewAccessCheckMiddleware(
3131
responseErr func(w http.ResponseWriter, err error),
32-
routeVarTableOrView string,
3332
) func(http.Handler) http.Handler {
3433
accesibleTableOrViews := make(map[string]struct{})
3534
for _, t := range opts.EnabledTableOrViews {

0 commit comments

Comments
 (0)
Please sign in to comment.