github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/internal/cli/server/server.go (about) 1 package server 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net" 8 "net/http" 9 "os" 10 "strings" 11 "time" 12 13 "github.com/ethereum/go-ethereum/accounts/keystore" 14 "github.com/ethereum/go-ethereum/eth" 15 "github.com/ethereum/go-ethereum/eth/tracers" 16 "github.com/ethereum/go-ethereum/ethstats" 17 "github.com/ethereum/go-ethereum/graphql" 18 "github.com/ethereum/go-ethereum/internal/cli/server/proto" 19 "github.com/ethereum/go-ethereum/log" 20 "github.com/ethereum/go-ethereum/metrics" 21 "github.com/ethereum/go-ethereum/metrics/influxdb" 22 "github.com/ethereum/go-ethereum/metrics/prometheus" 23 "github.com/ethereum/go-ethereum/node" 24 "github.com/mattn/go-colorable" 25 "github.com/mattn/go-isatty" 26 "go.opentelemetry.io/otel" 27 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" 28 "go.opentelemetry.io/otel/propagation" 29 "go.opentelemetry.io/otel/sdk/resource" 30 sdktrace "go.opentelemetry.io/otel/sdk/trace" 31 semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 32 "google.golang.org/grpc" 33 ) 34 35 type Server struct { 36 proto.UnimplementedBorServer 37 node *node.Node 38 backend *eth.Ethereum 39 grpcServer *grpc.Server 40 tracer *sdktrace.TracerProvider 41 config *Config 42 } 43 44 func NewServer(config *Config) (*Server, error) { 45 srv := &Server{ 46 config: config, 47 } 48 49 // start the logger 50 setupLogger(config.LogLevel) 51 52 if err := srv.setupGRPCServer(config.GRPC.Addr); err != nil { 53 return nil, err 54 } 55 56 // load the chain genesis 57 if err := config.loadChain(); err != nil { 58 return nil, err 59 } 60 61 // create the node/stack 62 nodeCfg, err := config.buildNode() 63 if err != nil { 64 return nil, err 65 } 66 stack, err := node.New(nodeCfg) 67 if err != nil { 68 return nil, err 69 } 70 srv.node = stack 71 72 // setup account manager (only keystore) 73 { 74 keydir := stack.KeyStoreDir() 75 n, p := keystore.StandardScryptN, keystore.StandardScryptP 76 if config.Accounts.UseLightweightKDF { 77 n, p = keystore.LightScryptN, keystore.LightScryptP 78 } 79 stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) 80 } 81 82 // register the ethereum backend 83 ethCfg, err := config.buildEth(stack) 84 if err != nil { 85 return nil, err 86 } 87 88 backend, err := eth.New(stack, ethCfg) 89 if err != nil { 90 return nil, err 91 } 92 srv.backend = backend 93 94 // debug tracing is enabled by default 95 stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) 96 97 // graphql is started from another place 98 if config.JsonRPC.Graphql.Enabled { 99 if err := graphql.New(stack, backend.APIBackend, config.JsonRPC.Cors, config.JsonRPC.VHost); err != nil { 100 return nil, fmt.Errorf("failed to register the GraphQL service: %v", err) 101 } 102 } 103 104 // register ethash service 105 if config.Ethstats != "" { 106 if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), config.Ethstats); err != nil { 107 return nil, err 108 } 109 } 110 111 // sealing (if enabled) or in dev mode 112 if config.Sealer.Enabled || config.Developer.Enabled { 113 if err := backend.StartMining(1); err != nil { 114 return nil, err 115 } 116 } 117 118 if err := srv.setupMetrics(config.Telemetry, config.Name); err != nil { 119 return nil, err 120 } 121 122 // start the node 123 if err := srv.node.Start(); err != nil { 124 return nil, err 125 } 126 return srv, nil 127 } 128 129 func (s *Server) Stop() { 130 s.node.Close() 131 132 // shutdown the tracer 133 if s.tracer != nil { 134 if err := s.tracer.Shutdown(context.Background()); err != nil { 135 log.Error("Failed to shutdown open telemetry tracer") 136 } 137 } 138 } 139 140 func (s *Server) setupMetrics(config *TelemetryConfig, serviceName string) error { 141 metrics.Enabled = config.Enabled 142 metrics.EnabledExpensive = config.Expensive 143 144 if !metrics.Enabled { 145 // metrics are disabled, do not set up any sink 146 return nil 147 } 148 149 log.Info("Enabling metrics collection") 150 151 // influxdb 152 if v1Enabled, v2Enabled := config.InfluxDB.V1Enabled, config.InfluxDB.V2Enabled; v1Enabled || v2Enabled { 153 if v1Enabled && v2Enabled { 154 return fmt.Errorf("both influx v1 and influx v2 cannot be enabled") 155 } 156 157 cfg := config.InfluxDB 158 tags := cfg.Tags 159 endpoint := cfg.Endpoint 160 161 if v1Enabled { 162 log.Info("Enabling metrics export to InfluxDB (v1)") 163 go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Database, cfg.Username, cfg.Password, "geth.", tags) 164 } 165 if v2Enabled { 166 log.Info("Enabling metrics export to InfluxDB (v2)") 167 go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, cfg.Token, cfg.Bucket, cfg.Organization, "geth.", tags) 168 } 169 } 170 171 // Start system runtime metrics collection 172 go metrics.CollectProcessMetrics(3 * time.Second) 173 174 if config.PrometheusAddr != "" { 175 176 prometheusMux := http.NewServeMux() 177 178 prometheusMux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { 179 prometheus.Handler(metrics.DefaultRegistry) 180 }) 181 182 promServer := &http.Server{ 183 Addr: config.PrometheusAddr, 184 Handler: prometheusMux, 185 } 186 187 go func() { 188 if err := promServer.ListenAndServe(); err != nil { 189 log.Error("Failure in running Prometheus server", "err", err) 190 } 191 }() 192 193 } 194 195 if config.OpenCollectorEndpoint != "" { 196 // setup open collector tracer 197 ctx := context.Background() 198 199 res, err := resource.New(ctx, 200 resource.WithAttributes( 201 // the service name used to display traces in backends 202 semconv.ServiceNameKey.String(serviceName), 203 ), 204 ) 205 if err != nil { 206 return fmt.Errorf("failed to create open telemetry resource for service: %v", err) 207 } 208 209 // Set up a trace exporter 210 traceExporter, err := otlptracegrpc.New( 211 ctx, 212 otlptracegrpc.WithInsecure(), 213 otlptracegrpc.WithEndpoint(config.OpenCollectorEndpoint), 214 ) 215 if err != nil { 216 return fmt.Errorf("failed to create open telemetry tracer exporter for service: %v", err) 217 } 218 219 // Register the trace exporter with a TracerProvider, using a batch 220 // span processor to aggregate spans before export. 221 bsp := sdktrace.NewBatchSpanProcessor(traceExporter) 222 tracerProvider := sdktrace.NewTracerProvider( 223 sdktrace.WithSampler(sdktrace.AlwaysSample()), 224 sdktrace.WithResource(res), 225 sdktrace.WithSpanProcessor(bsp), 226 ) 227 otel.SetTracerProvider(tracerProvider) 228 229 // set global propagator to tracecontext (the default is no-op). 230 otel.SetTextMapPropagator(propagation.TraceContext{}) 231 232 // set the tracer 233 s.tracer = tracerProvider 234 } 235 236 return nil 237 } 238 239 func (s *Server) setupGRPCServer(addr string) error { 240 s.grpcServer = grpc.NewServer(s.withLoggingUnaryInterceptor()) 241 proto.RegisterBorServer(s.grpcServer, s) 242 243 lis, err := net.Listen("tcp", addr) 244 if err != nil { 245 return err 246 } 247 248 go func() { 249 if err := s.grpcServer.Serve(lis); err != nil { 250 log.Error("failed to serve grpc server", "err", err) 251 } 252 }() 253 254 log.Info("GRPC Server started", "addr", addr) 255 return nil 256 } 257 258 func (s *Server) withLoggingUnaryInterceptor() grpc.ServerOption { 259 return grpc.UnaryInterceptor(s.loggingServerInterceptor) 260 } 261 262 func (s *Server) loggingServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 263 start := time.Now() 264 h, err := handler(ctx, req) 265 log.Trace("Request", "method", info.FullMethod, "duration", time.Since(start), "error", err) 266 return h, err 267 } 268 269 func setupLogger(logLevel string) { 270 output := io.Writer(os.Stderr) 271 usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" 272 if usecolor { 273 output = colorable.NewColorableStderr() 274 } 275 ostream := log.StreamHandler(output, log.TerminalFormat(usecolor)) 276 glogger := log.NewGlogHandler(ostream) 277 278 // logging 279 lvl, err := log.LvlFromString(strings.ToLower(logLevel)) 280 if err == nil { 281 glogger.Verbosity(lvl) 282 } else { 283 glogger.Verbosity(log.LvlInfo) 284 } 285 log.Root().SetHandler(glogger) 286 }