Compare commits
14 Commits
Author | Date | ||
---|---|---|---|
![]() |
98eac47127 | ||
![]() |
69b0fb05aa | ||
![]() |
ba9357e166 | ||
![]() |
9df516247c | ||
![]() |
93bdc425e5 | ||
![]() |
426a77f4cf | ||
![]() |
a098d141c2 | ||
![]() |
1d8453af9d | ||
![]() |
b70e9ff90d | ||
![]() |
8335b17e89 | ||
![]() |
900d2e9789 | ||
![]() |
92b821a12d | ||
![]() |
136178a1c1 | ||
![]() |
f978fa3f87 |
129
api.go
129
api.go
@ -1,6 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"qoobing.com/gomod/log"
|
"qoobing.com/gomod/log"
|
||||||
@ -10,7 +13,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
validate = validator.New()
|
validate = validator.New()
|
||||||
requestLogidGetter = defaultLogidGetter
|
requestLogidGetter = defaultLogidGetter
|
||||||
|
requestHandlerMapper = map[string]gin.HandlerFunc{}
|
||||||
errCodeUnknownError = -10000
|
errCodeUnknownError = -10000
|
||||||
|
errCodeParameterError = -20000
|
||||||
errCodeUnimplementApi = -20401
|
errCodeUnimplementApi = -20401
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,15 +38,74 @@ type BaseWithErrCodeOutput struct {
|
|||||||
ErrMsg string `json:"errMsg"`
|
ErrMsg string `json:"errMsg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEngine() *Engine {
|
func NewEngine(pre ...gin.HandlerFunc) *Engine {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
e := gin.New()
|
e := gin.New()
|
||||||
|
for _, m := range pre {
|
||||||
|
e.Use(m)
|
||||||
|
}
|
||||||
e.Use(middlewareMyApiEngine())
|
e.Use(middlewareMyApiEngine())
|
||||||
return &Engine{e}
|
return &Engine{e}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) POST(uri string, handler HandlerFunc) {
|
func (e *Engine) POST(uri string, handler Handler) {
|
||||||
e.Engine.POST(uri, handlerWrapper(handler))
|
h := handlerWrapper(handler)
|
||||||
|
log.Infof("api handler [%v] registor success", handler)
|
||||||
|
e.Engine.POST(uri, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) GET(uri string, handler Handler) {
|
||||||
|
h := handlerWrapper(handler)
|
||||||
|
log.Infof("api handler [%v] registor success", handler)
|
||||||
|
e.Engine.GET(uri, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handlerWrapper
|
||||||
|
func handlerWrapper(handler Handler) gin.HandlerFunc {
|
||||||
|
handlerFunc := handler.HandlerFunc()
|
||||||
|
wrappedHandlerFunc := 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hkey := fmt.Sprintf("%v", wrappedHandlerFunc)
|
||||||
|
requestHandlerMapper[hkey] = wrappedHandlerFunc
|
||||||
|
return wrappedHandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
} 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 {
|
||||||
|
defer cc.TryRecover()
|
||||||
|
cc.AppId = c.GetString("appid")
|
||||||
|
cc.UserId = c.GetUint64("userid")
|
||||||
|
handler(cc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// middlewareMyApiEngine myapi engine middleware
|
// middlewareMyApiEngine myapi engine middleware
|
||||||
@ -53,11 +117,16 @@ func middlewareMyApiEngine() gin.HandlerFunc {
|
|||||||
defer log.Cleanup()
|
defer log.Cleanup()
|
||||||
|
|
||||||
// Step 2. init *api.Context
|
// Step 2. init *api.Context
|
||||||
|
req := c.Request
|
||||||
|
log.DebugfWithDepth(1, "[%s|%s|%s]", req.Method, req.Host, req.RequestURI)
|
||||||
cc := New(c)
|
cc := New(c)
|
||||||
c.Set("cc", cc)
|
c.Set("cc", cc)
|
||||||
|
|
||||||
// Step 3. do request
|
// Step 3. do request
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
||||||
|
// Step 4. recorde metrics
|
||||||
|
cc.recordMetrics()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +141,57 @@ func defaultLogidGetter(c *gin.Context) (logid string) {
|
|||||||
panic("unreachable code")
|
panic("unreachable code")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CC(c *gin.Context) *Context {
|
||||||
|
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 {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiHandler(c *gin.Context) Handler {
|
||||||
|
h := c.Handler()
|
||||||
|
hkey := fmt.Sprintf("%v", h)
|
||||||
|
if _, ok := requestHandlerMapper[hkey]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newc := &gin.Context{
|
||||||
|
Request: nil,
|
||||||
|
Keys: map[string]any{},
|
||||||
|
}
|
||||||
|
h(newc)
|
||||||
|
return newc.Keys["handler"].(Handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecordMetrics(api, errcode, appid string, costms float64) {
|
||||||
|
var (
|
||||||
|
counter = metricApiCounter.WithLabelValues(api, errcode, appid)
|
||||||
|
summary = metricApiSummary.WithLabelValues(api, errcode, appid)
|
||||||
|
)
|
||||||
|
// Metric 1. api request counter
|
||||||
|
counter.Inc()
|
||||||
|
// Metric 2. api request cost/latency
|
||||||
|
summary.Observe(costms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DumpHandlerFunc(c *Context) error {
|
||||||
|
errmsg := "api not implemented"
|
||||||
|
errcode := errCodeUnimplementApi
|
||||||
|
return c.RESULT_ERROR(errcode, errmsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorHandler(c *gin.Context, errcode int, errmsg string) {
|
||||||
|
handler := handlerFuncWrapper(func(c *Context) error {
|
||||||
|
return c.RESULT_ERROR(errcode, errmsg)
|
||||||
|
})
|
||||||
|
handler(c)
|
||||||
|
}
|
||||||
|
|
||||||
func InitErrCodes(errcodes map[string]int) {
|
func InitErrCodes(errcodes map[string]int) {
|
||||||
if code, ok := errcodes["ErrCodeUnknownError"]; ok {
|
if code, ok := errcodes["ErrCodeUnknownError"]; ok {
|
||||||
errCodeUnknownError = code
|
errCodeUnknownError = code
|
||||||
@ -79,4 +199,7 @@ func InitErrCodes(errcodes map[string]int) {
|
|||||||
if code, ok := errcodes["ErrCodeUnimplementApi"]; ok {
|
if code, ok := errcodes["ErrCodeUnimplementApi"]; ok {
|
||||||
errCodeUnknownError = code
|
errCodeUnknownError = code
|
||||||
}
|
}
|
||||||
|
if code, ok := errcodes["errCodeParameterError"]; ok {
|
||||||
|
errCodeParameterError = code
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
87
context.go
87
context.go
@ -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
|
||||||
@ -25,6 +27,8 @@ type Context struct {
|
|||||||
resultentry string
|
resultentry string
|
||||||
httpstatus int
|
httpstatus int
|
||||||
starttime time.Time
|
starttime time.Time
|
||||||
|
output interface{}
|
||||||
|
errcode int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(c *gin.Context) *Context {
|
func New(c *gin.Context) *Context {
|
||||||
@ -56,9 +60,6 @@ func (c *Context) TryRecover() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) BindInput(i interface{}) (err error) {
|
func (c *Context) BindInput(i interface{}) (err error) {
|
||||||
req := c.Request
|
|
||||||
log.DebugfWithDepth(1, "[%s|%s|%s]", req.Method, req.Host, req.RequestURI)
|
|
||||||
|
|
||||||
logstr := fmt.Sprintf("input:[have not initialized]")
|
logstr := fmt.Sprintf("input:[have not initialized]")
|
||||||
defer func(plogstr *string) {
|
defer func(plogstr *string) {
|
||||||
log.DebugfWithDepth(2, "%s", *plogstr)
|
log.DebugfWithDepth(2, "%s", *plogstr)
|
||||||
@ -89,6 +90,29 @@ func (c *Context) BindInput(i interface{}) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) BindOutput(o interface{}) (err error) {
|
||||||
|
var v = reflect.ValueOf(o)
|
||||||
|
if v.Kind() != reflect.Pointer {
|
||||||
|
return fmt.Errorf("output is NOT a pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
var e = v.Elem()
|
||||||
|
var t = e.Type()
|
||||||
|
if _, ok := t.FieldByName("ErrCode"); !ok {
|
||||||
|
return fmt.Errorf("out have no field ErrCode")
|
||||||
|
}
|
||||||
|
if _, ok := t.FieldByName("ErrMsg"); !ok {
|
||||||
|
return fmt.Errorf("out have no field ErrMsg")
|
||||||
|
}
|
||||||
|
if code := e.FieldByName("ErrCode"); !code.CanSet() {
|
||||||
|
return fmt.Errorf("output.ErrCode can not be set, " +
|
||||||
|
"output is NOT a pointer or output have no field 'ErrCode'")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.output = o
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) SetCookie(cookie *http.Cookie) {
|
func (c *Context) SetCookie(cookie *http.Cookie) {
|
||||||
if cookie.Path == "" {
|
if cookie.Path == "" {
|
||||||
cookie.Path = "/"
|
cookie.Path = "/"
|
||||||
@ -96,7 +120,14 @@ func (c *Context) SetCookie(cookie *http.Cookie) {
|
|||||||
http.SetCookie(c.Writer, cookie)
|
http.SetCookie(c.Writer, cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) AlreadyHaveResult() (done bool, entry string) {
|
||||||
|
return c.resultentry != "", c.resultentry
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) RESULT(output interface{}) error {
|
func (c *Context) RESULT(output interface{}) error {
|
||||||
|
if c.resultentry == "" {
|
||||||
|
c.resultentry = "RESULT"
|
||||||
|
}
|
||||||
var t = reflect.TypeOf(output)
|
var t = reflect.TypeOf(output)
|
||||||
defer func(o *interface{}) {
|
defer func(o *interface{}) {
|
||||||
b, err := json.Marshal(o)
|
b, err := json.Marshal(o)
|
||||||
@ -130,15 +161,23 @@ func (c *Context) RESULT(output interface{}) error {
|
|||||||
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.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.FieldByName("ErrMsg"); !ok {
|
if _, ok := t.FieldByName("ErrMsg"); !ok {
|
||||||
errstr := "Result MUST have 'ErrMsg' field"
|
errstr := "Result MUST have 'ErrMsg' field"
|
||||||
output = BaseWithErrCodeOutput{errCodeUnknownError, errstr}
|
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.errcode = int(reflect.ValueOf(output).FieldByName("ErrCode").Int())
|
||||||
c.IndentedJSON(c.httpstatus, output)
|
c.IndentedJSON(c.httpstatus, output)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -149,6 +188,7 @@ func (c *Context) RESULT_ERROR(eno int, err string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := BaseWithErrCodeOutput{eno, err}
|
result := BaseWithErrCodeOutput{eno, err}
|
||||||
|
c.errcode = eno
|
||||||
return c.RESULT(result)
|
return c.RESULT(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,5 +196,44 @@ func (c *Context) RESULT_PARAMETER_ERROR(err string) error {
|
|||||||
if c.resultentry == "" {
|
if c.resultentry == "" {
|
||||||
c.resultentry = "RESULT_PARAMETER_ERROR"
|
c.resultentry = "RESULT_PARAMETER_ERROR"
|
||||||
}
|
}
|
||||||
return c.RESULT_ERROR(errCodeUnknownError, err)
|
return c.RESULT_ERROR(errCodeParameterError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) ApiHandler() Handler {
|
||||||
|
return ApiHandler(c.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) shapeOutput(o interface{}) {
|
||||||
|
if c.output == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var out, ok = o.(BaseWithErrCodeOutput)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var e = reflect.ValueOf(c.output).Elem()
|
||||||
|
e.FieldByName("ErrMsg").SetString(out.ErrMsg)
|
||||||
|
e.FieldByName("ErrCode").SetInt(int64(out.ErrCode))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) recordMetrics() {
|
||||||
|
if metricApiCounter == nil ||
|
||||||
|
metricApiSummary == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var h = c.ApiHandler()
|
||||||
|
if h == nil {
|
||||||
|
h = unimplementHandler{c.Context}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
api = h.ApiName()
|
||||||
|
appid = c.AppId
|
||||||
|
costus = time.Now().Sub(c.starttime).Microseconds()
|
||||||
|
costms = float64(costus) / 1000.0
|
||||||
|
errcode = strconv.Itoa(c.errcode)
|
||||||
|
)
|
||||||
|
RecordMetrics(api, errcode, appid, costms)
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module qoobing.com/gomod/api
|
module qoobing.com/gomod/api
|
||||||
|
|
||||||
go 1.16
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
|
102
prometheus.go
Normal file
102
prometheus.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/push"
|
||||||
|
"qoobing.com/gomod/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MetricExporterPusher = func() error { return nil }
|
||||||
|
MetricExporterHandler Handler = nil
|
||||||
|
MetricRegistry *prometheus.Registry = nil
|
||||||
|
metricApiCounter *prometheus.CounterVec = nil
|
||||||
|
metricApiSummary *prometheus.SummaryVec = nil
|
||||||
|
instance string = "--unknown--"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metricExporterHandler struct {
|
||||||
|
name string
|
||||||
|
promhttpHandler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricExporterHandler) ApiName() string {
|
||||||
|
return m.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricExporterHandler) HandlerFunc() HandlerFunc {
|
||||||
|
return func(c *Context) (err error) {
|
||||||
|
m.promhttpHandler.ServeHTTP(c.Writer, c.Request)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpMetricExporterPusher() error {
|
||||||
|
return errors.New("not initilized, forgot call 'SetupMetricsExporterPusher'?")
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitMetrics(instanceValue string) {
|
||||||
|
if instanceValue == "" {
|
||||||
|
instance = getInstanceIpAddress()
|
||||||
|
} else if ap := strings.Split(instanceValue, ":"); len(ap) == 2 &&
|
||||||
|
ap[0] == "0.0.0.0" || ap[0] == "" {
|
||||||
|
instance = getInstanceIpAddress() + ":" + ap[1]
|
||||||
|
}
|
||||||
|
log.Infof("self instance: [%s]", instance)
|
||||||
|
|
||||||
|
metricApiCounter = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "api_requests_total",
|
||||||
|
Help: "How many HTTP requests processed",
|
||||||
|
//ConstLabels: prometheus.Labels{"instance": instance},
|
||||||
|
},
|
||||||
|
[]string{"api", "errcode", "appid"},
|
||||||
|
)
|
||||||
|
metricApiSummary = prometheus.NewSummaryVec(
|
||||||
|
prometheus.SummaryOpts{
|
||||||
|
Name: "api_requests_summary",
|
||||||
|
Help: "The api request summary of cost.",
|
||||||
|
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||||
|
//ConstLabels: prometheus.Labels{"instance": instance},
|
||||||
|
},
|
||||||
|
[]string{"api", "errcode", "appid"},
|
||||||
|
)
|
||||||
|
|
||||||
|
MetricRegistry = prometheus.NewRegistry()
|
||||||
|
MetricRegistry.MustRegister(metricApiCounter)
|
||||||
|
MetricRegistry.MustRegister(metricApiSummary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupMetricsExporterHandler(apiname string) {
|
||||||
|
if apiname == "" {
|
||||||
|
apiname = "premetheus_metrics_exporter"
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := promhttp.HandlerOpts{Registry: MetricRegistry}
|
||||||
|
MetricExporterHandler = &metricExporterHandler{
|
||||||
|
name: apiname,
|
||||||
|
promhttpHandler: promhttp.HandlerFor(MetricRegistry, opt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetupMetricsExporterPusher(pushgateway string, jobname string) {
|
||||||
|
if jobname == "" {
|
||||||
|
jobname = fmt.Sprintf("api-metrics-job-%s", getExeFilename())
|
||||||
|
}
|
||||||
|
|
||||||
|
var pusher = push.
|
||||||
|
New(pushgateway, jobname).
|
||||||
|
Grouping("instance", instance).
|
||||||
|
Gatherer(MetricRegistry)
|
||||||
|
|
||||||
|
MetricExporterPusher = func() error {
|
||||||
|
RecordMetrics("push_metrics_to_prometheus", "0", "selfmonitoring", 1.0)
|
||||||
|
return pusher.Push()
|
||||||
|
}
|
||||||
|
}
|
78
routes.go
78
routes.go
@ -1,49 +1,65 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"qoobing.com/gomod/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type routes struct {
|
// ///////////////////////////////////////////////////////////////////
|
||||||
gin.IRoutes
|
// IRouter & IRoutes in gin, we redefine it simple
|
||||||
}
|
// ///////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// // IRouter defines all router handle interface
|
||||||
|
// // includes single and group router.
|
||||||
|
// type IRouter interface {
|
||||||
|
// IRoutes
|
||||||
|
// Group(string, ...HandlerFunc) *RouterGroup
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // IRoutes defines all router handle interface.
|
||||||
|
// type IRoutes interface {
|
||||||
|
// Use(...HandlerFunc) IRoutes
|
||||||
|
//
|
||||||
|
// Handle(string, string, ...HandlerFunc) IRoutes
|
||||||
|
// Any(string, ...HandlerFunc) IRoutes
|
||||||
|
// GET(string, ...HandlerFunc) IRoutes
|
||||||
|
// POST(string, ...HandlerFunc) IRoutes
|
||||||
|
// DELETE(string, ...HandlerFunc) IRoutes
|
||||||
|
// PATCH(string, ...HandlerFunc) IRoutes
|
||||||
|
// PUT(string, ...HandlerFunc) IRoutes
|
||||||
|
// OPTIONS(string, ...HandlerFunc) IRoutes
|
||||||
|
// HEAD(string, ...HandlerFunc) IRoutes
|
||||||
|
//
|
||||||
|
// StaticFile(string, string) IRoutes
|
||||||
|
// StaticFileFS(string, string, http.FileSystem) IRoutes
|
||||||
|
// Static(string, string) IRoutes
|
||||||
|
// StaticFS(string, http.FileSystem) IRoutes
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ///////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
ApiName() string
|
||||||
|
HandlerFunc() HandlerFunc
|
||||||
|
}
|
||||||
type HandlerFunc func(*Context) error
|
type HandlerFunc func(*Context) error
|
||||||
|
|
||||||
type IRoutes interface {
|
type IRoutes interface {
|
||||||
POST(uri string, handler HandlerFunc)
|
GET(uri string, handler Handler)
|
||||||
|
POST(uri string, handler Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r routes) POST(uri string, handler HandlerFunc) {
|
type unimplementHandler struct {
|
||||||
r.IRoutes.POST(uri, handlerWrapper(handler))
|
c *gin.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func DumpHandler(c *Context) error {
|
func (u unimplementHandler) ApiName() string {
|
||||||
return c.RESULT_ERROR(errCodeUnimplementApi, "api not implemented")
|
req := u.c.Request
|
||||||
|
return strings.ToLower(fmt.Sprintf("[%s]%s", req.Method, req.RequestURI))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrorHandler(c *gin.Context, errcode int, errmsg string) {
|
func (u unimplementHandler) HandlerFunc() HandlerFunc {
|
||||||
handler := handlerWrapper(func(c *Context) error {
|
return DumpHandlerFunc
|
||||||
return c.RESULT_ERROR(errcode, errmsg)
|
|
||||||
})
|
|
||||||
handler(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlerWrapper(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")
|
|
||||||
} 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 {
|
|
||||||
defer cc.TryRecover()
|
|
||||||
cc.AppId = c.GetString("appid")
|
|
||||||
cc.UserId = c.GetUint64("userid")
|
|
||||||
handler(cc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
70
util.go
Normal file
70
util.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getExeFilename
|
||||||
|
func getExeFilename() string {
|
||||||
|
_, logfilename := filepath.Split(os.Args[0])
|
||||||
|
if logfilename == "" {
|
||||||
|
panic("get exe filename failed")
|
||||||
|
}
|
||||||
|
return logfilename
|
||||||
|
}
|
||||||
|
|
||||||
|
// preferencesMatch
|
||||||
|
func preferencesMatch(s string, preferences ...string) bool {
|
||||||
|
for _, pstr := range preferences {
|
||||||
|
reg := regexp.MustCompile(pstr)
|
||||||
|
if reg.Match([]byte(s)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInstanceIpAddress
|
||||||
|
func getInstanceIpAddress(preferences ...string) string {
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("getInstanceIpAddress failed: [%s]", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
prefer := func() func(string) bool {
|
||||||
|
if len(preferences) <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func(ip string) bool {
|
||||||
|
return preferencesMatch(ip, preferences...)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var ipnets = []*net.IPNet{}
|
||||||
|
for _, address := range addrs {
|
||||||
|
if ipnet, ok := address.(*net.IPNet); !ok {
|
||||||
|
continue
|
||||||
|
} else if ipnet.IP.IsLoopback() {
|
||||||
|
continue
|
||||||
|
} else if ipv4 := ipnet.IP.To4(); ipv4 == nil {
|
||||||
|
continue
|
||||||
|
} else if prefer == nil {
|
||||||
|
var n = *ipnet
|
||||||
|
ipnets = append(ipnets, &n)
|
||||||
|
continue
|
||||||
|
} else if prefer != nil && prefer(ipv4.String()) {
|
||||||
|
return ipv4.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: sort & return first
|
||||||
|
if len(ipnets) > 0 {
|
||||||
|
return ipnets[0].IP.To4().String()
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("getInstanceIpAddreass failed,"+
|
||||||
|
"preferences=[%v], ipnets=[%v]", preferences, ipnets))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user