github.com/ewagmig/fabric@v2.1.1+incompatible/core/operations/system.go (about) 1 /* 2 Copyright IBM Corp All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package operations 8 9 import ( 10 "context" 11 "crypto/tls" 12 "net" 13 "net/http" 14 "os" 15 "strings" 16 "time" 17 18 kitstatsd "github.com/go-kit/kit/metrics/statsd" 19 "github.com/hyperledger/fabric-lib-go/healthz" 20 "github.com/hyperledger/fabric/common/flogging" 21 "github.com/hyperledger/fabric/common/flogging/httpadmin" 22 "github.com/hyperledger/fabric/common/metadata" 23 "github.com/hyperledger/fabric/common/metrics" 24 "github.com/hyperledger/fabric/common/metrics/disabled" 25 "github.com/hyperledger/fabric/common/metrics/prometheus" 26 "github.com/hyperledger/fabric/common/metrics/statsd" 27 "github.com/hyperledger/fabric/common/metrics/statsd/goruntime" 28 "github.com/hyperledger/fabric/common/util" 29 "github.com/hyperledger/fabric/core/middleware" 30 "github.com/prometheus/client_golang/prometheus/promhttp" 31 ) 32 33 //go:generate counterfeiter -o fakes/logger.go -fake-name Logger . Logger 34 35 type Logger interface { 36 Warn(args ...interface{}) 37 Warnf(template string, args ...interface{}) 38 } 39 40 type Statsd struct { 41 Network string 42 Address string 43 WriteInterval time.Duration 44 Prefix string 45 } 46 47 type MetricsOptions struct { 48 Provider string 49 Statsd *Statsd 50 } 51 52 type Options struct { 53 Logger Logger 54 ListenAddress string 55 Metrics MetricsOptions 56 TLS TLS 57 Version string 58 } 59 60 type System struct { 61 metrics.Provider 62 63 logger Logger 64 healthHandler *healthz.HealthHandler 65 options Options 66 statsd *kitstatsd.Statsd 67 collectorTicker *time.Ticker 68 sendTicker *time.Ticker 69 httpServer *http.Server 70 mux *http.ServeMux 71 addr string 72 versionGauge metrics.Gauge 73 } 74 75 func NewSystem(o Options) *System { 76 logger := o.Logger 77 if logger == nil { 78 logger = flogging.MustGetLogger("operations.runner") 79 } 80 81 system := &System{ 82 logger: logger, 83 options: o, 84 } 85 86 system.initializeServer() 87 system.initializeHealthCheckHandler() 88 system.initializeLoggingHandler() 89 system.initializeMetricsProvider() 90 system.initializeVersionInfoHandler() 91 92 return system 93 } 94 95 func (s *System) Run(signals <-chan os.Signal, ready chan<- struct{}) error { 96 err := s.Start() 97 if err != nil { 98 return err 99 } 100 101 close(ready) 102 103 select { 104 case <-signals: 105 return s.Stop() 106 } 107 } 108 109 func (s *System) Start() error { 110 err := s.startMetricsTickers() 111 if err != nil { 112 return err 113 } 114 115 s.versionGauge.With("version", s.options.Version).Set(1) 116 117 listener, err := s.listen() 118 if err != nil { 119 return err 120 } 121 s.addr = listener.Addr().String() 122 123 go s.httpServer.Serve(listener) 124 125 return nil 126 } 127 128 func (s *System) Stop() error { 129 if s.collectorTicker != nil { 130 s.collectorTicker.Stop() 131 s.collectorTicker = nil 132 } 133 if s.sendTicker != nil { 134 s.sendTicker.Stop() 135 s.sendTicker = nil 136 } 137 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 138 defer cancel() 139 140 return s.httpServer.Shutdown(ctx) 141 } 142 143 func (s *System) RegisterChecker(component string, checker healthz.HealthChecker) error { 144 return s.healthHandler.RegisterChecker(component, checker) 145 } 146 147 func (s *System) initializeServer() { 148 s.mux = http.NewServeMux() 149 s.httpServer = &http.Server{ 150 Addr: s.options.ListenAddress, 151 Handler: s.mux, 152 ReadTimeout: 10 * time.Second, 153 WriteTimeout: 2 * time.Minute, 154 } 155 } 156 157 func (s *System) handlerChain(h http.Handler, secure bool) http.Handler { 158 if secure { 159 return middleware.NewChain(middleware.RequireCert(), middleware.WithRequestID(util.GenerateUUID)).Handler(h) 160 } 161 return middleware.NewChain(middleware.WithRequestID(util.GenerateUUID)).Handler(h) 162 } 163 164 func (s *System) initializeMetricsProvider() error { 165 m := s.options.Metrics 166 providerType := m.Provider 167 switch providerType { 168 case "statsd": 169 prefix := m.Statsd.Prefix 170 if prefix != "" && !strings.HasSuffix(prefix, ".") { 171 prefix = prefix + "." 172 } 173 174 ks := kitstatsd.New(prefix, s) 175 s.Provider = &statsd.Provider{Statsd: ks} 176 s.statsd = ks 177 s.versionGauge = versionGauge(s.Provider) 178 return nil 179 180 case "prometheus": 181 s.Provider = &prometheus.Provider{} 182 s.versionGauge = versionGauge(s.Provider) 183 s.mux.Handle("/metrics", s.handlerChain(promhttp.Handler(), s.options.TLS.Enabled)) 184 return nil 185 186 default: 187 if providerType != "disabled" { 188 s.logger.Warnf("Unknown provider type: %s; metrics disabled", providerType) 189 } 190 191 s.Provider = &disabled.Provider{} 192 s.versionGauge = versionGauge(s.Provider) 193 return nil 194 } 195 } 196 197 func (s *System) initializeLoggingHandler() { 198 s.mux.Handle("/logspec", s.handlerChain(httpadmin.NewSpecHandler(), s.options.TLS.Enabled)) 199 } 200 201 func (s *System) initializeHealthCheckHandler() { 202 s.healthHandler = healthz.NewHealthHandler() 203 s.mux.Handle("/healthz", s.handlerChain(s.healthHandler, false)) 204 } 205 206 func (s *System) initializeVersionInfoHandler() { 207 versionInfo := &VersionInfoHandler{ 208 CommitSHA: metadata.CommitSHA, 209 Version: metadata.Version, 210 } 211 s.mux.Handle("/version", s.handlerChain(versionInfo, false)) 212 } 213 214 func (s *System) startMetricsTickers() error { 215 m := s.options.Metrics 216 if s.statsd != nil { 217 network := m.Statsd.Network 218 address := m.Statsd.Address 219 c, err := net.Dial(network, address) 220 if err != nil { 221 return err 222 } 223 c.Close() 224 225 opts := s.options.Metrics.Statsd 226 writeInterval := opts.WriteInterval 227 228 s.collectorTicker = time.NewTicker(writeInterval / 2) 229 goCollector := goruntime.NewCollector(s.Provider) 230 go goCollector.CollectAndPublish(s.collectorTicker.C) 231 232 s.sendTicker = time.NewTicker(writeInterval) 233 go s.statsd.SendLoop(s.sendTicker.C, network, address) 234 } 235 236 return nil 237 } 238 239 func (s *System) listen() (net.Listener, error) { 240 listener, err := net.Listen("tcp", s.options.ListenAddress) 241 if err != nil { 242 return nil, err 243 } 244 tlsConfig, err := s.options.TLS.Config() 245 if err != nil { 246 return nil, err 247 } 248 if tlsConfig != nil { 249 listener = tls.NewListener(listener, tlsConfig) 250 } 251 return listener, nil 252 } 253 254 func (s *System) Addr() string { 255 return s.addr 256 } 257 258 func (s *System) Log(keyvals ...interface{}) error { 259 s.logger.Warn(keyvals...) 260 return nil 261 }