From 06f4bd09334e4ece98d04acfa4c9fe5032885758 Mon Sep 17 00:00:00 2001 From: Allen Sun Date: Thu, 2 Aug 2018 16:18:38 +0800 Subject: [PATCH] feature: add request handling statistics for pouch daemon Signed-off-by: Allen Sun --- apis/server/response_stats.go | 65 +++++++++++++++++++++++++++++++++ apis/server/router.go | 28 ++++++++++++++- apis/swagger.yml | 35 ++++++++++++++++++ apis/types/response_stats.go | 67 +++++++++++++++++++++++++++++++++++ test/api_system_test.go | 21 +++++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 apis/server/response_stats.go create mode 100644 apis/types/response_stats.go diff --git a/apis/server/response_stats.go b/apis/server/response_stats.go new file mode 100644 index 0000000000..9cae594675 --- /dev/null +++ b/apis/server/response_stats.go @@ -0,0 +1,65 @@ +package server + +import "sync" + +// RequestStats is the request dealing result, +// including all handled request number, 2xx3xx number, 4xx number and 5xx number +type RequestStats struct { + sync.Mutex + // req5xxCount is the count number of failed request which returns a status code of 5xx. + req5xxCount uint64 + // req4xxCount is the count number of failed request which returns a status code of 5xx. + req4xxCount uint64 + // req2xxCount is the count number of requests which returns a status code of 2xx and 3xx. + req2xx3xxCount uint64 + // reqCount is the count number of all request. + reqCount uint64 +} + +func (rs *RequestStats) getAllCount() uint64 { + rs.Lock() + defer rs.Unlock() + return rs.reqCount +} + +func (rs *RequestStats) get2xx3xxCount() uint64 { + rs.Lock() + defer rs.Unlock() + return rs.req2xx3xxCount +} + +func (rs *RequestStats) get4xxCount() uint64 { + rs.Lock() + defer rs.Unlock() + return rs.req4xxCount +} + +func (rs *RequestStats) get5xxCount() uint64 { + rs.Lock() + defer rs.Unlock() + return rs.req5xxCount +} + +func (rs *RequestStats) increaseReqCount() { + rs.Lock() + rs.reqCount++ + rs.Unlock() +} + +func (rs *RequestStats) increase2xx3xxCount() { + rs.Lock() + rs.req2xx3xxCount++ + rs.Unlock() +} + +func (rs *RequestStats) increase4xxCount() { + rs.Lock() + rs.req4xxCount++ + rs.Unlock() +} + +func (rs *RequestStats) increase5xxCount() { + rs.Lock() + rs.req5xxCount++ + rs.Unlock() +} diff --git a/apis/server/router.go b/apis/server/router.go index 9a27eacab7..bc86499ff9 100644 --- a/apis/server/router.go +++ b/apis/server/router.go @@ -20,6 +20,9 @@ import ( // versionMatcher defines to parse version url path. const versionMatcher = "/v{version:[0-9.]+}" +// reqStats is used to calculate the request dealing status. +var reqStats = &RequestStats{} + func initRoute(s *Server) http.Handler { r := mux.NewRouter() @@ -27,6 +30,14 @@ func initRoute(s *Server) http.Handler { s.addRoute(r, http.MethodGet, "/_ping", s.ping) s.addRoute(r, http.MethodGet, "/info", s.info) s.addRoute(r, http.MethodGet, "/version", s.version) + s.addRoute(r, http.MethodGet, "/stats", func(context context.Context, rw http.ResponseWriter, req *http.Request) (err error) { + return EncodeResponse(rw, http.StatusOK, types.ResponseStats{ + AllRequests: reqStats.getAllCount(), + Req2xx3xxCount: reqStats.get2xx3xxCount(), + Req4xxCount: reqStats.get4xxCount(), + Req5xxCount: reqStats.get5xxCount(), + }) + }) s.addRoute(r, http.MethodPost, "/auth", s.auth) // daemon, we still list this API into system manager. @@ -75,7 +86,6 @@ func initRoute(s *Server) http.Handler { s.addRoute(r, http.MethodDelete, "/volumes/{name:.*}", s.removeVolume) // network - s.addRoute(r, http.MethodGet, "/networks", s.listNetwork) s.addRoute(r, http.MethodPost, "/networks/create", s.createNetwork) s.addRoute(r, http.MethodGet, "/networks/{id:.*}", s.getNetwork) @@ -184,9 +194,13 @@ func filter(handler handler, s *Server) http.HandlerFunc { logrus.Debugf("Calling %s %s, client %s", req.Method, req.URL.RequestURI(), clientInfo) } + // increase the request number by 1. + reqStats.increaseReqCount() + // Start to handle request. err := handler(ctx, w, req) if err == nil { + reqStats.increase2xx3xxCount() return } // Handle error if request handling fails. @@ -224,6 +238,8 @@ func HandleErrorResponse(w http.ResponseWriter, err error) { code = http.StatusConflict } + calReqStatictics(code) + w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) enc := json.NewEncoder(w) @@ -234,3 +250,13 @@ func HandleErrorResponse(w http.ResponseWriter, err error) { } enc.Encode(resp) } + +// calReqStatictics will increase the 4xx count and 5xx count. +func calReqStatictics(code int) { + if code >= http.StatusInternalServerError { + reqStats.increase5xxCount() + } + if code >= http.StatusBadRequest && code < http.StatusInternalServerError { + reqStats.increase4xxCount() + } +} diff --git a/apis/swagger.yml b/apis/swagger.yml index 8a8eb6d557..1d3213ce96 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -80,6 +80,20 @@ paths: schema: $ref: "#/definitions/AuthConfig" + /stats: + get: + summary: "Get daemon's request dealing result, including all handled request number, 2xx3xx number, 4xx number and 5xx number" + description: "Get daemon's request dealing result, including all handled request number, 2xx3xx number, 4xx number and 5xx number" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 200: + description: "The request handling stats have returned" + schema: + $ref: "#/definitions/ResponseStats" + /daemon/update: post: summary: "Update daemon's labels and image proxy" @@ -1644,6 +1658,27 @@ definitions: example: - ["unix:///var/run/pouchd.sock", "tcp://0.0.0.0:4243"] + ResponseStats: + type: "object" + description: ResponseStats contains all request handling result in daemon including 2xx3xx response number, 4xx and 5xx number. + properties: + AllRequests: + description: all requests number handled by daemon no matter success or failure. + type: "integer" + format: "uint64" + req2xx3xxCount: + description: all response number returned by daemon which is 2xx or 3xx status code. + type: "integer" + format: "uint64" + Req4xxCount: + description: all response number returned by daemon which is 4xx status code. + type: "integer" + format: "uint64" + Req5xxCount: + description: all response number returned by daemon which is 5xx status code. + type: "integer" + format: "uint64" + DaemonUpdateConfig: type: "object" properties: diff --git a/apis/types/response_stats.go b/apis/types/response_stats.go new file mode 100644 index 0000000000..35424bd1e2 --- /dev/null +++ b/apis/types/response_stats.go @@ -0,0 +1,67 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" +) + +// ResponseStats ResponseStats contains all request handling result in daemon including 2xx3xx response number, 4xx and 5xx number. +// swagger:model ResponseStats + +type ResponseStats struct { + + // all requests number handled by daemon no matter success or failure. + AllRequests uint64 `json:"AllRequests,omitempty"` + + // all response number returned by daemon which is 4xx status code. + Req4xxCount uint64 `json:"Req4xxCount,omitempty"` + + // all response number returned by daemon which is 5xx status code. + Req5xxCount uint64 `json:"Req5xxCount,omitempty"` + + // all response number returned by daemon which is 2xx or 3xx status code. + Req2xx3xxCount uint64 `json:"req2xx3xxCount,omitempty"` +} + +/* polymorph ResponseStats AllRequests false */ + +/* polymorph ResponseStats Req4xxCount false */ + +/* polymorph ResponseStats Req5xxCount false */ + +/* polymorph ResponseStats req2xx3xxCount false */ + +// Validate validates this response stats +func (m *ResponseStats) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *ResponseStats) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ResponseStats) UnmarshalBinary(b []byte) error { + var res ResponseStats + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/test/api_system_test.go b/test/api_system_test.go index cd99b7b212..0e80fd673b 100644 --- a/test/api_system_test.go +++ b/test/api_system_test.go @@ -105,3 +105,24 @@ func (suite *APISystemSuite) TestRegistryLogin(c *check.C) { request.DecodeBody(authResp, resp.Body) c.Assert(util.PartialEqual(authResp.Status, "Login Succeeded"), check.IsNil) } + +// TestStats tests /stats API. +func (suite *APISystemSuite) TestStats(c *check.C) { + resp, err := request.Get("/stats") + c.Assert(err, check.IsNil) + defer resp.Body.Close() + + CheckRespStatus(c, resp, 200) + + got := types.ResponseStats{} + err = json.NewDecoder(resp.Body).Decode(&got) + c.Assert(err, check.IsNil) + + if got.AllRequests <= 1 { + c.Fatalf("daemon handling request should be more than 1, actual: %d", got.AllRequests) + } + + if got.Req2xx3xxCount <= 1 { + c.Fatalf("daemon handling request should be more than 0, actual: %d", got.Req2xx3xxCount) + } +}