From efebea7a186d6b78f7808210545f4b3565796934 Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 15:07:19 +0300 Subject: [PATCH 1/8] add prometheus metrics --- cmd/root.go | 26 ++++++ go.mod | 1 + go.sum | 8 ++ pkg/prom/db_stats_collector.go | 143 +++++++++++++++++++++++++++++++++ pkg/prom/prom.go | 27 +++++++ pkg/prom/serve.go | 31 +++++++ 6 files changed, 236 insertions(+) create mode 100644 pkg/prom/db_stats_collector.go create mode 100644 pkg/prom/prom.go create mode 100644 pkg/prom/serve.go diff --git a/cmd/root.go b/cmd/root.go index fdcd87d2..608c9ebd 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -24,6 +24,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/vulcanize/ipld-eth-server/pkg/prom" ) var ( @@ -62,6 +63,19 @@ func initFuncs(cmd *cobra.Command, args []string) { if err := logLevel(); err != nil { log.Fatal("Could not set log level: ", err) } + + if viper.GetBool("metrics") { + prom.Init() + } + + if viper.GetBool("http") { + addr := fmt.Sprintf( + "%s:%s", + viper.GetString("http.addr"), + viper.GetString("http.port"), + ) + prom.Serve(addr) + } } func logLevel() error { @@ -93,6 +107,12 @@ func init() { rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file") rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic") + rootCmd.PersistentFlags().Bool("metrics", false, "enable metrics") + + rootCmd.PersistentFlags().Bool("http", false, "enable http service") + rootCmd.PersistentFlags().String("http-addr", "127.0.0.1", "http host") + rootCmd.PersistentFlags().String("http-port", "8080", "http port") + viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile")) viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port")) @@ -100,6 +120,12 @@ func init() { viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user")) viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password")) viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level")) + + viper.BindPFlag("metrics", rootCmd.PersistentFlags().Lookup("metrics")) + + viper.BindPFlag("http", rootCmd.PersistentFlags().Lookup("http")) + viper.BindPFlag("http.addr", rootCmd.PersistentFlags().Lookup("http-addr")) + viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("http-port")) } func initConfig() { diff --git a/go.mod b/go.mod index 028423f3..00b84a37 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/multiformats/go-multihash v0.0.13 github.com/onsi/ginkgo v1.12.1 github.com/onsi/gomega v1.10.1 + github.com/prometheus/client_golang v1.5.1 github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.7.0 diff --git a/go.sum b/go.sum index 054a0e26..a1503247 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -703,6 +704,7 @@ github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuuj github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= @@ -827,21 +829,25 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= @@ -953,6 +959,8 @@ github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5 h1:U+BqhjRLR22e9OEm8cgW github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5/go.mod h1:7oC0Ni6dosMv5pxMigm6s0hN8g4haJMBnqmmo0D9YfQ= github.com/vulcanize/ipld-eth-indexer v0.2.0-alpha h1:+XVaC7TsA0K278YWpfqdrNxwgC6hY6fBaN8w2/e1Lts= github.com/vulcanize/ipld-eth-indexer v0.2.0-alpha/go.mod h1:SuMBscFfcBHYlQuzDDd4by+R0S3gaAFjrOU+uQfAefE= +github.com/vulcanize/ipld-eth-indexer v0.4.0-alpha h1:DXZ/hF65uegvh70YZTcPM6InMUxGkWxee6awyLcrhDg= +github.com/vulcanize/ipld-eth-indexer v0.4.0-alpha/go.mod h1:SuMBscFfcBHYlQuzDDd4by+R0S3gaAFjrOU+uQfAefE= github.com/vulcanize/pg-ipfs-ethdb v0.0.1-alpha h1:Y7j0Hw1jgVVOg+eUGUr7OgH+gOBID0DwbsfZV1KoL7I= github.com/vulcanize/pg-ipfs-ethdb v0.0.1-alpha/go.mod h1:OuqE4r2LGWAtDVx3s1yaAzDcwy+LEAqrWaE1L8UfrGY= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= diff --git a/pkg/prom/db_stats_collector.go b/pkg/prom/db_stats_collector.go new file mode 100644 index 00000000..0b225899 --- /dev/null +++ b/pkg/prom/db_stats_collector.go @@ -0,0 +1,143 @@ +package prom + +import ( + "database/sql" + + "github.com/prometheus/client_golang/prometheus" +) + +const subsystem = "connections" + +// DBStatsGetter is an interface that gets sql.DBStats. +type DBStatsGetter interface { + Stats() sql.DBStats +} + +// DBStatsCollector implements the prometheus.Collector interface. +type DBStatsCollector struct { + sg DBStatsGetter + + // descriptions of exported metrics + maxOpenDesc *prometheus.Desc + openDesc *prometheus.Desc + inUseDesc *prometheus.Desc + idleDesc *prometheus.Desc + waitedForDesc *prometheus.Desc + blockedSecondsDesc *prometheus.Desc + closedMaxIdleDesc *prometheus.Desc + closedMaxLifetimeDesc *prometheus.Desc +} + +// NewDBStatsCollector creates a new DBStatsCollector. +func NewDBStatsCollector(dbName string, sg DBStatsGetter) *DBStatsCollector { + labels := prometheus.Labels{"db_name": dbName} + return &DBStatsCollector{ + sg: sg, + maxOpenDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "max_open"), + "Maximum number of open connections to the database.", + nil, + labels, + ), + openDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "open"), + "The number of established connections both in use and idle.", + nil, + labels, + ), + inUseDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "in_use"), + "The number of connections currently in use.", + nil, + labels, + ), + idleDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "idle"), + "The number of idle connections.", + nil, + labels, + ), + waitedForDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "waited_for"), + "The total number of connections waited for.", + nil, + labels, + ), + blockedSecondsDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "blocked_seconds"), + "The total time blocked waiting for a new connection.", + nil, + labels, + ), + closedMaxIdleDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "closed_max_idle"), + "The total number of connections closed due to SetMaxIdleConns.", + nil, + labels, + ), + closedMaxLifetimeDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "closed_max_lifetime"), + "The total number of connections closed due to SetConnMaxLifetime.", + nil, + labels, + ), + } +} + +// Describe implements the prometheus.Collector interface. +func (c DBStatsCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.maxOpenDesc + ch <- c.openDesc + ch <- c.inUseDesc + ch <- c.idleDesc + ch <- c.waitedForDesc + ch <- c.blockedSecondsDesc + ch <- c.closedMaxIdleDesc + ch <- c.closedMaxLifetimeDesc +} + +// Collect implements the prometheus.Collector interface. +func (c DBStatsCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.sg.Stats() + + ch <- prometheus.MustNewConstMetric( + c.maxOpenDesc, + prometheus.GaugeValue, + float64(stats.MaxOpenConnections), + ) + ch <- prometheus.MustNewConstMetric( + c.openDesc, + prometheus.GaugeValue, + float64(stats.OpenConnections), + ) + ch <- prometheus.MustNewConstMetric( + c.inUseDesc, + prometheus.GaugeValue, + float64(stats.InUse), + ) + ch <- prometheus.MustNewConstMetric( + c.idleDesc, + prometheus.GaugeValue, + float64(stats.Idle), + ) + ch <- prometheus.MustNewConstMetric( + c.waitedForDesc, + prometheus.CounterValue, + float64(stats.WaitCount), + ) + ch <- prometheus.MustNewConstMetric( + c.blockedSecondsDesc, + prometheus.CounterValue, + stats.WaitDuration.Seconds(), + ) + ch <- prometheus.MustNewConstMetric( + c.closedMaxIdleDesc, + prometheus.CounterValue, + float64(stats.MaxIdleClosed), + ) + ch <- prometheus.MustNewConstMetric( + c.closedMaxLifetimeDesc, + prometheus.CounterValue, + float64(stats.MaxLifetimeClosed), + ) +} diff --git a/pkg/prom/prom.go b/pkg/prom/prom.go new file mode 100644 index 00000000..ebf95c86 --- /dev/null +++ b/pkg/prom/prom.go @@ -0,0 +1,27 @@ +package prom + +import ( + "github.com/jmoiron/sqlx" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + namespace = "ipld_eth_server" + statsSubsystem = "stats" +) + +var ( + metrics bool +) + +// Init module initialization +func Init() { + metrics = true +} + +// RegisterDBCollector create metric colletor for given connection +func RegisterDBCollector(name string, db *sqlx.DB) { + if metrics { + prometheus.Register(NewDBStatsCollector(name, db)) + } +} diff --git a/pkg/prom/serve.go b/pkg/prom/serve.go new file mode 100644 index 00000000..058d1772 --- /dev/null +++ b/pkg/prom/serve.go @@ -0,0 +1,31 @@ +package prom + +import ( + "errors" + "net/http" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" +) + +var errPromHTTP = errors.New("can't start http server for prometheus") + +// Serve start listening http +func Serve(addr string) *http.Server { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + srv := http.Server{ + Addr: addr, + Handler: mux, + } + go func() { + if err := srv.ListenAndServe(); err != nil { + logrus. + WithError(err). + WithField("module", "prom"). + WithField("addr", addr). + Fatal(errPromHTTP) + } + }() + return &srv +} From a0d70b544b3637cb5acd4b2e9f1e05d180573197 Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 16:07:29 +0300 Subject: [PATCH 2/8] add prometheus metrics --- cmd/root.go | 26 ++++++ go.mod | 1 + go.sum | 8 ++ pkg/prom/db_stats_collector.go | 143 +++++++++++++++++++++++++++++++++ pkg/prom/prom.go | 27 +++++++ pkg/prom/serve.go | 31 +++++++ pkg/serve/config.go | 2 + 7 files changed, 238 insertions(+) create mode 100644 pkg/prom/db_stats_collector.go create mode 100644 pkg/prom/prom.go create mode 100644 pkg/prom/serve.go diff --git a/cmd/root.go b/cmd/root.go index fdcd87d2..2e8c86b7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -24,6 +24,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/vulcanize/ipld-eth-server/pkg/prom" ) var ( @@ -62,6 +63,19 @@ func initFuncs(cmd *cobra.Command, args []string) { if err := logLevel(); err != nil { log.Fatal("Could not set log level: ", err) } + + if viper.GetBool("metrics") { + prom.Init() + } + + if viper.GetBool("http") { + addr := fmt.Sprintf( + "%s:%s", + viper.GetString("http.addr"), + viper.GetString("http.port"), + ) + prom.Serve(addr) + } } func logLevel() error { @@ -93,6 +107,12 @@ func init() { rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file") rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic") + rootCmd.PersistentFlags().Bool("metrics", false, "enable metrics") + + rootCmd.PersistentFlags().Bool("http", false, "enable http service for prometheus") + rootCmd.PersistentFlags().String("http-addr", "127.0.0.1", "http host for prometheus") + rootCmd.PersistentFlags().String("http-port", "8080", "http port for prometheus") + viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile")) viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port")) @@ -100,6 +120,12 @@ func init() { viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user")) viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password")) viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level")) + + viper.BindPFlag("metrics", rootCmd.PersistentFlags().Lookup("metrics")) + + viper.BindPFlag("http", rootCmd.PersistentFlags().Lookup("http")) + viper.BindPFlag("http.addr", rootCmd.PersistentFlags().Lookup("http-addr")) + viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("http-port")) } func initConfig() { diff --git a/go.mod b/go.mod index 028423f3..00b84a37 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/multiformats/go-multihash v0.0.13 github.com/onsi/ginkgo v1.12.1 github.com/onsi/gomega v1.10.1 + github.com/prometheus/client_golang v1.5.1 github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.7.0 diff --git a/go.sum b/go.sum index 054a0e26..a1503247 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -703,6 +704,7 @@ github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuuj github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= @@ -827,21 +829,25 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= @@ -953,6 +959,8 @@ github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5 h1:U+BqhjRLR22e9OEm8cgW github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5/go.mod h1:7oC0Ni6dosMv5pxMigm6s0hN8g4haJMBnqmmo0D9YfQ= github.com/vulcanize/ipld-eth-indexer v0.2.0-alpha h1:+XVaC7TsA0K278YWpfqdrNxwgC6hY6fBaN8w2/e1Lts= github.com/vulcanize/ipld-eth-indexer v0.2.0-alpha/go.mod h1:SuMBscFfcBHYlQuzDDd4by+R0S3gaAFjrOU+uQfAefE= +github.com/vulcanize/ipld-eth-indexer v0.4.0-alpha h1:DXZ/hF65uegvh70YZTcPM6InMUxGkWxee6awyLcrhDg= +github.com/vulcanize/ipld-eth-indexer v0.4.0-alpha/go.mod h1:SuMBscFfcBHYlQuzDDd4by+R0S3gaAFjrOU+uQfAefE= github.com/vulcanize/pg-ipfs-ethdb v0.0.1-alpha h1:Y7j0Hw1jgVVOg+eUGUr7OgH+gOBID0DwbsfZV1KoL7I= github.com/vulcanize/pg-ipfs-ethdb v0.0.1-alpha/go.mod h1:OuqE4r2LGWAtDVx3s1yaAzDcwy+LEAqrWaE1L8UfrGY= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= diff --git a/pkg/prom/db_stats_collector.go b/pkg/prom/db_stats_collector.go new file mode 100644 index 00000000..0b225899 --- /dev/null +++ b/pkg/prom/db_stats_collector.go @@ -0,0 +1,143 @@ +package prom + +import ( + "database/sql" + + "github.com/prometheus/client_golang/prometheus" +) + +const subsystem = "connections" + +// DBStatsGetter is an interface that gets sql.DBStats. +type DBStatsGetter interface { + Stats() sql.DBStats +} + +// DBStatsCollector implements the prometheus.Collector interface. +type DBStatsCollector struct { + sg DBStatsGetter + + // descriptions of exported metrics + maxOpenDesc *prometheus.Desc + openDesc *prometheus.Desc + inUseDesc *prometheus.Desc + idleDesc *prometheus.Desc + waitedForDesc *prometheus.Desc + blockedSecondsDesc *prometheus.Desc + closedMaxIdleDesc *prometheus.Desc + closedMaxLifetimeDesc *prometheus.Desc +} + +// NewDBStatsCollector creates a new DBStatsCollector. +func NewDBStatsCollector(dbName string, sg DBStatsGetter) *DBStatsCollector { + labels := prometheus.Labels{"db_name": dbName} + return &DBStatsCollector{ + sg: sg, + maxOpenDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "max_open"), + "Maximum number of open connections to the database.", + nil, + labels, + ), + openDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "open"), + "The number of established connections both in use and idle.", + nil, + labels, + ), + inUseDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "in_use"), + "The number of connections currently in use.", + nil, + labels, + ), + idleDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "idle"), + "The number of idle connections.", + nil, + labels, + ), + waitedForDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "waited_for"), + "The total number of connections waited for.", + nil, + labels, + ), + blockedSecondsDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "blocked_seconds"), + "The total time blocked waiting for a new connection.", + nil, + labels, + ), + closedMaxIdleDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "closed_max_idle"), + "The total number of connections closed due to SetMaxIdleConns.", + nil, + labels, + ), + closedMaxLifetimeDesc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, "closed_max_lifetime"), + "The total number of connections closed due to SetConnMaxLifetime.", + nil, + labels, + ), + } +} + +// Describe implements the prometheus.Collector interface. +func (c DBStatsCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.maxOpenDesc + ch <- c.openDesc + ch <- c.inUseDesc + ch <- c.idleDesc + ch <- c.waitedForDesc + ch <- c.blockedSecondsDesc + ch <- c.closedMaxIdleDesc + ch <- c.closedMaxLifetimeDesc +} + +// Collect implements the prometheus.Collector interface. +func (c DBStatsCollector) Collect(ch chan<- prometheus.Metric) { + stats := c.sg.Stats() + + ch <- prometheus.MustNewConstMetric( + c.maxOpenDesc, + prometheus.GaugeValue, + float64(stats.MaxOpenConnections), + ) + ch <- prometheus.MustNewConstMetric( + c.openDesc, + prometheus.GaugeValue, + float64(stats.OpenConnections), + ) + ch <- prometheus.MustNewConstMetric( + c.inUseDesc, + prometheus.GaugeValue, + float64(stats.InUse), + ) + ch <- prometheus.MustNewConstMetric( + c.idleDesc, + prometheus.GaugeValue, + float64(stats.Idle), + ) + ch <- prometheus.MustNewConstMetric( + c.waitedForDesc, + prometheus.CounterValue, + float64(stats.WaitCount), + ) + ch <- prometheus.MustNewConstMetric( + c.blockedSecondsDesc, + prometheus.CounterValue, + stats.WaitDuration.Seconds(), + ) + ch <- prometheus.MustNewConstMetric( + c.closedMaxIdleDesc, + prometheus.CounterValue, + float64(stats.MaxIdleClosed), + ) + ch <- prometheus.MustNewConstMetric( + c.closedMaxLifetimeDesc, + prometheus.CounterValue, + float64(stats.MaxLifetimeClosed), + ) +} diff --git a/pkg/prom/prom.go b/pkg/prom/prom.go new file mode 100644 index 00000000..ebf95c86 --- /dev/null +++ b/pkg/prom/prom.go @@ -0,0 +1,27 @@ +package prom + +import ( + "github.com/jmoiron/sqlx" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + namespace = "ipld_eth_server" + statsSubsystem = "stats" +) + +var ( + metrics bool +) + +// Init module initialization +func Init() { + metrics = true +} + +// RegisterDBCollector create metric colletor for given connection +func RegisterDBCollector(name string, db *sqlx.DB) { + if metrics { + prometheus.Register(NewDBStatsCollector(name, db)) + } +} diff --git a/pkg/prom/serve.go b/pkg/prom/serve.go new file mode 100644 index 00000000..058d1772 --- /dev/null +++ b/pkg/prom/serve.go @@ -0,0 +1,31 @@ +package prom + +import ( + "errors" + "net/http" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" +) + +var errPromHTTP = errors.New("can't start http server for prometheus") + +// Serve start listening http +func Serve(addr string) *http.Server { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + srv := http.Server{ + Addr: addr, + Handler: mux, + } + go func() { + if err := srv.ListenAndServe(); err != nil { + logrus. + WithError(err). + WithField("module", "prom"). + WithField("addr", addr). + Fatal(errPromHTTP) + } + }() + return &srv +} diff --git a/pkg/serve/config.go b/pkg/serve/config.go index 821ee8dc..215b7d95 100644 --- a/pkg/serve/config.go +++ b/pkg/serve/config.go @@ -21,6 +21,7 @@ import ( "path/filepath" "github.com/vulcanize/ipld-eth-indexer/pkg/node" + "github.com/vulcanize/ipld-eth-server/pkg/prom" "github.com/spf13/viper" @@ -81,6 +82,7 @@ func NewConfig() (*Config, error) { c.HTTPEndpoint = httpPath overrideDBConnConfig(&c.DBConfig) serveDB := utils.LoadPostgres(c.DBConfig, node.Info{}) + prom.RegisterDBCollector(c.DBConfig.Name, serveDB.DB) c.DB = &serveDB return c, nil From b1f00c57762d7f20afc893e19217c1ff86035e4f Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 16:20:05 +0300 Subject: [PATCH 3/8] fix merge error --- cmd/root.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 64a4d61b..2e8c86b7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -109,15 +109,9 @@ func init() { rootCmd.PersistentFlags().Bool("metrics", false, "enable metrics") -<<<<<<< HEAD rootCmd.PersistentFlags().Bool("http", false, "enable http service for prometheus") rootCmd.PersistentFlags().String("http-addr", "127.0.0.1", "http host for prometheus") rootCmd.PersistentFlags().String("http-port", "8080", "http port for prometheus") -======= - rootCmd.PersistentFlags().Bool("http", false, "enable http service") - rootCmd.PersistentFlags().String("http-addr", "127.0.0.1", "http host") - rootCmd.PersistentFlags().String("http-port", "8080", "http port") ->>>>>>> efebea7a186d6b78f7808210545f4b3565796934 viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile")) viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) From 1043df91564c8714febcc420cb42425128164cfb Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 16:41:39 +0300 Subject: [PATCH 4/8] rollback environments/example.toml --- environments/example.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environments/example.toml b/environments/example.toml index 632b25c5..67d319b2 100644 --- a/environments/example.toml +++ b/environments/example.toml @@ -1,5 +1,5 @@ [database] - name = "vulcanize_public1" # $DATABASE_NAME + name = "vulcanize_public" # $DATABASE_NAME hostname = "localhost" # $DATABASE_HOSTNAME port = 5432 # $DATABASE_PORT user = "postgres" # $DATABASE_USER From f627c2edfaff8c2ac3d5df5716d713108aaa8727 Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 18:00:55 +0300 Subject: [PATCH 5/8] add prometheus-middlewares for http and ws endpoint --- cmd/serve.go | 9 ++++--- pkg/prom/middleware.go | 35 +++++++++++++++++++++++++ pkg/prom/prom.go | 56 ++++++++++++++++++++++++++++++++++++++-- pkg/rpc/check.go | 21 +++++++++++++++ pkg/rpc/http.go | 41 +++++++++++++++++++++++++++++ pkg/rpc/ipc.go | 58 ++++++++++++++++++++++++++++++++++++++++++ pkg/rpc/ws.go | 46 +++++++++++++++++++++++++++++++++ 7 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 pkg/prom/middleware.go create mode 100644 pkg/rpc/check.go create mode 100644 pkg/rpc/http.go create mode 100644 pkg/rpc/ipc.go create mode 100644 pkg/rpc/ws.go diff --git a/cmd/serve.go b/cmd/serve.go index 03508b65..0e1aeb52 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -27,6 +27,8 @@ import ( "github.com/vulcanize/ipld-eth-indexer/pkg/eth" + srpc "github.com/vulcanize/ipld-eth-server/pkg/rpc" + s "github.com/vulcanize/ipld-eth-server/pkg/serve" v "github.com/vulcanize/ipld-eth-server/version" ) @@ -78,17 +80,18 @@ func serve() { func startServers(server s.Server, settings *s.Config) error { logWithCommand.Info("starting up IPC server") - _, _, err := rpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs()) + _, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs()) if err != nil { return err } logWithCommand.Info("starting up WS server") - _, _, err = rpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb"}, nil, true) + _, _, err = srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb"}, nil, true) if err != nil { return err } logWithCommand.Info("starting up HTTP server") - _, _, err = rpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth"}, nil, []string{"*"}, rpc.HTTPTimeouts{}) + _, _, err = srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth"}, nil, []string{"*"}, rpc.HTTPTimeouts{}) + return err } diff --git a/pkg/prom/middleware.go b/pkg/prom/middleware.go new file mode 100644 index 00000000..741fed9a --- /dev/null +++ b/pkg/prom/middleware.go @@ -0,0 +1,35 @@ +package prom + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" +) + +func HTTPMiddleware(next http.Handler) http.Handler { + if !metrics { + return next + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + httpCount.Inc() + + timer := prometheus.NewTimer(httpDuration) + next.ServeHTTP(w, r) + timer.ObserveDuration() + }) +} + +func WSMiddleware(next http.Handler) http.Handler { + if !metrics { + return next + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wsCount.Inc() + + timer := prometheus.NewTimer(wsDuration) + next.ServeHTTP(w, r) + timer.ObserveDuration() + }) +} diff --git a/pkg/prom/prom.go b/pkg/prom/prom.go index ebf95c86..828ed160 100644 --- a/pkg/prom/prom.go +++ b/pkg/prom/prom.go @@ -3,20 +3,72 @@ package prom import ( "github.com/jmoiron/sqlx" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ) const ( - namespace = "ipld_eth_server" - statsSubsystem = "stats" + namespace = "ipld_eth_server" + + subsystemHTTP = "http" + subsystemWS = "ws" + subsystemIPC = "ipc" ) var ( metrics bool + + httpCount prometheus.Counter + httpDuration prometheus.Histogram + + wsCount prometheus.Counter + wsDuration prometheus.Histogram + + ipcCount prometheus.Counter + ipcDuration prometheus.Gauge ) // Init module initialization func Init() { metrics = true + + httpCount = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: subsystemHTTP, + Name: "count", + Help: "", + }) + httpDuration = promauto.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystemHTTP, + Name: "duration", + Help: "", + }) + + wsCount = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: subsystemWS, + Name: "count", + Help: "", + }) + wsDuration = promauto.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystemWS, + Name: "duration", + Help: "", + }) + + ipcCount = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: subsystemIPC, + Name: "count", + Help: "", + }) + ipcDuration = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Subsystem: subsystemIPC, + Name: "duration", + Help: "", + }) } // RegisterDBCollector create metric colletor for given connection diff --git a/pkg/rpc/check.go b/pkg/rpc/check.go new file mode 100644 index 00000000..754f752e --- /dev/null +++ b/pkg/rpc/check.go @@ -0,0 +1,21 @@ +package rpc + +import "github.com/ethereum/go-ethereum/rpc" + +// checkModuleAvailability check that all names given in modules are actually +// available API services. +func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available []string) { + availableSet := make(map[string]struct{}) + for _, api := range apis { + if _, ok := availableSet[api.Namespace]; !ok { + availableSet[api.Namespace] = struct{}{} + available = append(available, api.Namespace) + } + } + for _, name := range modules { + if _, ok := availableSet[name]; !ok { + bad = append(bad, name) + } + } + return bad, available +} diff --git a/pkg/rpc/http.go b/pkg/rpc/http.go new file mode 100644 index 00000000..ae1ab03e --- /dev/null +++ b/pkg/rpc/http.go @@ -0,0 +1,41 @@ +package rpc + +import ( + "net" + + "github.com/ethereum/go-ethereum/rpc" + log "github.com/sirupsen/logrus" + "github.com/vulcanize/ipld-eth-server/pkg/prom" +) + +// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules. +func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) (net.Listener, *rpc.Server, error) { + if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { + log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available) + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("HTTP registered", "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + go rpc.NewHTTPServer(cors, vhosts, timeouts, prom.HTTPMiddleware(handler)).Serve(listener) + return listener, handler, err +} diff --git a/pkg/rpc/ipc.go b/pkg/rpc/ipc.go new file mode 100644 index 00000000..297be5ab --- /dev/null +++ b/pkg/rpc/ipc.go @@ -0,0 +1,58 @@ +package rpc + +import ( + "C" + + "fmt" + "net" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/rpc" + log "github.com/sirupsen/logrus" +) + +var ( + // On Linux, sun_path is 108 bytes in size + // see http://man7.org/linux/man-pages/man7/unix.7.html + maxPathSize = 108 +) + +// ipcListen will create a Unix socket on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + if len(endpoint) > int(maxPathSize) { + log.Warn(fmt.Sprintf("The ipc endpoint is longer than %d characters. ", maxPathSize), + "endpoint", endpoint) + } + + // Ensure the IPC path exists and remove any previous leftover + if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil { + return nil, err + } + os.Remove(endpoint) + l, err := net.Listen("unix", endpoint) + if err != nil { + return nil, err + } + os.Chmod(endpoint, 0600) + return l, nil +} + +// StartIPCEndpoint starts an IPC endpoint. +func StartIPCEndpoint(ipcEndpoint string, apis []rpc.API) (net.Listener, *rpc.Server, error) { + // Register all the APIs exposed by the services. + handler := rpc.NewServer() + for _, api := range apis { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("IPC registered", "namespace", api.Namespace) + } + // All APIs registered, start the IPC listener. + listener, err := ipcListen(ipcEndpoint) + if err != nil { + return nil, nil, err + } + go handler.ServeListener(listener) + return listener, handler, nil +} diff --git a/pkg/rpc/ws.go b/pkg/rpc/ws.go new file mode 100644 index 00000000..cff19ab9 --- /dev/null +++ b/pkg/rpc/ws.go @@ -0,0 +1,46 @@ +package rpc + +import ( + "net" + + "github.com/ethereum/go-ethereum/rpc" + log "github.com/sirupsen/logrus" + "github.com/vulcanize/ipld-eth-server/pkg/prom" +) + +// StartWSEndpoint starts a websocket endpoint. +func StartWSEndpoint(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) (net.Listener, *rpc.Server, error) { + if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 { + log.Error("Unavailable modules in WS API list", "unavailable", bad, "available", available) + } + // Generate the whitelist based on the allowed modules + whitelist := make(map[string]bool) + for _, module := range modules { + whitelist[module] = true + } + // Register all the APIs exposed by the services + handler := rpc.NewServer() + for _, api := range apis { + if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + return nil, nil, err + } + log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace) + } + } + // All APIs registered, start the HTTP listener + var ( + listener net.Listener + err error + ) + if listener, err = net.Listen("tcp", endpoint); err != nil { + return nil, nil, err + } + + wsServer := rpc.NewWSServer(wsOrigins, handler) + wsServer.Handler = prom.WSMiddleware(wsServer.Handler) + go wsServer.Serve(listener) + + return listener, handler, err + +} From 4c18554fbe945afd5844ab542c5034f09c251157 Mon Sep 17 00:00:00 2001 From: Ilnur Galiev Date: Mon, 19 Oct 2020 23:00:09 +0300 Subject: [PATCH 6/8] add websocket and unixsocket counters --- pkg/prom/middleware.go | 25 +++++++++++++++++++------ pkg/prom/prom.go | 32 ++++++++------------------------ pkg/rpc/ipc.go | 23 ++++++++++++++++++++--- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/pkg/prom/middleware.go b/pkg/prom/middleware.go index 741fed9a..b2daa79f 100644 --- a/pkg/prom/middleware.go +++ b/pkg/prom/middleware.go @@ -2,10 +2,12 @@ package prom import ( "net/http" + "time" - "github.com/prometheus/client_golang/prometheus" + "github.com/ethereum/go-ethereum/rpc" ) +// HTTPMiddleware http connection metric reader func HTTPMiddleware(next http.Handler) http.Handler { if !metrics { return next @@ -14,12 +16,14 @@ func HTTPMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { httpCount.Inc() - timer := prometheus.NewTimer(httpDuration) + start := time.Now() next.ServeHTTP(w, r) - timer.ObserveDuration() + duration := time.Now().Sub(start) + httpDuration.Observe(float64(duration.Seconds())) }) } +// WSMiddleware websocket connection counter func WSMiddleware(next http.Handler) http.Handler { if !metrics { return next @@ -27,9 +31,18 @@ func WSMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wsCount.Inc() - - timer := prometheus.NewTimer(wsDuration) next.ServeHTTP(w, r) - timer.ObserveDuration() + wsCount.Dec() }) } + +// IPCMiddleware unix-socket connection counter +func IPCMiddleware(server *rpc.Server, client rpc.Conn) { + if metrics { + ipcCount.Inc() + } + server.ServeCodec(rpc.NewCodec(client), 0) + if metrics { + ipcCount.Dec() + } +} diff --git a/pkg/prom/prom.go b/pkg/prom/prom.go index 828ed160..f17ef546 100644 --- a/pkg/prom/prom.go +++ b/pkg/prom/prom.go @@ -19,12 +19,8 @@ var ( httpCount prometheus.Counter httpDuration prometheus.Histogram - - wsCount prometheus.Counter - wsDuration prometheus.Histogram - - ipcCount prometheus.Counter - ipcDuration prometheus.Gauge + wsCount prometheus.Gauge + ipcCount prometheus.Gauge ) // Init module initialization @@ -35,39 +31,27 @@ func Init() { Namespace: namespace, Subsystem: subsystemHTTP, Name: "count", - Help: "", + Help: "http request count", }) httpDuration = promauto.NewHistogram(prometheus.HistogramOpts{ Namespace: namespace, Subsystem: subsystemHTTP, Name: "duration", - Help: "", + Help: "http request duration", }) - wsCount = promauto.NewCounter(prometheus.CounterOpts{ + wsCount = promauto.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystemWS, Name: "count", - Help: "", - }) - wsDuration = promauto.NewHistogram(prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: subsystemWS, - Name: "duration", - Help: "", + Help: "websocket conntection count", }) - ipcCount = promauto.NewCounter(prometheus.CounterOpts{ + ipcCount = promauto.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystemIPC, Name: "count", - Help: "", - }) - ipcDuration = promauto.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: subsystemIPC, - Name: "duration", - Help: "", + Help: "unix socket connection count", }) } diff --git a/pkg/rpc/ipc.go b/pkg/rpc/ipc.go index 297be5ab..4e170ebe 100644 --- a/pkg/rpc/ipc.go +++ b/pkg/rpc/ipc.go @@ -1,15 +1,15 @@ package rpc import ( - "C" - "fmt" "net" "os" "path/filepath" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/rpc" log "github.com/sirupsen/logrus" + "github.com/vulcanize/ipld-eth-server/pkg/prom" ) var ( @@ -38,6 +38,22 @@ func ipcListen(endpoint string) (net.Listener, error) { return l, nil } +func ipcServe(srv *rpc.Server, listener net.Listener) { + for { + conn, err := listener.Accept() + if netutil.IsTemporaryError(err) { + log.WithError(err).Warn("rpc accept error") + continue + } + if err != nil { + log.WithError(err).Warn("unknown error") + continue + } + log.WithField("addr", conn.RemoteAddr()).Trace("accepted ipc connection") + go prom.IPCMiddleware(srv, conn) + } +} + // StartIPCEndpoint starts an IPC endpoint. func StartIPCEndpoint(ipcEndpoint string, apis []rpc.API) (net.Listener, *rpc.Server, error) { // Register all the APIs exposed by the services. @@ -53,6 +69,7 @@ func StartIPCEndpoint(ipcEndpoint string, apis []rpc.API) (net.Listener, *rpc.Se if err != nil { return nil, nil, err } - go handler.ServeListener(listener) + + go ipcServe(handler, listener) return listener, handler, nil } From d9b05233b06116593977221ae708802eaf262f30 Mon Sep 17 00:00:00 2001 From: ramil Date: Wed, 21 Oct 2020 14:22:43 +0300 Subject: [PATCH 7/8] fix null pointer exception --- cmd/serve.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/serve.go b/cmd/serve.go index 8873899a..502f2918 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -112,7 +112,7 @@ func init() { viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("server-http-path")) viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("server-ipc-path")) - viper.BindPFlag("ethereum.chainID", rootCmd.PersistentFlags().Lookup("eth-chain-id")) - viper.BindPFlag("ethereum.defaultSender", rootCmd.PersistentFlags().Lookup("eth-default-sender")) - viper.BindPFlag("ethereum.rpcGasCap", rootCmd.PersistentFlags().Lookup("eth-rpc-gas-cap")) + viper.BindPFlag("ethereum.chainID", serveCmd.PersistentFlags().Lookup("eth-chain-id")) + viper.BindPFlag("ethereum.defaultSender", serveCmd.PersistentFlags().Lookup("eth-default-sender")) + viper.BindPFlag("ethereum.rpcGasCap", serveCmd.PersistentFlags().Lookup("eth-rpc-gas-cap")) } From 909c85b547c35731bfbd0cbe0ba43396e6b4d5eb Mon Sep 17 00:00:00 2001 From: ramil Date: Wed, 21 Oct 2020 15:38:48 +0300 Subject: [PATCH 8/8] monitoring documentation, prometheus config, grafana dashboard --- README.md | 10 + cmd/root.go | 2 +- monitoring/grafana.png | Bin 0 -> 120282 bytes monitoring/grafana/dashboard_main.json | 353 +++++++++++++++++++++++++ monitoring/prometheus.yml | 7 + pkg/prom/prom.go | 2 +- 6 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 monitoring/grafana.png create mode 100644 monitoring/grafana/dashboard_main.json create mode 100644 monitoring/prometheus.yml diff --git a/README.md b/README.md index b13f9172..ed6f7f36 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,16 @@ TODO: Add the rest of the standard endpoints add unique endpoints (e.g. getSlice `make test` will run the unit tests `make test` setups a clean `vulcanize_testing` db +## Monitoring + +* Enable http server and metrics using parameters `--http --metrics` +* ipld-eth-server exposes prometheus metrics at `/metric` endpoint +* start prometheus using `monitoring/prometheus.yml` config (`prometheus --config.file=monitoring/prometheus.yml`) +* start grafana, connect to prometheus datasource and import dashboard from `monitoring/grafana/dashboard_main.json` + +![](monitoring/grafana.png) + + ## Contributing Contributions are welcome! diff --git a/cmd/root.go b/cmd/root.go index 2e8c86b7..00fba60e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -111,7 +111,7 @@ func init() { rootCmd.PersistentFlags().Bool("http", false, "enable http service for prometheus") rootCmd.PersistentFlags().String("http-addr", "127.0.0.1", "http host for prometheus") - rootCmd.PersistentFlags().String("http-port", "8080", "http port for prometheus") + rootCmd.PersistentFlags().String("http-port", "8090", "http port for prometheus") viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile")) viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name")) diff --git a/monitoring/grafana.png b/monitoring/grafana.png new file mode 100644 index 0000000000000000000000000000000000000000..3d1220e12df4aa89ca126c54b7ad2f5582359c89 GIT binary patch literal 120282 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

@}w&9Bp}VufDMB%Gk<5QKJS3C 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 z�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 literal 0 HcmV?d00001 diff --git a/monitoring/grafana/dashboard_main.json b/monitoring/grafana/dashboard_main.json new file mode 100644 index 00000000..83ed1a0b --- /dev/null +++ b/monitoring/grafana/dashboard_main.json @@ -0,0 +1,353 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 4, + "links": [], + "panels": [ + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "ipld_eth_server_ws_count", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Websocket Connection Count", + "type": "gauge" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 5, + "options": { + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.2.1", + "targets": [ + { + "expr": "ipld_eth_server_ipc_count", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "IPC Connection Count", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(ipld_eth_server_http_count[1m])", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP requests per second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {}, + "unit": "s" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9, rate(ipld_eth_server_http_duration_bucket[1m]))", + "hide": false, + "interval": "", + "legendFormat": "0.9", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.75, rate(ipld_eth_server_http_duration_bucket[1m]))", + "interval": "", + "legendFormat": "0.75", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.5, rate(ipld_eth_server_http_duration_bucket[1m]))", + "interval": "", + "legendFormat": "0.5", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Requests duration (percentile)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "ipld-eth-server", + "uid": "lFVEvNtGk", + "version": 7 +} diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml new file mode 100644 index 00000000..79b4d620 --- /dev/null +++ b/monitoring/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 10s + +scrape_configs: + - job_name: 'ipld-eth-server' + static_configs: + - targets: ['localhost:8090'] diff --git a/pkg/prom/prom.go b/pkg/prom/prom.go index f17ef546..72a282ce 100644 --- a/pkg/prom/prom.go +++ b/pkg/prom/prom.go @@ -44,7 +44,7 @@ func Init() { Namespace: namespace, Subsystem: subsystemWS, Name: "count", - Help: "websocket conntection count", + Help: "websocket connection count", }) ipcCount = promauto.NewGauge(prometheus.GaugeOpts{