diff --git a/config.go b/config.go index 09077620..3cce0d55 100644 --- a/config.go +++ b/config.go @@ -313,6 +313,8 @@ type config struct { Workers *lncfg.Workers `group:"workers" namespace:"workers"` Caches *lncfg.Caches `group:"caches" namespace:"caches"` + + Prometheus lncfg.Prometheus `group:"prometheus" namespace:"prometheus"` } // loadConfig initializes and parses the config using a config file and command @@ -407,6 +409,7 @@ func loadConfig() (*config, error) { RejectCacheSize: channeldb.DefaultRejectCacheSize, ChannelCacheSize: channeldb.DefaultChannelCacheSize, }, + Prometheus: lncfg.DefaultPrometheus(), } // Pre-parse the command line options to pick up an alternative config diff --git a/lncfg/monitoring_off.go b/lncfg/monitoring_off.go new file mode 100644 index 00000000..5ee21a12 --- /dev/null +++ b/lncfg/monitoring_off.go @@ -0,0 +1,19 @@ +// +build !monitoring + +package lncfg + +// Prometheus configures the Prometheus exporter when monitoring is enabled. +// Monitoring is currently disabled. +type Prometheus struct{} + +// DefaultPrometheus is the default configuration for the Prometheus metrics +// exporter when monitoring is enabled. Monitoring is currently disabled. +func DefaultPrometheus() Prometheus { + return Prometheus{} +} + +// Enabled returns whether or not Prometheus monitoring is enabled. Monitoring +// is currently disabled, so Enabled will always return false. +func (p *Prometheus) Enabled() bool { + return false +} diff --git a/lncfg/monitoring_on.go b/lncfg/monitoring_on.go new file mode 100644 index 00000000..dc31e51d --- /dev/null +++ b/lncfg/monitoring_on.go @@ -0,0 +1,30 @@ +// +build monitoring + +package lncfg + +// Prometheus is the set of configuration data that specifies the listening +// address of the Prometheus exporter. +type Prometheus struct { + // Listen is the listening address that we should use to allow the main + // Prometheus server to scrape our metrics. + Listen string `long:"listen" description:"the interface we should listen on for Prometheus"` + + // Enable indicates whether to export lnd gRPC performance metrics to + // Prometheus. Default is false. + Enable bool `long:"enable" description:"enable Prometheus exporting of lnd gRPC performance metrics."` +} + +// DefaultPrometheus is the default configuration for the Prometheus metrics +// exporter. +func DefaultPrometheus() Prometheus { + return Prometheus{ + Listen: "127.0.0.1:8989", + Enable: false, + } +} + +// Enabled returns whether or not Prometheus monitoring is enabled. Monitoring +// is disabled by default, but may be enabled by the user. +func (p *Prometheus) Enabled() bool { + return p.Enable +} diff --git a/log.go b/log.go index 8ddb7f6d..c84fd7b2 100644 --- a/log.go +++ b/log.go @@ -28,6 +28,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/monitoring" "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/signal" @@ -85,6 +86,7 @@ var ( irpcLog = build.NewSubLogger("IRPC", backendLog.Logger) chnfLog = build.NewSubLogger("CHNF", backendLog.Logger) chbuLog = build.NewSubLogger("CHBU", backendLog.Logger) + promLog = build.NewSubLogger("PROM", backendLog.Logger) ) // Initialize package-global logger variables. @@ -112,6 +114,7 @@ func init() { invoicesrpc.UseLogger(irpcLog) channelnotifier.UseLogger(chnfLog) chanbackup.UseLogger(chbuLog) + monitoring.UseLogger(promLog) addSubLogger(routerrpc.Subsystem, routerrpc.UseLogger) } @@ -155,6 +158,7 @@ var subsystemLoggers = map[string]btclog.Logger{ "IRPC": irpcLog, "CHNF": chnfLog, "CHBU": chbuLog, + "PROM": promLog, } // initLogRotator initializes the logging rotator to write logs to logFile and diff --git a/rpcserver.go b/rpcserver.go index dca0a93a..191b51ef 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -28,6 +28,7 @@ import ( "github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/coreos/bbolt" "github.com/davecgh/go-spew/spew" + "github.com/grpc-ecosystem/go-grpc-middleware" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/build" @@ -45,6 +46,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" + "github.com/lightningnetwork/lnd/monitoring" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/sweep" @@ -531,19 +533,36 @@ func newRPCServer(s *server, macService *macaroons.Service, } // If macaroons aren't disabled (a non-nil service), then we'll set up - // our set of interceptors which will allow us handle the macaroon - // authentication in a single location . + // our set of interceptors which will allow us to handle the macaroon + // authentication in a single location. + macUnaryInterceptors := []grpc.UnaryServerInterceptor{} + macStrmInterceptors := []grpc.StreamServerInterceptor{} if macService != nil { - unaryInterceptor := grpc.UnaryInterceptor( - macService.UnaryServerInterceptor(permissions), - ) - streamInterceptor := grpc.StreamInterceptor( - macService.StreamServerInterceptor(permissions), - ) + unaryInterceptor := macService.UnaryServerInterceptor(permissions) + macUnaryInterceptors = append(macUnaryInterceptors, unaryInterceptor) - serverOpts = append(serverOpts, - unaryInterceptor, streamInterceptor, + strmInterceptor := macService.StreamServerInterceptor(permissions) + macStrmInterceptors = append(macStrmInterceptors, strmInterceptor) + } + + // Get interceptors for Prometheus to gather gRPC performance metrics. + // If monitoring is disabled, GetPromInterceptors() will return empty + // slices. + promUnaryInterceptors, promStrmInterceptors := monitoring.GetPromInterceptors() + + // Concatenate the slices of unary and stream interceptors respectively. + unaryInterceptors := append(macUnaryInterceptors, promUnaryInterceptors...) + strmInterceptors := append(macStrmInterceptors, promStrmInterceptors...) + + // If any interceptors have been set up, add them to the server options. + if len(unaryInterceptors) != 0 && len(strmInterceptors) != 0 { + chainedUnary := grpc_middleware.WithUnaryServerChain( + unaryInterceptors..., ) + chainedStream := grpc_middleware.WithStreamServerChain( + strmInterceptors..., + ) + serverOpts = append(serverOpts, chainedUnary, chainedStream) } // Finally, with all the pre-set up complete, we can create the main @@ -616,6 +635,16 @@ func (r *rpcServer) Start() error { }() } + // If Prometheus monitoring is enabled, start the Prometheus exporter. + if cfg.Prometheus.Enabled() { + err := monitoring.ExportPrometheusMetrics( + r.grpcServer, cfg.Prometheus, + ) + if err != nil { + return err + } + } + // Finally, start the REST proxy for our gRPC server above. We'll ensure // we direct LND to connect to its loopback address rather than a // wildcard to prevent certificate issues when accessing the proxy