From 9e0552c509da145d22e26c89adfa6f519ee25645 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Mon, 19 Dec 2022 19:15:04 -0600 Subject: [PATCH 1/2] Setup basic prom metrics endpoint --- go.mod | 17 ++++++++++----- go.sum | 39 ++++++++++++++++++++++++++--------- pkg/api/api.go | 9 ++++++++ pkg/api/routes.go | 7 +++++++ pkg/api/session_manager.go | 6 +++++- pkg/cli/cli.go | 20 ++++++++++++++++++ pkg/command/options.go | 3 +++ pkg/metrics/metrics.go | 42 ++++++++++++++++++++++++++++++++++++++ pkg/metrics/server.go | 39 +++++++++++++++++++++++++++++++++++ 9 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 pkg/metrics/metrics.go create mode 100644 pkg/metrics/server.go diff --git a/go.mod b/go.mod index 24fd4e83e..4687135e9 100644 --- a/go.mod +++ b/go.mod @@ -14,13 +14,16 @@ require ( github.com/lib/pq v1.10.5 github.com/mitchellh/go-homedir v1.1.0 github.com/mr-tron/base58 v1.2.0 + github.com/prometheus/client_golang v1.14.0 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.8.0 github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -31,13 +34,17 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/ugorji/go/codec v1.2.6 // indirect - golang.org/x/sys v0.2.0 // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dd6aa2a74..4ca4ba8e3 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,10 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 h1:VauE2GcJNZFun2Och6tIT2zJZK1v6jxALQDA9BIji/E= github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -25,12 +29,14 @@ github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPg github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= @@ -60,6 +66,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -73,19 +81,29 @@ github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjW github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 h1:yL0l/u242MzDP6D0B5vGC+wxm5WRY+alQZy+dJk3bFI= github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948/go.mod h1:a06d/M1pxWi51qiSrfGMHaEydtuXT06nha8N2aNQuXk= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -101,6 +119,7 @@ golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4Wwu golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -111,23 +130,22 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -138,5 +156,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= diff --git a/pkg/api/api.go b/pkg/api/api.go index 28dd4de16..7ba572f5e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -16,6 +16,7 @@ import ( "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" + "github.com/sosedoff/pgweb/pkg/metrics" "github.com/sosedoff/pgweb/pkg/shared" "github.com/sosedoff/pgweb/static" ) @@ -278,6 +279,12 @@ func Disconnect(c *gin.Context) { return } + if command.Opts.Sessions { + result := DbSessions.Remove(getSessionId(c.Request)) + successResponse(c, gin.H{"success": result}) + return + } + conn := DB(c) if conn == nil { badRequest(c, errNotConnected) @@ -492,6 +499,8 @@ func GetTableConstraints(c *gin.Context) { // HandleQuery runs the database query func HandleQuery(query string, c *gin.Context) { + metrics.IncrementQueriesCount() + rawQuery, err := base64.StdEncoding.DecodeString(desanitize64(query)) if err == nil { query = string(rawQuery) diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 35048b165..52e59dd27 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -4,6 +4,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sosedoff/pgweb/pkg/command" + "github.com/sosedoff/pgweb/pkg/metrics" ) func SetupMiddlewares(group *gin.RouterGroup) { @@ -53,3 +54,9 @@ func SetupRoutes(router *gin.Engine) { api.GET("/bookmarks", GetBookmarks) api.GET("/export", DataExport) } + +func SetupMetrics(engine *gin.Engine) { + if command.Opts.MetricsEnabled && command.Opts.MetricsAddr == "" { + engine.GET("/metrics", gin.WrapH(metrics.Handler())) + } +} diff --git a/pkg/api/session_manager.go b/pkg/api/session_manager.go index 9166f9f0b..a3b34bd0d 100644 --- a/pkg/api/session_manager.go +++ b/pkg/api/session_manager.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/sosedoff/pgweb/pkg/client" + "github.com/sosedoff/pgweb/pkg/metrics" ) type SessionManager struct { @@ -58,8 +59,10 @@ func (m *SessionManager) Get(id string) *client.Client { func (m *SessionManager) Add(id string, conn *client.Client) { m.mu.Lock() + defer m.mu.Unlock() + m.sessions[id] = conn - m.mu.Unlock() + metrics.SetSessionsCount(len(m.sessions)) } func (m *SessionManager) Remove(id string) bool { @@ -72,6 +75,7 @@ func (m *SessionManager) Remove(id string) bool { delete(m.sessions, id) } + metrics.SetSessionsCount(len(m.sessions)) return ok } diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 7b92bbc9a..9bdbda3e6 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -19,6 +19,7 @@ import ( "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" + "github.com/sosedoff/pgweb/pkg/metrics" "github.com/sosedoff/pgweb/pkg/util" ) @@ -199,9 +200,12 @@ func startServer() { api.SetLogger(logger) api.SetupRoutes(router) + api.SetupMetrics(router) fmt.Println("Starting server...") go func() { + metrics.SetHealty(true) + err := router.Run(fmt.Sprintf("%v:%v", options.HTTPHost, options.HTTPPort)) if err != nil { fmt.Println("Cant start server:", err) @@ -213,6 +217,18 @@ func startServer() { }() } +func startMetricsServer() { + serverAddr := fmt.Sprintf("%v:%v", command.Opts.HTTPHost, command.Opts.HTTPPort) + if options.MetricsAddr == serverAddr { + return + } + + err := metrics.StartServer(logger, options.MetricsPath, options.MetricsAddr) + if err != nil { + logger.WithError(err).Fatal("unable to start prometheus metrics server") + } +} + func handleSignals() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) @@ -265,6 +281,10 @@ func Run() { } } + if options.MetricsEnabled && options.MetricsAddr != "" { + go startMetricsServer() + } + startServer() openPage() handleSignals() diff --git a/pkg/command/options.go b/pkg/command/options.go index 0269e1e08..702db43c7 100644 --- a/pkg/command/options.go +++ b/pkg/command/options.go @@ -58,6 +58,9 @@ type Options struct { Cors bool `long:"cors" description:"Enable Cross-Origin Resource Sharing (CORS)"` CorsOrigin string `long:"cors-origin" description:"Allowed CORS origins" default:"*"` BinaryCodec string `long:"binary-codec" description:"Codec for binary data serialization, one of 'none', 'hex', 'base58', 'base64'" default:"none"` + MetricsEnabled bool `long:"metrics" description:"Enable Prometheus metrics endpoint"` + MetricsPath string `long:"metrics-path" description:"Path prefix for Prometheus metrics endpoint" default:"/metrics"` + MetricsAddr string `long:"metrics-addr" description:"Listen host and port for Prometheus metrics server"` } var Opts Options diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 000000000..c02feef3b --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,42 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +var ( + sessionsGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: "pgweb", + Name: "sessions_count", + Help: "Total number of database sessions", + }) + + queriesCounter = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: "pgweb", + Name: "queries_count", + Help: "Total number of custom queries executed", + }) + + healtyGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "healty", + Namespace: "pgweb", + Help: "Server health status", + }) +) + +func IncrementQueriesCount() { + queriesCounter.Inc() +} + +func SetSessionsCount(val int) { + sessionsGauge.Set(float64(val)) +} + +func SetHealty(val bool) { + healthy := 0.0 + if val { + healthy = 1.0 + } + healtyGauge.Set(float64(healthy)) +} diff --git a/pkg/metrics/server.go b/pkg/metrics/server.go new file mode 100644 index 000000000..0b260a51c --- /dev/null +++ b/pkg/metrics/server.go @@ -0,0 +1,39 @@ +package metrics + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" +) + +var ( + registry = prometheus.NewRegistry() + + handler = promhttp.HandlerFor( + registry, + promhttp.HandlerOpts{ + EnableOpenMetrics: false, + }, + ) +) + +func init() { + registry.MustRegister( + sessionsGauge, + queriesCounter, + healtyGauge, + ) +} + +func Handler() http.Handler { + return handler +} + +func StartServer(logger *logrus.Logger, path string, addr string) error { + logger.WithField("addr", addr).WithField("path", path).Info("starting prometheus metrics server") + + http.Handle(path, handler) + return http.ListenAndServe(addr, nil) +} From 0bbc2acece2b7d7afe0e485568090835a7f2d9b7 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Tue, 20 Dec 2022 09:03:31 -0600 Subject: [PATCH 2/2] Use default prom handler to expose go runtime metrics --- pkg/metrics/metrics.go | 15 ++++++--------- pkg/metrics/server.go | 24 ++---------------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index c02feef3b..43b9bf933 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -7,21 +7,18 @@ import ( var ( sessionsGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Namespace: "pgweb", - Name: "sessions_count", - Help: "Total number of database sessions", + Name: "pgweb_sessions_count", + Help: "Total number of database sessions", }) queriesCounter = promauto.NewCounter(prometheus.CounterOpts{ - Namespace: "pgweb", - Name: "queries_count", - Help: "Total number of custom queries executed", + Name: "pgweb_queries_count", + Help: "Total number of custom queries executed", }) healtyGauge = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "healty", - Namespace: "pgweb", - Help: "Server health status", + Name: "pgweb_healty", + Help: "Server health status", }) ) diff --git a/pkg/metrics/server.go b/pkg/metrics/server.go index 0b260a51c..7291d61a4 100644 --- a/pkg/metrics/server.go +++ b/pkg/metrics/server.go @@ -3,37 +3,17 @@ package metrics import ( "net/http" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sirupsen/logrus" ) -var ( - registry = prometheus.NewRegistry() - - handler = promhttp.HandlerFor( - registry, - promhttp.HandlerOpts{ - EnableOpenMetrics: false, - }, - ) -) - -func init() { - registry.MustRegister( - sessionsGauge, - queriesCounter, - healtyGauge, - ) -} - func Handler() http.Handler { - return handler + return promhttp.Handler() } func StartServer(logger *logrus.Logger, path string, addr string) error { logger.WithField("addr", addr).WithField("path", path).Info("starting prometheus metrics server") - http.Handle(path, handler) + http.Handle(path, Handler()) return http.ListenAndServe(addr, nil) }