diff options
-rw-r--r-- | cmd/rr/.rr.yaml | 12 | ||||
-rw-r--r-- | cmd/rr/cmd/root.go | 24 | ||||
-rw-r--r-- | cmd/rr/http/reset.go | 2 | ||||
-rw-r--r-- | cmd/rr/http/workers.go | 14 | ||||
-rw-r--r-- | cmd/rr/main.go | 4 | ||||
-rw-r--r-- | cmd/rr/utils/config.go | 26 | ||||
-rw-r--r-- | debug/config.go | 7 | ||||
-rw-r--r-- | debug/service.go | 125 | ||||
-rw-r--r-- | http/server.go | 35 | ||||
-rw-r--r-- | http/service.go | 21 | ||||
-rw-r--r-- | pool.go | 2 | ||||
-rw-r--r-- | rpc/config.go | 2 | ||||
-rw-r--r-- | server.go | 6 | ||||
-rw-r--r-- | static/service.go | 16 | ||||
-rw-r--r-- | static_pool.go | 2 | ||||
-rw-r--r-- | utils/cprint.go (renamed from cmd/rr/utils/cprint.go) | 0 |
16 files changed, 229 insertions, 69 deletions
diff --git a/cmd/rr/.rr.yaml b/cmd/rr/.rr.yaml index bf4c699b..9ee75856 100644 --- a/cmd/rr/.rr.yaml +++ b/cmd/rr/.rr.yaml @@ -33,7 +33,7 @@ http: # worker pool configuration. pool: # number of workers to be serving. - numWorkers: 16 + numWorkers: 1 # maximum jobs per worker, 0 - unlimited. maxJobs: 0 @@ -53,4 +53,12 @@ static: dir: "c:/GoProj/phpapp/webroot" # list of extensions for forbid for serving. - forbid: [".php", ".htaccess"]
\ No newline at end of file + forbid: [".php", ".htaccess"] + +# rr debugging +debug: + # enable debug output + enable: true + + # show individual worker events + verbose: true
\ No newline at end of file diff --git a/cmd/rr/cmd/root.go b/cmd/rr/cmd/root.go index ea7c8a3b..59a43b4e 100644 --- a/cmd/rr/cmd/root.go +++ b/cmd/rr/cmd/root.go @@ -24,8 +24,8 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/spiral/roadrunner/cmd/rr/utils" "github.com/spiral/roadrunner/service" + "github.com/spiral/roadrunner/utils" "os" ) @@ -49,6 +49,26 @@ var ( } ) +// ViperWrapper provides interface bridge between v configs and service.Config. +type ViperWrapper struct { + v *viper.Viper +} + +// Get nested config section (sub-map), returns nil if section not found. +func (w *ViperWrapper) Get(key string) service.Config { + sub := w.v.Sub(key) + if sub == nil { + return nil + } + + return &ViperWrapper{sub} +} + +// Unmarshal unmarshal config data into given struct. +func (w *ViperWrapper) Unmarshal(out interface{}) error { + return w.v.Unmarshal(out) +} + // Execute adds all child commands to the CLI command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the CLI. func Execute() { @@ -100,5 +120,5 @@ func initConfig(cfgFile string, path []string, name string) service.Config { return nil } - return &utils.ViperWrapper{Viper: cfg} + return &ViperWrapper{cfg} } diff --git a/cmd/rr/http/reset.go b/cmd/rr/http/reset.go index 2583f8cd..fd27149d 100644 --- a/cmd/rr/http/reset.go +++ b/cmd/rr/http/reset.go @@ -26,7 +26,7 @@ import ( rr "github.com/spiral/roadrunner/cmd/rr/cmd" "github.com/spiral/roadrunner/rpc" "github.com/spiral/roadrunner/service" - "github.com/spiral/roadrunner/cmd/rr/utils" + "github.com/spiral/roadrunner/utils" ) func init() { diff --git a/cmd/rr/http/workers.go b/cmd/rr/http/workers.go index b5da833d..ddfe337c 100644 --- a/cmd/rr/http/workers.go +++ b/cmd/rr/http/workers.go @@ -22,20 +22,20 @@ package http import ( "errors" + tm "github.com/buger/goterm" + "github.com/dustin/go-humanize" + "github.com/olekukonko/tablewriter" + "github.com/shirou/gopsutil/process" "github.com/spf13/cobra" rr "github.com/spiral/roadrunner/cmd/rr/cmd" + "github.com/spiral/roadrunner/http" rrpc "github.com/spiral/roadrunner/rpc" "github.com/spiral/roadrunner/service" - "github.com/spiral/roadrunner/http" - "github.com/olekukonko/tablewriter" + "github.com/spiral/roadrunner/utils" + "net/rpc" "os" "strconv" "time" - "github.com/dustin/go-humanize" - "github.com/spiral/roadrunner/cmd/rr/utils" - "github.com/shirou/gopsutil/process" - "net/rpc" - tm "github.com/buger/goterm" ) var ( diff --git a/cmd/rr/main.go b/cmd/rr/main.go index ed47b2ed..6065d3d1 100644 --- a/cmd/rr/main.go +++ b/cmd/rr/main.go @@ -32,6 +32,7 @@ import ( // cli plugins _ "github.com/spiral/roadrunner/cmd/rr/http" + "github.com/spiral/roadrunner/debug" ) func main() { @@ -44,6 +45,9 @@ func main() { // serving static files rr.Container.Register(static.Name, &static.Service{}) + // provides additional verbosity + rr.Container.Register(debug.Name, &debug.Service{Logger: rr.Logger}) + // you can register additional commands using cmd.CLI rr.Execute() } diff --git a/cmd/rr/utils/config.go b/cmd/rr/utils/config.go deleted file mode 100644 index 452dd195..00000000 --- a/cmd/rr/utils/config.go +++ /dev/null @@ -1,26 +0,0 @@ -package utils - -import ( - "github.com/spf13/viper" - "github.com/spiral/roadrunner/service" -) - -// ViperWrapper provides interface bridge between Viper configs and service.Config. -type ViperWrapper struct { - Viper *viper.Viper -} - -// Get nested config section (sub-map), returns nil if section not found. -func (w *ViperWrapper) Get(key string) service.Config { - sub := w.Viper.Sub(key) - if sub == nil { - return nil - } - - return &ViperWrapper{sub} -} - -// Unmarshal unmarshal config data into given struct. -func (w *ViperWrapper) Unmarshal(out interface{}) error { - return w.Viper.Unmarshal(out) -} diff --git a/debug/config.go b/debug/config.go new file mode 100644 index 00000000..54ec9bf5 --- /dev/null +++ b/debug/config.go @@ -0,0 +1,7 @@ +package debug + +// Config enabled and disabled debug service. +type Config struct { + // Enable must be set to true to enable debugging. + Enable bool +} diff --git a/debug/service.go b/debug/service.go new file mode 100644 index 00000000..5838b75d --- /dev/null +++ b/debug/service.go @@ -0,0 +1,125 @@ +package debug + +import ( + "github.com/sirupsen/logrus" + "github.com/spiral/roadrunner/service" + "github.com/spiral/roadrunner/http" + "github.com/spiral/roadrunner/utils" + "github.com/spiral/roadrunner" +) + +// Default service name. +const Name = "debug" + +// Service provide debug callback for system events. With colors! +type Service struct { + // Logger used to flush all debug events. + Logger *logrus.Logger + + cfg *Config +} + +// Configure must return configure service and return true if service hasStatus enabled. Must return error in case of +// misconfiguration. Services must not be used without proper configuration pushed first. +func (s *Service) Configure(cfg service.Config, c service.Container) (enabled bool, err error) { + config := &Config{} + if err := cfg.Unmarshal(config); err != nil { + return false, err + } + + if !config.Enable { + return false, nil + } + + s.cfg = config + + // registering as middleware + if h, ok := c.Get(http.Name); ok >= service.StatusConfigured { + if h, ok := h.(*http.Service); ok { + h.AddListener(s.listener) + } + } else { + s.Logger.Error("unable to find http server") + } + + return true, nil +} + +// listener listens to http events and generates nice looking output. +func (s *Service) listener(event int, ctx interface{}) { + // http events + switch event { + case http.EventResponse: + log := ctx.(*http.Log) + s.Logger.Print(utils.Sprintf("%s <white+hb>%s</reset> %s", statusColor(log.Status), log.Method, log.Uri)) + case http.EventError: + log := ctx.(*http.Log) + + if _, ok := log.Error.(roadrunner.JobError); ok { + s.Logger.Print(utils.Sprintf("%s <white+hb>%s</reset> %s", statusColor(log.Status), log.Method, log.Uri)) + } else { + s.Logger.Print(utils.Sprintf( + "%s <white+hb>%s</reset> %s <red>%s</reset>", + statusColor(log.Status), + log.Method, + log.Uri, + log.Error, + )) + } + } + + switch event { + case roadrunner.EventWorkerKill: + w := ctx.(*roadrunner.Worker) + s.Logger.Print(utils.Sprintf( + "<white+hb>worker.%v</reset> <red>killed</red>", + *w.Pid, + )) + + case roadrunner.EventWorkerError: + err := ctx.(roadrunner.WorkerError) + s.Logger.Print(utils.Sprintf( + "<white+hb>worker.%v</reset> <red></reset>", + *err.Worker.Pid, + err.Caused, + )) + } + + // rr server events + switch event { + case roadrunner.EventServerFailure: + s.Logger.Print(utils.Sprintf("<red+hb>http.rr</reset>: <red>%s</reset>", ctx)) + } + + // pool events + switch event { + case roadrunner.EventPoolConstruct: + s.Logger.Print(utils.Sprintf("<white+hb>http.rr</reset>: <green>worker pool constructed</reset>")) + case roadrunner.EventPoolDestruct: + s.Logger.Print(utils.Sprintf("<white+hb>http.rr</reset>: <yellow>worker pool destructed</reset>")) + case roadrunner.EventPoolError: + s.Logger.Print(utils.Sprintf("<red+hb>http.rr</reset>: <red>%s</reset>", ctx)) + } +} + +// Serve serves. +func (s *Service) Serve() error { return nil } + +// Stop stops the service. +func (s *Service) Stop() {} + +func statusColor(status int) string { + if status < 300 { + return utils.Sprintf("<green>%v</reset>", status) + } + + if status < 400 { + return utils.Sprintf("<cyan>%v</reset>", status) + } + + if status < 500 { + return utils.Sprintf("<yellow>%v</reset>", status) + } + + return utils.Sprintf("<red+hb>%v</reset>", status) +} diff --git a/http/server.go b/http/server.go index 178980a7..de414b08 100644 --- a/http/server.go +++ b/http/server.go @@ -7,6 +7,29 @@ import ( "github.com/pkg/errors" ) +const ( + // EventResponse thrown after the request been processed. See Log as payload. + EventResponse = iota + 500 + + // EventError thrown on any non job error provided by road runner server. + EventError +) + +// Log represents singular http response event. +type Log struct { + // Method of the request. + Method string + + // Uri requested by the client. + Uri string + + // Status is response status. + Status int + + // Associated error, if any. + Error error +} + // Server serves http connections to underlying PHP application using PSR-7 protocol. Context will include request headers, // parsed files and query, payload will include parsed form dataTree (if any). type Server struct { @@ -15,7 +38,7 @@ type Server struct { rr *roadrunner.Server } -// Listen attaches pool event watcher. +// AddListener attaches pool event watcher. func (s *Server) Listen(l func(event int, ctx interface{})) { s.listener = l } @@ -65,12 +88,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + s.handleResponse(req, resp) resp.Write(w) } -// handleError sends error +// handleResponse triggers response event. +func (s *Server) handleResponse(req *Request, resp *Response) { + s.throw(EventResponse, &Log{Method: req.Method, Uri: req.Uri, Status: resp.Status}) +} + +// handleError sends error. func (s *Server) handleError(w http.ResponseWriter, r *http.Request, err error) { - s.throw(2332323, err) + s.throw(EventError, &Log{Method: r.Method, Uri: uri(r), Status: 500, Error: err}) w.WriteHeader(500) w.Write([]byte(err.Error())) diff --git a/http/service.go b/http/service.go index 81ffb8a2..7835a652 100644 --- a/http/service.go +++ b/http/service.go @@ -18,24 +18,21 @@ type Middleware interface { // Service manages rr, http servers. type Service struct { - cfg *Config - - // todo: multiple listeners - listener func(event int, ctx interface{}) - + cfg *Config + listeners []func(event int, ctx interface{}) middleware []Middleware rr *roadrunner.Server srv *Server http *http.Server } -func (s *Service) Add(m Middleware) { +func (s *Service) AddMiddleware(m Middleware) { s.middleware = append(s.middleware, m) } -// Listen attaches server event watcher. -func (s *Service) Listen(o func(event int, ctx interface{})) { - s.listener = o +// AddListener attaches server event watcher. +func (s *Service) AddListener(l func(event int, ctx interface{})) { + s.listeners = append(s.listeners, l) } // Configure must return configure svc and return true if svc hasStatus enabled. Must return error in case of @@ -114,3 +111,9 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.srv.ServeHTTP(w, r) } + +func (s *Service) listener(event int, ctx interface{}) { + for _, l := range s.listeners { + l(event, ctx) + } +} @@ -19,7 +19,7 @@ const ( // Pool managed set of inner worker processes. type Pool interface { - // Listen all caused events to attached watcher. + // AddListener all caused events to attached watcher. Listen(l func(event int, ctx interface{})) // Exec one task with given payload and context, returns result or error. diff --git a/rpc/config.go b/rpc/config.go index 8a34752a..06d63d65 100644 --- a/rpc/config.go +++ b/rpc/config.go @@ -10,7 +10,7 @@ type config struct { // Indicates if RPC connection is enabled. Enable bool - // Listen string + // AddListener string Listen string } @@ -49,7 +49,7 @@ func NewServer(cfg *ServerConfig) *Server { return &Server{cfg: cfg} } -// Listen attaches server event watcher. +// AddListener attaches server event watcher. func (srv *Server) Listen(l func(event int, ctx interface{})) { srv.listener = l } @@ -169,7 +169,7 @@ func (srv *Server) Pool() Pool { return srv.pool } -// Listen pool events. +// AddListener pool events. func (srv *Server) poolListener(event int, ctx interface{}) { // bypassing to user specified listener srv.throw(event, ctx) @@ -185,7 +185,7 @@ func (srv *Server) poolListener(event int, ctx interface{}) { srv.factory = nil // everything is dead, this is recoverable but heavy state - srv.throw(EventServerFailure, srv) + srv.throw(EventServerFailure, err) } } } diff --git a/static/service.go b/static/service.go index 03a18ba8..5f6030aa 100644 --- a/static/service.go +++ b/static/service.go @@ -18,9 +18,6 @@ type Service struct { // root is initiated http directory root http.Dir - - // let's service stay running - done chan interface{} } // Configure must return configure service and return true if service hasStatus enabled. Must return error in case of @@ -45,7 +42,7 @@ func (s *Service) Configure(cfg service.Config, c service.Container) (enabled bo // registering as middleware if h, ok := c.Get(rrttp.Name); ok >= service.StatusConfigured { if h, ok := h.(*rrttp.Service); ok { - h.Add(s) + h.AddMiddleware(s) } } @@ -53,17 +50,10 @@ func (s *Service) Configure(cfg service.Config, c service.Container) (enabled bo } // Serve serves the service. -func (s *Service) Serve() error { - s.done = make(chan interface{}) - <-s.done - - return nil -} +func (s *Service) Serve() error { return nil } // Stop stops the service. -func (s *Service) Stop() { - close(s.done) -} +func (s *Service) Stop() {} // Handle must return true if request/response pair is handled withing the middleware. func (s *Service) Handle(w http.ResponseWriter, r *http.Request) bool { diff --git a/static_pool.go b/static_pool.go index 887904b8..0ae345e5 100644 --- a/static_pool.go +++ b/static_pool.go @@ -69,7 +69,7 @@ func NewPool(cmd func() *exec.Cmd, factory Factory, cfg Config) (*StaticPool, er return p, nil } -// Listen attaches pool event watcher. +// AddListener attaches pool event watcher. func (p *StaticPool) Listen(l func(event int, ctx interface{})) { p.listener = l } diff --git a/cmd/rr/utils/cprint.go b/utils/cprint.go index f6f828f8..f6f828f8 100644 --- a/cmd/rr/utils/cprint.go +++ b/utils/cprint.go |