summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/rr/.rr.yaml12
-rw-r--r--cmd/rr/cmd/root.go24
-rw-r--r--cmd/rr/http/reset.go2
-rw-r--r--cmd/rr/http/workers.go14
-rw-r--r--cmd/rr/main.go4
-rw-r--r--cmd/rr/utils/config.go26
-rw-r--r--debug/config.go7
-rw-r--r--debug/service.go125
-rw-r--r--http/server.go35
-rw-r--r--http/service.go21
-rw-r--r--pool.go2
-rw-r--r--rpc/config.go2
-rw-r--r--server.go6
-rw-r--r--static/service.go16
-rw-r--r--static_pool.go2
-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)
+ }
+}
diff --git a/pool.go b/pool.go
index a7721050..121967c5 100644
--- a/pool.go
+++ b/pool.go
@@ -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
}
diff --git a/server.go b/server.go
index 6ee2a170..d914760b 100644
--- a/server.go
+++ b/server.go
@@ -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