diff --git a/config.md b/config.md index afb12c8c..a622fe82 100644 --- a/config.md +++ b/config.md @@ -73,6 +73,12 @@ nav_order: 2 |methods| CORS setting to control the allowed methods|`string`|`[GET POST PUT PATCH DELETE]` |origins|CORS setting to control the allowed origins|`string`|`[*]` +## debug + +|Key|Description|Type|Default Value| +|---|-----------|----|-------------| +|port|An HTTP port on which to enable the go debugger|`int`|`-1` + ## eventstreams |Key|Description|Type|Default Value| diff --git a/internal/tmconfig/tmconfig.go b/internal/tmconfig/tmconfig.go index 746df23f..7a524cb4 100644 --- a/internal/tmconfig/tmconfig.go +++ b/internal/tmconfig/tmconfig.go @@ -56,6 +56,7 @@ var ( PersistenceLevelDBSyncWrites = ffc("persistence.leveldb.syncWrites") APIDefaultRequestTimeout = ffc("api.defaultRequestTimeout") APIMaxRequestTimeout = ffc("api.maxRequestTimeout") + DebugPort = ffc("debug.port") ) var APIConfig config.Section @@ -100,6 +101,7 @@ func setDefaults() { viper.SetDefault(string(EventStreamsRetryInitDelay), "250ms") viper.SetDefault(string(EventStreamsRetryMaxDelay), "30s") viper.SetDefault(string(EventStreamsRetryFactor), 2.0) + viper.SetDefault(string(DebugPort), -1) } func Reset() { diff --git a/internal/tmmsgs/en_config_descriptions.go b/internal/tmmsgs/en_config_descriptions.go index b5322076..13e1aec1 100644 --- a/internal/tmmsgs/en_config_descriptions.go +++ b/internal/tmmsgs/en_config_descriptions.go @@ -36,6 +36,8 @@ var ( ConfigAPIWriteTimeout = ffc("config.api.writeTimeout", "The maximum time to wait when writing to a HTTP connection", i18n.TimeDurationType) ConfigAPIShutdownTimeout = ffc("config.api.shutdownTimeout", "The maximum amount of time to wait for any open HTTP requests to finish before shutting down the HTTP server", i18n.TimeDurationType) + ConfigDebugPort = ffc("config.debug.port", "An HTTP port on which to enable the go debugger", i18n.IntType) + ConfigConfirmationsBlockCacheSize = ffc("config.confirmations.blockCacheSize", "The maximum number of block headers to keep in the cache", i18n.IntType) ConfigConfirmationsBlockQueueLength = ffc("config.confirmations.blockQueueLength", "Internal queue length for notifying the confirmations manager of new blocks", i18n.IntType) ConfigConfirmationsNotificationsQueueLength = ffc("config.confirmations.notificationQueueLength", "Internal queue length for notifying the confirmations manager of new transactions/events", i18n.IntType) diff --git a/pkg/fftm/api.go b/pkg/fftm/api.go index 7c453ffd..fe2f8ff8 100644 --- a/pkg/fftm/api.go +++ b/pkg/fftm/api.go @@ -18,13 +18,17 @@ package fftm import ( "encoding/json" + "fmt" "net/http" + "net/http/pprof" + "time" "github.com/ghodss/yaml" "github.com/gorilla/mux" "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/ffapi" "github.com/hyperledger/firefly-common/pkg/i18n" + "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly-transaction-manager/internal/tmconfig" ) @@ -77,3 +81,27 @@ func (m *manager) router() *mux.Router { func (m *manager) runAPIServer() { m.apiServer.ServeHTTP(m.ctx) } + +func (m *manager) runDebugServer() { + var debugServer *http.Server + debugPort := config.GetInt(tmconfig.DebugPort) + + defer func() { + if debugServer != nil { + _ = debugServer.Close() + } + close(m.debugServerDone) + }() + + if debugPort >= 0 { + r := mux.NewRouter() + r.PathPrefix("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline) + r.PathPrefix("/debug/pprof/profile").HandlerFunc(pprof.Profile) + r.PathPrefix("/debug/pprof/symbol").HandlerFunc(pprof.Symbol) + r.PathPrefix("/debug/pprof/trace").HandlerFunc(pprof.Trace) + r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) + debugServer = &http.Server{Addr: fmt.Sprintf("localhost:%d", debugPort), Handler: r, ReadHeaderTimeout: 30 * time.Second} + log.L(m.ctx).Debugf("Debug HTTP endpoint listening on localhost:%d", debugPort) + _ = debugServer.ListenAndServe() + } +} diff --git a/pkg/fftm/manager.go b/pkg/fftm/manager.go index c06438de..00cc98f9 100644 --- a/pkg/fftm/manager.go +++ b/pkg/fftm/manager.go @@ -87,6 +87,7 @@ type manager struct { blockListenerDone chan struct{} started bool apiServerDone chan error + debugServerDone chan struct{} policyLoopInterval time.Duration nonceStateTimeout time.Duration @@ -182,6 +183,8 @@ func (m *manager) Start() error { return err } + m.debugServerDone = make(chan struct{}) + go m.runDebugServer() go m.runAPIServer() m.policyLoopDone = make(chan struct{}) m.markInflightStale() @@ -199,6 +202,7 @@ func (m *manager) Close() { <-m.apiServerDone <-m.policyLoopDone <-m.blockListenerDone + <-m.debugServerDone streams := []events.Stream{} m.mux.Lock()