Skip to content

Commit 4c8ac17

Browse files
metrics server as component
1 parent 0310dfd commit 4c8ac17

File tree

1 file changed

+73
-32
lines changed

1 file changed

+73
-32
lines changed

module/metrics/server.go

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,31 @@ package metrics
33
import (
44
"context"
55
"errors"
6+
"net"
67
"net/http"
78
"strconv"
89
"time"
910

1011
"github.com/prometheus/client_golang/prometheus/promhttp"
1112
"github.com/rs/zerolog"
13+
"github.com/rs/zerolog/log"
14+
15+
"github.com/onflow/flow-go/module/component"
16+
"github.com/onflow/flow-go/module/irrecoverable"
1217
)
1318

19+
// metricsServerShutdownTimeout is the time to wait for the server to shut down gracefully
20+
const metricsServerShutdownTimeout = 5 * time.Second
21+
1422
// Server is the http server that will be serving the /metrics request for prometheus
1523
type Server struct {
16-
server *http.Server
17-
log zerolog.Logger
24+
component.Component
25+
26+
address string
27+
server *http.Server
28+
log zerolog.Logger
29+
30+
startupCompleted chan struct{}
1831
}
1932

2033
// NewServer creates a new server that will start on the specified port,
@@ -25,44 +38,72 @@ func NewServer(log zerolog.Logger, port uint) *Server {
2538
mux := http.NewServeMux()
2639
endpoint := "/metrics"
2740
mux.Handle(endpoint, promhttp.Handler())
28-
log.Info().Str("address", addr).Str("endpoint", endpoint).Msg("metrics server started")
2941

3042
m := &Server{
31-
server: &http.Server{Addr: addr, Handler: mux},
32-
log: log,
43+
address: addr,
44+
server: &http.Server{Addr: addr, Handler: mux},
45+
log: log.With().Str("address", addr).Str("endpoint", endpoint).Logger(),
46+
startupCompleted: make(chan struct{}),
3347
}
3448

49+
m.Component = component.NewComponentManagerBuilder().
50+
AddWorker(m.serve).
51+
AddWorker(m.shutdownOnContextDone).
52+
Build()
53+
3554
return m
3655
}
3756

38-
// Ready returns a channel that will close when the network stack is ready.
39-
func (m *Server) Ready() <-chan struct{} {
40-
ready := make(chan struct{})
41-
go func() {
42-
if err := m.server.ListenAndServe(); err != nil {
43-
// http.ErrServerClosed is returned when Close or Shutdown is called
44-
// we don't consider this an error, so print this with debug level instead
45-
if errors.Is(err, http.ErrServerClosed) {
46-
m.log.Debug().Err(err).Msg("metrics server shutdown")
47-
} else {
48-
m.log.Err(err).Msg("error shutting down metrics server")
49-
}
57+
func (m *Server) serve(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) {
58+
m.log.Info().Msg("starting metrics server on address")
59+
60+
l, err := net.Listen("tcp", m.address)
61+
if err != nil {
62+
m.log.Err(err).Msg("failed to start the metrics server")
63+
ctx.Throw(err)
64+
return
65+
}
66+
67+
ready()
68+
69+
// pass the signaler context to the server so that the signaler context
70+
// can control the server's lifetime
71+
m.server.BaseContext = func(_ net.Listener) context.Context {
72+
return ctx
73+
}
74+
75+
err = m.server.Serve(l) // blocking call
76+
if err != nil {
77+
if errors.Is(err, http.ErrServerClosed) {
78+
return
5079
}
51-
}()
52-
go func() {
53-
close(ready)
54-
}()
55-
return ready
80+
log.Err(err).Msg("fatal error in the metrics server")
81+
ctx.Throw(err)
82+
}
5683
}
5784

58-
// Done returns a channel that will close when shutdown is complete.
59-
func (m *Server) Done() <-chan struct{} {
60-
done := make(chan struct{})
61-
go func() {
62-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
63-
_ = m.server.Shutdown(ctx)
64-
cancel()
65-
close(done)
66-
}()
67-
return done
85+
func (m *Server) shutdownOnContextDone(ictx irrecoverable.SignalerContext, ready component.ReadyFunc) {
86+
ready()
87+
<-ictx.Done()
88+
89+
ctx, cancel := context.WithTimeout(context.Background(), metricsServerShutdownTimeout)
90+
defer cancel()
91+
92+
// shutdown the server gracefully
93+
err := m.server.Shutdown(ctx)
94+
if err == nil {
95+
m.log.Info().Msg("metrics server graceful shutdown completed")
96+
return
97+
}
98+
99+
if errors.Is(err, ctx.Err()) {
100+
m.log.Warn().Msg("metrics server graceful shutdown timed out")
101+
// shutdown the server forcefully
102+
err := m.server.Close()
103+
if err != nil {
104+
m.log.Err(err).Msg("error closing metrics server")
105+
}
106+
} else {
107+
m.log.Err(err).Msg("error shutting down metrics server")
108+
}
68109
}

0 commit comments

Comments
 (0)