diff --git a/pkg/appsrv/appparams.go b/pkg/appsrv/appparams.go index c6db1658c5..64b425dd05 100644 --- a/pkg/appsrv/appparams.go +++ b/pkg/appsrv/appparams.go @@ -18,6 +18,7 @@ import ( "context" "net/http" + "yunion.io/x/jsonutils" "yunion.io/x/pkg/appctx" ) @@ -31,6 +32,7 @@ type SAppParams struct { SkipTrace bool Params map[string]string Path []string + Body jsonutils.JSONObject Request *http.Request Response http.ResponseWriter diff --git a/pkg/appsrv/appsrv.go b/pkg/appsrv/appsrv.go index 4896585259..6732ee4e4b 100644 --- a/pkg/appsrv/appsrv.go +++ b/pkg/appsrv/appsrv.go @@ -16,10 +16,12 @@ package appsrv import ( "bufio" + "bytes" "context" "crypto/sha1" "encoding/base64" "fmt" + "io/ioutil" "math/rand" "net" "net/http" @@ -33,6 +35,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/appctx" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/trace" "yunion.io/x/pkg/util/httputils" "yunion.io/x/pkg/util/signalutils" @@ -67,6 +70,8 @@ type Application struct { httpServer *http.Server slaveHttpServer *http.Server + exception func(method, path string, body jsonutils.JSONObject, err error) + isTLS bool } @@ -118,6 +123,11 @@ func NewApplication(name string, connMax int, db bool) *Application { return &app } +func (self *Application) OnException(exception func(method, path string, body jsonutils.JSONObject, err error)) *Application { + self.exception = exception + return self +} + func SplitPath(path string) []string { ret := make([]string, 0) for _, seg := range strings.Split(path, "/") { @@ -194,6 +204,12 @@ func (app *Application) AddHandler3(hi *SHandlerInfo) *SHandlerInfo { type loggingResponseWriter struct { http.ResponseWriter status int + data []byte +} + +func (lrw *loggingResponseWriter) Write(data []byte) (int, error) { + lrw.data = data + return lrw.ResponseWriter.Write(data) } func (lrw *loggingResponseWriter) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { @@ -227,7 +243,7 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) { // log.Printf("defaultHandler %s %s", r.Method, r.URL.Path) rid := genRequestId(w, r) w.Header().Set("X-Request-Host-Id", app.hostId) - lrw := &loggingResponseWriter{w, http.StatusOK} + lrw := &loggingResponseWriter{ResponseWriter: w, status: http.StatusOK, data: []byte{}} start := time.Now() hi, params := app.defaultHandle(lrw, r, rid) if hi == nil { @@ -240,6 +256,9 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) { counter = &hi.counter4XX } else { counter = &hi.counter5XX + if app.exception != nil { + app.exception(r.Method, r.URL.String(), params.Body, errors.Errorf(string(lrw.data))) + } } duration := float64(time.Since(start).Nanoseconds()) / 1000000 counter.hit += 1 @@ -375,6 +394,11 @@ func (app *Application) defaultHandle(w http.ResponseWriter, r *http.Request, ri task.appParams = hand.GetAppParams(params, segs) task.appParams.Request = r task.appParams.Response = w + if r.Body != nil && r.ContentLength > 0 && getContentType(r) == ContentTypeJson { + data, _ := ioutil.ReadAll(r.Body) + task.appParams.Body, _ = jsonutils.Parse(data) + r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) + } session.Run( task, currentWorker, diff --git a/pkg/compute/service/service.go b/pkg/compute/service/service.go index 13d8a120fe..92a7184cce 100644 --- a/pkg/compute/service/service.go +++ b/pkg/compute/service/service.go @@ -22,6 +22,7 @@ import ( "time" "yunion.io/x/cloudmux/pkg/multicloud/esxi" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" _ "yunion.io/x/sqlchemy/backends" @@ -87,7 +88,10 @@ func StartService() { log.Errorf("try to init etcd options error: %v", err) } - app := app_common.InitApp(baseOpts, true) + app := app_common.InitApp(baseOpts, true). + OnException(func(method, path string, body jsonutils.JSONObject, err error) { + // send notify exception + }) cloudcommon.InitDB(dbOpts)