diff --git a/api.go b/api.go index 5fc7872..b55ccf0 100644 --- a/api.go +++ b/api.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "reflect" "github.com/gin-gonic/gin" @@ -46,15 +45,40 @@ func NewEngine() *Engine { } func (e *Engine) POST(uri string, handler Handler) { - h := handlerWrapper(handler.HandlerFunc()) - hkey := fmt.Sprintf("%v", h) - requestHandlerMapper[hkey] = handler - log.Infof("api handler [%s] registor success", hkey) + h := handlerWrapper(handler) + log.Infof("api handler [%v] registor success", handler) e.Engine.POST(uri, h) } // 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) { if icc, ok := c.Get("cc"); !ok { 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.UserId = c.GetUint64("userid") handler(cc) + return } } } @@ -113,8 +138,9 @@ func CC(c *gin.Context) *Context { } func ApiHandler(c *gin.Context) Handler { - cc := CC(c) - return cc.ApiHandler() + newc := &gin.Context{Request: nil, Keys: map[string]any{}} + c.Handler()(newc) + return newc.Keys["handler"].(Handler) } func DumpHandler(c *Context) error { @@ -124,7 +150,7 @@ func DumpHandler(c *Context) error { } 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) }) handler(c) diff --git a/context.go b/context.go index f14065c..ba46f20 100644 --- a/context.go +++ b/context.go @@ -7,6 +7,7 @@ import ( "net/http" "reflect" "runtime/debug" + "strconv" "strings" "time" @@ -18,6 +19,7 @@ import ( type Context struct { *gin.Context + e *gin.Engine AppId string //current request appid, must not be empty UserId uint64 //current request userid, maybe 0 if user unknown @@ -152,25 +154,26 @@ func (c *Context) RESULT(output interface{}) error { c.recordMetric() }(&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 { errstr := "Result MUST have 'ErrCode' field" output = BaseWithErrCodeOutput{errCodeUnknownError, errstr} 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) return errors.New(errstr) } 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 nil } @@ -193,13 +196,7 @@ func (c *Context) RESULT_PARAMETER_ERROR(err string) error { } func (c *Context) ApiHandler() Handler { - h := c.Context.Handler() - hkey := fmt.Sprintf("%v", h) - if handler, ok := requestHandlerMapper[hkey]; ok { - return handler - } - log.Fatalf("api handler [%s] not found", hkey) - return nil + return ApiHandler(c.Context) } func (c *Context) shapeOutput(o interface{}) { @@ -225,10 +222,11 @@ func (c *Context) recordMetric() { } var ( h = c.ApiHandler() - api = h.Name() - cost = time.Now().Sub(c.starttime).Milliseconds() + api = h.ApiName() 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) summary = metricApiSummary.WithLabelValues(api, errcode, appid) ) @@ -236,5 +234,5 @@ func (c *Context) recordMetric() { // Metric 1. api request counter counter.Inc() // Metric 2. api request cost/latency - summary.Observe(cost) + summary.Observe(costms) } diff --git a/prometheus.go b/prometheus.go index 5c7af3e..59ffc98 100644 --- a/prometheus.go +++ b/prometheus.go @@ -22,13 +22,13 @@ type metricExporterHandler struct { promhttpHandler http.Handler } -func (m *metricExporterHandler) Name() string { - return m.apiName +func (m *metricExporterHandler) ApiName() string { + return m.name } func (m *metricExporterHandler) HandlerFunc() HandlerFunc { return func(c *Context) (err error) { - m.promhttpHandler.ServeHTTP(c.ResponseWriter, c.Request) + m.promhttpHandler.ServeHTTP(c.Writer, c.Request) return nil } } @@ -50,8 +50,6 @@ func InitMetrics() { []string{"api", "errcode", "appid"}, ) metricRegistry = prometheus.NewRegistry() - - opt := promhttp.HandlerOpts{Registry: metricRegistry} metricRegistry.MustRegister(metricApiCounter) metricRegistry.MustRegister(metricApiSummary) } @@ -61,6 +59,7 @@ func SetupMetricsExporterHandler(apiname string) { apiname = "premetheus_metrics_exporter" } + opt := promhttp.HandlerOpts{Registry: metricRegistry} MetricExporterHandler = &metricExporterHandler{ name: apiname, promhttpHandler: promhttp.HandlerFor(metricRegistry, opt), diff --git a/routes.go b/routes.go index ce67880..90eb6fa 100644 --- a/routes.go +++ b/routes.go @@ -34,7 +34,7 @@ package api // /////////////////////////////////////////////////////////////////// type Handler interface { - Name() string + ApiName() string HandlerFunc() HandlerFunc } type HandlerFunc func(*Context) error