add prometheus record

This commit is contained in:
bryanqiu 2022-12-21 00:31:56 +08:00
parent 900d2e9789
commit 8335b17e89
4 changed files with 59 additions and 36 deletions

44
api.go
View File

@ -1,7 +1,6 @@
package api package api
import ( import (
"fmt"
"reflect" "reflect"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -46,15 +45,40 @@ func NewEngine() *Engine {
} }
func (e *Engine) POST(uri string, handler Handler) { func (e *Engine) POST(uri string, handler Handler) {
h := handlerWrapper(handler.HandlerFunc()) h := handlerWrapper(handler)
hkey := fmt.Sprintf("%v", h) log.Infof("api handler [%v] registor success", handler)
requestHandlerMapper[hkey] = handler
log.Infof("api handler [%s] registor success", hkey)
e.Engine.POST(uri, h) e.Engine.POST(uri, h)
} }
// handlerWrapper // handlerWrapper
func handlerWrapper(handler HandlerFunc) gin.HandlerFunc { func handlerWrapper(handler Handler) gin.HandlerFunc {
handlerFunc := handler.HandlerFunc()
return func(c *gin.Context) {
// Case 1. get request handler
if c.Request == nil && c.Keys != nil {
c.Keys["handler"] = handler
return
}
// Case 2. normal case, user request will go this way
if icc, ok := c.Get("cc"); !ok {
log.Errorf("Unreachable Code: can not get cc(*api.Context) from *gin.Context")
} else if cc, ok := icc.(*Context); !ok {
log.Debugf("Unreachable Code: cc from *gin.Context is type of:[%s]", reflect.TypeOf(icc).String())
log.Errorf("Unreachable Code: cc from *gin.Context is not type of *api.Context")
} else {
// user request should go this way
defer cc.TryRecover()
cc.AppId = c.GetString("appid")
cc.UserId = c.GetUint64("userid")
handlerFunc(cc)
return
}
}
}
// handlerWrapper
func handlerFuncWrapper(handler HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
if icc, ok := c.Get("cc"); !ok { if icc, ok := c.Get("cc"); !ok {
log.Errorf("Unreachable Code: can not get cc(*api.Context) from *gin.Context") log.Errorf("Unreachable Code: can not get cc(*api.Context) from *gin.Context")
@ -66,6 +90,7 @@ func handlerWrapper(handler HandlerFunc) gin.HandlerFunc {
cc.AppId = c.GetString("appid") cc.AppId = c.GetString("appid")
cc.UserId = c.GetUint64("userid") cc.UserId = c.GetUint64("userid")
handler(cc) handler(cc)
return
} }
} }
} }
@ -113,8 +138,9 @@ func CC(c *gin.Context) *Context {
} }
func ApiHandler(c *gin.Context) Handler { func ApiHandler(c *gin.Context) Handler {
cc := CC(c) newc := &gin.Context{Request: nil, Keys: map[string]any{}}
return cc.ApiHandler() c.Handler()(newc)
return newc.Keys["handler"].(Handler)
} }
func DumpHandler(c *Context) error { func DumpHandler(c *Context) error {
@ -124,7 +150,7 @@ func DumpHandler(c *Context) error {
} }
func ErrorHandler(c *gin.Context, errcode int, errmsg string) { func ErrorHandler(c *gin.Context, errcode int, errmsg string) {
handler := handlerWrapper(func(c *Context) error { handler := handlerFuncWrapper(func(c *Context) error {
return c.RESULT_ERROR(errcode, errmsg) return c.RESULT_ERROR(errcode, errmsg)
}) })
handler(c) handler(c)

View File

@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"time" "time"
@ -18,6 +19,7 @@ import (
type Context struct { type Context struct {
*gin.Context *gin.Context
e *gin.Engine
AppId string //current request appid, must not be empty AppId string //current request appid, must not be empty
UserId uint64 //current request userid, maybe 0 if user unknown UserId uint64 //current request userid, maybe 0 if user unknown
@ -152,25 +154,26 @@ func (c *Context) RESULT(output interface{}) error {
c.recordMetric() c.recordMetric()
}(&output) }(&output)
if _, ok := t.FieldByName("ErrMsg"); !ok {
errstr := "Result MUST have 'ErrMsg' field"
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
c.shapeOutput(output)
c.errcode = reflect.ValueOf(output).FieldByName("ErrCode").GetInt()
c.IndentedJSON(c.httpstatus, output)
return errors.New(errstr)
}
if _, ok := t.FieldByName("ErrCode"); !ok { if _, ok := t.FieldByName("ErrCode"); !ok {
errstr := "Result MUST have 'ErrCode' field" errstr := "Result MUST have 'ErrCode' field"
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
c.shapeOutput(output) c.shapeOutput(output)
c.errcode = reflect.ValueOf(output).FieldByName("ErrCode").GetInt() c.errcode = int(reflect.ValueOf(output).FieldByName("ErrCode").Int())
c.IndentedJSON(c.httpstatus, output)
return errors.New(errstr)
}
if _, ok := t.FieldByName("ErrMsg"); !ok {
errstr := "Result MUST have 'ErrMsg' field"
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
c.shapeOutput(output)
c.errcode = int(reflect.ValueOf(output).FieldByName("ErrCode").Int())
c.IndentedJSON(c.httpstatus, output) c.IndentedJSON(c.httpstatus, output)
return errors.New(errstr) return errors.New(errstr)
} }
c.shapeOutput(output) c.shapeOutput(output)
c.errcode = reflect.ValueOf(output).FieldByName("ErrCode").GetInt() c.errcode = int(reflect.ValueOf(output).FieldByName("ErrCode").Int())
c.IndentedJSON(c.httpstatus, output) c.IndentedJSON(c.httpstatus, output)
return nil return nil
} }
@ -193,13 +196,7 @@ func (c *Context) RESULT_PARAMETER_ERROR(err string) error {
} }
func (c *Context) ApiHandler() Handler { func (c *Context) ApiHandler() Handler {
h := c.Context.Handler() return ApiHandler(c.Context)
hkey := fmt.Sprintf("%v", h)
if handler, ok := requestHandlerMapper[hkey]; ok {
return handler
}
log.Fatalf("api handler [%s] not found", hkey)
return nil
} }
func (c *Context) shapeOutput(o interface{}) { func (c *Context) shapeOutput(o interface{}) {
@ -225,10 +222,11 @@ func (c *Context) recordMetric() {
} }
var ( var (
h = c.ApiHandler() h = c.ApiHandler()
api = h.Name() api = h.ApiName()
cost = time.Now().Sub(c.starttime).Milliseconds()
appid = c.AppId appid = c.AppId
errcode = c.errcode costus = time.Now().Sub(c.starttime).Microseconds()
costms = float64(costus) / 1000.0
errcode = strconv.Itoa(c.errcode)
counter = metricApiCounter.WithLabelValues(api, errcode, appid) counter = metricApiCounter.WithLabelValues(api, errcode, appid)
summary = metricApiSummary.WithLabelValues(api, errcode, appid) summary = metricApiSummary.WithLabelValues(api, errcode, appid)
) )
@ -236,5 +234,5 @@ func (c *Context) recordMetric() {
// Metric 1. api request counter // Metric 1. api request counter
counter.Inc() counter.Inc()
// Metric 2. api request cost/latency // Metric 2. api request cost/latency
summary.Observe(cost) summary.Observe(costms)
} }

View File

@ -22,13 +22,13 @@ type metricExporterHandler struct {
promhttpHandler http.Handler promhttpHandler http.Handler
} }
func (m *metricExporterHandler) Name() string { func (m *metricExporterHandler) ApiName() string {
return m.apiName return m.name
} }
func (m *metricExporterHandler) HandlerFunc() HandlerFunc { func (m *metricExporterHandler) HandlerFunc() HandlerFunc {
return func(c *Context) (err error) { return func(c *Context) (err error) {
m.promhttpHandler.ServeHTTP(c.ResponseWriter, c.Request) m.promhttpHandler.ServeHTTP(c.Writer, c.Request)
return nil return nil
} }
} }
@ -50,8 +50,6 @@ func InitMetrics() {
[]string{"api", "errcode", "appid"}, []string{"api", "errcode", "appid"},
) )
metricRegistry = prometheus.NewRegistry() metricRegistry = prometheus.NewRegistry()
opt := promhttp.HandlerOpts{Registry: metricRegistry}
metricRegistry.MustRegister(metricApiCounter) metricRegistry.MustRegister(metricApiCounter)
metricRegistry.MustRegister(metricApiSummary) metricRegistry.MustRegister(metricApiSummary)
} }
@ -61,6 +59,7 @@ func SetupMetricsExporterHandler(apiname string) {
apiname = "premetheus_metrics_exporter" apiname = "premetheus_metrics_exporter"
} }
opt := promhttp.HandlerOpts{Registry: metricRegistry}
MetricExporterHandler = &metricExporterHandler{ MetricExporterHandler = &metricExporterHandler{
name: apiname, name: apiname,
promhttpHandler: promhttp.HandlerFor(metricRegistry, opt), promhttpHandler: promhttp.HandlerFor(metricRegistry, opt),

View File

@ -34,7 +34,7 @@ package api
// /////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////
type Handler interface { type Handler interface {
Name() string ApiName() string
HandlerFunc() HandlerFunc HandlerFunc() HandlerFunc
} }
type HandlerFunc func(*Context) error type HandlerFunc func(*Context) error