diff --git a/aperture.go b/aperture.go index 00dad6a..12233d0 100644 --- a/aperture.go +++ b/aperture.go @@ -28,7 +28,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/tor" - "github.com/prometheus/client_golang/prometheus/promhttp" clientv3 "go.etcd.io/etcd/client/v3" "golang.org/x/crypto/acme/autocert" "golang.org/x/net/http2" @@ -181,6 +180,12 @@ func NewAperture(cfg *Config) *Aperture { func (a *Aperture) Start(errChan chan error) error { var err error + // Start the prometheus exporter. + if err := StartPrometheusExporter(a.cfg.Prometheus); err != nil { + return fmt.Errorf("unable to start the prometheus exporter: "+ + "%v", err) + } + // Initialize our etcd client. a.etcdClient, err = clientv3.New(clientv3.Config{ Endpoints: []string{a.cfg.Etcd.Host}, @@ -654,15 +659,6 @@ func createProxy(cfg *Config, challenger *LndChallenger, return nil, nil, err } - // Ensure we spin up the necessary HTTP server to allow - // promtheus to scrape us. - go func() { - http.Handle("/metrics", promhttp.Handler()) - fmt.Println(http.ListenAndServe( - cfg.HashMail.PromListenAddr, nil), - ) - }() - localServices = append(localServices, hashMailServices...) proxyCleanup = cleanup } @@ -714,6 +710,9 @@ func createHashMailServer(cfg *Config) ([]proxy.LocalService, func(), error) { }), ) + // Export the gRPC information for the public gRPC server. + grpc_prometheus.Register(hashMailGRPC) + // And a REST proxy for it as well. // The default JSON marshaler of the REST proxy only sets OrigName to // true, which instructs it to use the same field names as specified in diff --git a/config.go b/config.go index 082dd25..3f0ddff 100644 --- a/config.go +++ b/config.go @@ -64,10 +64,6 @@ type HashMailConfig struct { Enabled bool `long:"enabled"` MessageRate time.Duration `long:"messagerate" description:"The average minimum time that should pass between each message."` MessageBurstAllowance int `long:"messageburstallowance" description:"The burst rate we allow for messages."` - - // PromListenAddr is the listening address that we should use to allow - // the main Prometheus server to scrape our metrics. - PromListenAddr string `long:"promlistenaddr" description:"the interface we should listen on for prometheus"` } type TorConfig struct { @@ -116,6 +112,10 @@ type Config struct { // Node Connect mailbox server. HashMail *HashMailConfig `group:"hashmail" namespace:"hashmail" description:"Configuration for the Lightning Node Connect mailbox server."` + // Prometheus is the config for setting up an endpoint for a Prometheus + // server to scrape metrics from. + Prometheus *PrometheusConfig `group:"prometheus" namespace:"prometheus" description:"Configuration setting up an endpoint that a Prometheus server can scrape."` + // DebugLevel is a string defining the log level for the service either // for all subsystems the same or individual level by subsystem. DebugLevel string `long:"debuglevel" description:"Debug level for the Aperture application and its subsystems."` diff --git a/hashmail_server.go b/hashmail_server.go index b4ac76f..9378726 100644 --- a/hashmail_server.go +++ b/hashmail_server.go @@ -406,6 +406,8 @@ func (h *hashMailServer) InitStream( h.streams[streamID] = freshStream + mailboxCount.Set(float64(len(h.streams))) + return &hashmailrpc.CipherInitResp{ Resp: &hashmailrpc.CipherInitResp_Success{}, }, nil @@ -478,6 +480,8 @@ func (h *hashMailServer) TearDownStream(ctx context.Context, streamID []byte, delete(h.streams, sid) + mailboxCount.Set(float64(len(h.streams))) + return nil } diff --git a/prometheus.go b/prometheus.go new file mode 100644 index 0000000..99b8eff --- /dev/null +++ b/prometheus.go @@ -0,0 +1,54 @@ +package aperture + +import ( + "fmt" + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + // mailboxCount tracks the current number of active mailboxes. + mailboxCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "hashmail", + Name: "mailbox_count", + }) +) + +// PrometheusConfig is the set of configuration data that specifies if +// Prometheus metric exporting is activated, and if so the listening address of +// the Prometheus server. +type PrometheusConfig struct { + // Enabled, if true, then Prometheus metrics will be exported. + Enabled bool `long:"enabled" description:"if true prometheus metrics will be exported"` + + // ListenAddr is the listening address that we should use to allow the + // main Prometheus server to scrape our metrics. + ListenAddr string `long:"listenaddr" description:"the interface we should listen on for prometheus"` +} + +// StartPrometheusExporter registers all relevant metrics with the Prometheus +// library, then launches the HTTP server that Prometheus will hit to scrape +// our metrics. +func StartPrometheusExporter(cfg *PrometheusConfig) error { + // If we're not active, then there's nothing more to do. + if !cfg.Enabled { + return nil + } + + // Next, we'll register all our metrics. + prometheus.MustRegister(mailboxCount) + + // Finally, we'll launch the HTTP server that Prometheus will use to + // scape our metrics. + go func() { + log.Infof("Prometheus metrics http endpoint being served on "+ + "%s", cfg.ListenAddr) + + http.Handle("/metrics", promhttp.Handler()) + fmt.Println(http.ListenAndServe(cfg.ListenAddr, nil)) + }() + + return nil +} diff --git a/sample-conf.yaml b/sample-conf.yaml index 10b9bb6..6493d5a 100644 --- a/sample-conf.yaml +++ b/sample-conf.yaml @@ -151,3 +151,9 @@ hashmail: enabled: true messagerate: 20ms messageburstallowance: 1000 + +# Enable the prometheus metrics exporter so that a prometheus server can scrape +# the metrics. +prometheus: + enabled: true + listenaddr: "localhost:9000"