Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/loop/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ type EnvConfig struct {
MercuryTransmitterReaperMaxAge time.Duration
MercuryVerboseLogging bool

PrometheusPort int
PrometheusPort int //TODO more than just prom

TracingEnabled bool
TracingCollectorTarget string
Expand Down
3 changes: 3 additions & 0 deletions pkg/loop/prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/logger"
)

// Deprecated: Use WebServer which includes pprof routes
type PromServer struct {
port int
srvrDone chan struct{} // closed when the http server is done
Expand All @@ -24,10 +25,12 @@ type PromServer struct {
handler http.Handler
}

// Deprecated: Use WebServerOpts which includes pprof routes
type PromServerOpts struct {
Handler http.Handler
}

// Deprecated: Use NewWebServer which includes pprof routes
func NewPromServer(port int, lggr logger.Logger) *PromServer {
return PromServerOpts{}.New(port, lggr)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/loop/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ type Server struct {
db *sqlx.DB // optional
dbStatsReporter *pg.StatsReporter // optional
DataSource sqlutil.DataSource // optional
promServer *PromServer
webServer *webServer
checker *services.HealthChecker
LimitsFactory limits.Factory
}
Expand Down Expand Up @@ -221,8 +221,8 @@ func (s *Server) start(opts ...ServerOpt) error {
}
}

s.promServer = NewPromServer(s.EnvConfig.PrometheusPort, s.Logger)
if err := s.promServer.Start(); err != nil {
s.webServer = WebServerOpts{}.New(s.Logger, s.EnvConfig.PrometheusPort)
if err := s.webServer.Start(ctx); err != nil {
return fmt.Errorf("error starting prometheus server: %w", err)
}

Expand Down Expand Up @@ -290,7 +290,7 @@ func (s *Server) Stop() {
s.Logger.ErrorIfFn(s.db.Close, "Failed to close database connection")
}
s.Logger.ErrorIfFn(s.checker.Close, "Failed to close health checker")
s.Logger.ErrorIfFn(s.promServer.Close, "Failed to close prometheus server")
s.Logger.ErrorIfFn(s.webServer.Close, "Failed to close web server")
if err := s.Logger.Sync(); err != nil {
fmt.Println("Failed to sync logger:", err)
}
Expand Down
101 changes: 101 additions & 0 deletions pkg/loop/web.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package loop

import (
"context"
"errors"
"net"
"net/http"
_ "net/http/pprof"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
)

// webServer serves web routes including
// - /metrics prometheus metrics
// - /debug/pprof/ debug profiling
type webServer struct {
lggr logger.Logger
port int
handler http.Handler

srvr *http.Server
tcpListener *net.TCPListener

done chan struct{}
}

type WebServerOpts struct {
Handler http.Handler
}

func (o WebServerOpts) New(lggr logger.Logger, port int) *webServer {
s := &webServer{
lggr: logger.Named(lggr, "WebServer"),
port: port,
handler: o.Handler,
srvr: &http.Server{
// reasonable default based on typical prom poll interval of 15s.
ReadTimeout: 5 * time.Second,
},
done: make(chan struct{}),
}
if s.handler == nil {
s.handler = promhttp.HandlerFor(
prometheus.DefaultGatherer,
promhttp.HandlerOpts{
EnableOpenMetrics: true,
},
)
}
return s
}

// setupListener creates an explicit listener so that we can resolve `:0` port, which is needed for testing
// if we didn't need the resolved addr, or could pick a static port we could use p.srvr.ListenAndServer
func (w *webServer) setupListener() error {
l, err := net.ListenTCP("tcp", &net.TCPAddr{
Port: w.port,
})
if err != nil {
return err
}

w.tcpListener = l
return nil
}

func (w *webServer) Start(ctx context.Context) error {
err := w.setupListener()
if err != nil {
return err
}

http.Handle("/metrics", w.handler)

// pprof handler registered via import side effects

go func() {
defer close(w.done)
err := w.srvr.Serve(w.tcpListener)
if !errors.Is(err, http.ErrServerClosed) {
w.lggr.Errorw("Unexpected server error", "err", err)
}
}()
return nil
}

func (w *webServer) Close() error {
err := w.srvr.Shutdown(context.Background())
<-w.done
return err
}

func (w *webServer) Ready() error { return nil }

func (w *webServer) HealthReport() map[string]error { return map[string]error{w.Name(): nil} }

func (w *webServer) Name() string { return w.lggr.Name() }
Loading