chore: Add metric and status package. (#585)

This commit is contained in:
Bo-Yi Wu 2021-07-13 23:58:47 +08:00 committed by GitHub
parent 35e1998cc5
commit 2cd46ad690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 154 additions and 84 deletions

View File

@ -2,7 +2,6 @@ package gorush
import (
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/storage"
"github.com/appleboy/go-fcm"
"github.com/msalihkarakasli/go-hms-push/push/core"
@ -20,8 +19,6 @@ var (
FCMClient *fcm.Client
// HMSClient is Huawei push client
HMSClient *core.HMSClient
// StatStorage implements the storage interface
StatStorage storage.Storage
// MaxConcurrentIOSPushes pool to limit the number of concurrent iOS pushes
MaxConcurrentIOSPushes chan struct{}
)

View File

@ -8,6 +8,7 @@ import (
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/status"
)
func TestMain(m *testing.M) {
@ -21,12 +22,16 @@ func TestMain(m *testing.M) {
log.Fatal(err)
}
if err := status.InitAppStatus(PushConf); err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
wg := &sync.WaitGroup{}
wg.Add(int(PushConf.Core.WorkerNum))
InitWorkers(ctx, wg, PushConf.Core.WorkerNum, PushConf.Core.QueueNum)
if err := InitAppStatus(); err != nil {
if err := status.InitAppStatus(PushConf); err != nil {
log.Fatal(err)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/appleboy/gorush/core"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/status"
"github.com/mitchellh/mapstructure"
"github.com/sideshow/apns2"
@ -428,7 +429,7 @@ Retry:
}(logx.LogError, createLogPushEntry(core.FailedPush, token, req, err), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout)
}
StatStorage.AddIosError(1)
status.StatStorage.AddIosError(1)
// We should retry only "retryable" statuses. More info about response:
// https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns
if res != nil && res.StatusCode >= http.StatusInternalServerError {
@ -438,7 +439,7 @@ Retry:
if res != nil && res.Sent() {
logPush(core.SucceededPush, token, req, nil)
StatStorage.AddIosSuccess(1)
status.StatStorage.AddIosSuccess(1)
}
// free push slot
<-MaxConcurrentIOSPushes

View File

@ -12,6 +12,7 @@ import (
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/core"
"github.com/appleboy/gorush/status"
"github.com/buger/jsonparser"
"github.com/sideshow/apns2"
"github.com/stretchr/testify/assert"
@ -761,7 +762,7 @@ func TestPushToIOS(t *testing.T) {
PushConf.Ios.KeyPath = "../certificate/certificate-valid.pem"
err := InitAPNSClient()
assert.Nil(t, err)
err = InitAppStatus()
err = status.InitAppStatus(PushConf)
assert.Nil(t, err)
req := PushNotification{
@ -781,7 +782,7 @@ func TestApnsHostFromRequest(t *testing.T) {
PushConf.Ios.KeyPath = "../certificate/certificate-valid.pem"
err := InitAPNSClient()
assert.Nil(t, err)
err = InitAppStatus()
err = status.InitAppStatus(PushConf)
assert.Nil(t, err)
req := PushNotification{

View File

@ -7,6 +7,7 @@ import (
"github.com/appleboy/go-fcm"
"github.com/appleboy/gorush/core"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/status"
"github.com/sirupsen/logrus"
)
@ -154,7 +155,7 @@ Retry:
}
}(logx.LogError, createLogPushEntry(core.FailedPush, req.To, req, err), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout)
}
StatStorage.AddAndroidError(1)
status.StatStorage.AddAndroidError(1)
} else {
for _, token := range req.Tokens {
if PushConf.Core.Sync {
@ -168,7 +169,7 @@ Retry:
}(logx.LogError, createLogPushEntry(core.FailedPush, token, req, err), PushConf.Core.FeedbackURL, PushConf.Core.FeedbackTimeout)
}
}
StatStorage.AddAndroidError(int64(len(req.Tokens)))
status.StatStorage.AddAndroidError(int64(len(req.Tokens)))
}
return
}
@ -177,8 +178,8 @@ Retry:
logx.LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure))
}
StatStorage.AddAndroidSuccess(int64(res.Success))
StatStorage.AddAndroidError(int64(res.Failure))
status.StatStorage.AddAndroidSuccess(int64(res.Success))
status.StatStorage.AddAndroidError(int64(res.Failure))
var newTokens []string
// result from Send messages to specific devices

View File

@ -7,6 +7,7 @@ import (
"sync"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/status"
"github.com/msalihkarakasli/go-hms-push/push/config"
"github.com/msalihkarakasli/go-hms-push/push/core"
@ -206,11 +207,11 @@ Retry:
// Huawei Push Send API does not support exact results for each token
if res.Code == "80000000" {
StatStorage.AddHuaweiSuccess(int64(1))
status.StatStorage.AddHuaweiSuccess(int64(1))
logx.LogAccess.Debug("Huwaei Send Notification is completed successfully!")
} else {
isError = true
StatStorage.AddHuaweiError(int64(1))
status.StatStorage.AddHuaweiError(int64(1))
logx.LogAccess.Debug("Huawei Send Notification is failed! Code: " + res.Code)
}

View File

@ -7,6 +7,9 @@ import (
"net/http"
"os"
"github.com/appleboy/gorush/metric"
"github.com/appleboy/gorush/status"
api "github.com/appleboy/gin-status-api"
"github.com/appleboy/gorush/logx"
"github.com/gin-contrib/logger"
@ -17,6 +20,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/thoas/stats"
"golang.org/x/crypto/acme/autocert"
)
@ -24,7 +28,9 @@ var isTerm bool
func init() {
// Support metrics
m := NewMetrics()
m := metric.NewMetrics(func() int {
return len(QueueNotification)
})
prometheus.MustRegister(m)
isTerm = isatty.IsTerminal(os.Stdout.Fd())
}
@ -109,6 +115,36 @@ func metricsHandler(c *gin.Context) {
promhttp.Handler().ServeHTTP(c.Writer, c.Request)
}
func appStatusHandler(c *gin.Context) {
result := status.App{}
result.Version = GetVersion()
result.QueueMax = cap(QueueNotification)
result.QueueUsage = len(QueueNotification)
result.TotalCount = status.StatStorage.GetTotalCount()
result.Ios.PushSuccess = status.StatStorage.GetIosSuccess()
result.Ios.PushError = status.StatStorage.GetIosError()
result.Android.PushSuccess = status.StatStorage.GetAndroidSuccess()
result.Android.PushError = status.StatStorage.GetAndroidError()
result.Huawei.PushSuccess = status.StatStorage.GetHuaweiSuccess()
result.Huawei.PushError = status.StatStorage.GetHuaweiError()
c.JSON(http.StatusOK, result)
}
func sysStatsHandler(c *gin.Context) {
c.JSON(http.StatusOK, status.Stats.Data())
}
// StatMiddleware response time, status code count, etc.
func StatMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
beginning, recorder := status.Stats.Begin(c.Writer)
c.Next()
status.Stats.End(beginning, stats.WithRecorder(recorder))
}
}
func autoTLSServer() *http.Server {
m := autocert.Manager{
Prompt: autocert.AcceptTOS,

View File

@ -7,6 +7,7 @@ import (
"github.com/appleboy/gorush/core"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/status"
)
// InitWorkers for initialize all workers.
@ -105,7 +106,7 @@ func queueNotification(ctx context.Context, req RequestPush) (int, []logx.LogPus
wg.Wait()
}
StatStorage.AddTotalCount(int64(count))
status.StatStorage.AddTotalCount(int64(count))
return count, log
}

11
main.go
View File

@ -20,6 +20,7 @@ import (
"github.com/appleboy/gorush/gorush"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/rpc"
"github.com/appleboy/gorush/status"
"golang.org/x/sync/errgroup"
)
@ -212,7 +213,7 @@ func main() {
logx.LogError.Fatal(err)
}
if err := gorush.InitAppStatus(); err != nil {
if err := status.InitAppStatus(gorush.PushConf); err != nil {
return
}
@ -245,7 +246,7 @@ func main() {
logx.LogError.Fatal(err)
}
if err := gorush.InitAppStatus(); err != nil {
if err := status.InitAppStatus(gorush.PushConf); err != nil {
return
}
@ -282,7 +283,7 @@ func main() {
logx.LogError.Fatal(err)
}
if err := gorush.InitAppStatus(); err != nil {
if err := status.InitAppStatus(gorush.PushConf); err != nil {
return
}
@ -308,7 +309,7 @@ func main() {
logx.LogError.Fatal(err)
}
if err = gorush.InitAppStatus(); err != nil {
if err = status.InitAppStatus(gorush.PushConf); err != nil {
logx.LogError.Fatal(err)
}
@ -323,7 +324,7 @@ func main() {
close(finished)
// close the connection with storage
logx.LogAccess.Info("close the storage connection: ", gorush.PushConf.Stat.Engine)
if err := gorush.StatStorage.Close(); err != nil {
if err := status.StatStorage.Close(); err != nil {
logx.LogError.Fatal("can't close the storage connection: ", err.Error())
}
})

View File

@ -1,6 +1,8 @@
package gorush
package metric
import (
"github.com/appleboy/gorush/status"
"github.com/prometheus/client_golang/prometheus"
)
@ -17,11 +19,14 @@ type Metrics struct {
HuaweiSuccess *prometheus.Desc
HuaweiError *prometheus.Desc
QueueUsage *prometheus.Desc
GetQueueUsage func() int
}
var getGetQueueUsage = func() int { return 0 }
// NewMetrics returns a new Metrics with all prometheus.Desc initialized
func NewMetrics() Metrics {
return Metrics{
func NewMetrics(c ...func() int) Metrics {
m := Metrics{
TotalPushCount: prometheus.NewDesc(
namespace+"total_push_count",
"Number of push count",
@ -62,7 +67,14 @@ func NewMetrics() Metrics {
"Length of internal queue",
nil, nil,
),
GetQueueUsage: getGetQueueUsage,
}
if len(c) > 0 {
m.GetQueueUsage = c[0]
}
return m
}
// Describe returns all possible prometheus.Desc
@ -82,41 +94,41 @@ func (c Metrics) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
c.TotalPushCount,
prometheus.CounterValue,
float64(StatStorage.GetTotalCount()),
float64(status.StatStorage.GetTotalCount()),
)
ch <- prometheus.MustNewConstMetric(
c.IosSuccess,
prometheus.CounterValue,
float64(StatStorage.GetIosSuccess()),
float64(status.StatStorage.GetIosSuccess()),
)
ch <- prometheus.MustNewConstMetric(
c.IosError,
prometheus.CounterValue,
float64(StatStorage.GetIosError()),
float64(status.StatStorage.GetIosError()),
)
ch <- prometheus.MustNewConstMetric(
c.AndroidSuccess,
prometheus.CounterValue,
float64(StatStorage.GetAndroidSuccess()),
float64(status.StatStorage.GetAndroidSuccess()),
)
ch <- prometheus.MustNewConstMetric(
c.AndroidError,
prometheus.CounterValue,
float64(StatStorage.GetAndroidError()),
float64(status.StatStorage.GetAndroidError()),
)
ch <- prometheus.MustNewConstMetric(
c.HuaweiSuccess,
prometheus.CounterValue,
float64(StatStorage.GetHuaweiSuccess()),
float64(status.StatStorage.GetHuaweiSuccess()),
)
ch <- prometheus.MustNewConstMetric(
c.HuaweiError,
prometheus.CounterValue,
float64(StatStorage.GetHuaweiError()),
float64(status.StatStorage.GetHuaweiError()),
)
ch <- prometheus.MustNewConstMetric(
c.QueueUsage,
prometheus.GaugeValue,
float64(len(QueueNotification)),
float64(c.GetQueueUsage()),
)
}

15
metric/metrics_test.go Normal file
View File

@ -0,0 +1,15 @@
package metric
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewMetrics(t *testing.T) {
m := NewMetrics()
assert.Equal(t, 0, m.GetQueueUsage())
m = NewMetrics(func() int { return 1 })
assert.Equal(t, 1, m.GetQueueUsage())
}

View File

@ -1,10 +1,11 @@
package gorush
package status
import (
"errors"
"net/http"
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/storage"
"github.com/appleboy/gorush/storage/badger"
"github.com/appleboy/gorush/storage/boltdb"
"github.com/appleboy/gorush/storage/buntdb"
@ -12,15 +13,17 @@ import (
"github.com/appleboy/gorush/storage/memory"
"github.com/appleboy/gorush/storage/redis"
"github.com/gin-gonic/gin"
"github.com/thoas/stats"
)
// Stats provide response time, status code count, etc.
var Stats = stats.New()
// StatusApp is app status structure
type StatusApp struct {
// StatStorage implements the storage interface
var StatStorage storage.Storage
// App is status structure
type App struct {
Version string `json:"version"`
QueueMax int `json:"queue_max"`
QueueUsage int `json:"queue_usage"`
@ -49,21 +52,21 @@ type HuaweiStatus struct {
}
// InitAppStatus for initialize app status
func InitAppStatus() error {
logx.LogAccess.Info("Init App Status Engine as ", PushConf.Stat.Engine)
switch PushConf.Stat.Engine {
func InitAppStatus(conf config.ConfYaml) error {
logx.LogAccess.Info("Init App Status Engine as ", conf.Stat.Engine)
switch conf.Stat.Engine {
case "memory":
StatStorage = memory.New()
case "redis":
StatStorage = redis.New(PushConf)
StatStorage = redis.New(conf)
case "boltdb":
StatStorage = boltdb.New(PushConf)
StatStorage = boltdb.New(conf)
case "buntdb":
StatStorage = buntdb.New(PushConf)
StatStorage = buntdb.New(conf)
case "leveldb":
StatStorage = leveldb.New(PushConf)
StatStorage = leveldb.New(conf)
case "badger":
StatStorage = badger.New(PushConf)
StatStorage = badger.New(conf)
default:
logx.LogError.Error("storage error: can't find storage driver")
return errors.New("can't find storage driver")
@ -77,33 +80,3 @@ func InitAppStatus() error {
return nil
}
func appStatusHandler(c *gin.Context) {
result := StatusApp{}
result.Version = GetVersion()
result.QueueMax = cap(QueueNotification)
result.QueueUsage = len(QueueNotification)
result.TotalCount = StatStorage.GetTotalCount()
result.Ios.PushSuccess = StatStorage.GetIosSuccess()
result.Ios.PushError = StatStorage.GetIosError()
result.Android.PushSuccess = StatStorage.GetAndroidSuccess()
result.Android.PushError = StatStorage.GetAndroidError()
result.Huawei.PushSuccess = StatStorage.GetHuaweiSuccess()
result.Huawei.PushError = StatStorage.GetHuaweiError()
c.JSON(http.StatusOK, result)
}
func sysStatsHandler(c *gin.Context) {
c.JSON(http.StatusOK, Stats.Data())
}
// StatMiddleware response time, status code count, etc.
func StatMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
beginning, recorder := Stats.Begin(c.Writer)
c.Next()
Stats.End(beginning, stats.WithRecorder(recorder))
}
}

View File

@ -1,15 +1,34 @@
package gorush
package status
import (
"log"
"testing"
"time"
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/logx"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
PushConf, _ := config.LoadConf("")
if err := logx.InitLog(
PushConf.Log.AccessLevel,
PushConf.Log.AccessLog,
PushConf.Log.ErrorLevel,
PushConf.Log.ErrorLog,
); err != nil {
log.Fatal(err)
}
m.Run()
}
func TestStorageDriverExist(t *testing.T) {
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "Test"
err := InitAppStatus()
err := InitAppStatus(PushConf)
assert.Error(t, err)
}
@ -18,8 +37,9 @@ func TestStatForMemoryEngine(t *testing.T) {
time.Sleep(5 * time.Second)
var val int64
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "memory"
err := InitAppStatus()
err := InitAppStatus(PushConf)
assert.Nil(t, err)
StatStorage.AddTotalCount(100)
@ -41,28 +61,31 @@ func TestStatForMemoryEngine(t *testing.T) {
}
func TestRedisServerSuccess(t *testing.T) {
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "redis"
PushConf.Stat.Redis.Addr = "redis:6379"
err := InitAppStatus()
err := InitAppStatus(PushConf)
assert.NoError(t, err)
}
func TestRedisServerError(t *testing.T) {
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "redis"
PushConf.Stat.Redis.Addr = "redis:6370"
err := InitAppStatus()
err := InitAppStatus(PushConf)
assert.Error(t, err)
}
func TestStatForRedisEngine(t *testing.T) {
var val int64
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "redis"
PushConf.Stat.Redis.Addr = "redis:6379"
err := InitAppStatus()
err := InitAppStatus(PushConf)
assert.Nil(t, err)
StatStorage.Init()
@ -89,7 +112,8 @@ func TestStatForRedisEngine(t *testing.T) {
func TestDefaultEngine(t *testing.T) {
var val int64
// defaul engine as memory
err := InitAppStatus()
PushConf, _ := config.LoadConf("")
err := InitAppStatus(PushConf)
assert.Nil(t, err)
StatStorage.Reset()
@ -114,8 +138,10 @@ func TestDefaultEngine(t *testing.T) {
func TestStatForBoltDBEngine(t *testing.T) {
var val int64
PushConf, _ := config.LoadConf("")
PushConf.Stat.Engine = "boltdb"
InitAppStatus()
err := InitAppStatus(PushConf)
assert.Nil(t, err)
StatStorage.Reset()

View File

@ -4,7 +4,7 @@ import (
"sync/atomic"
)
// StatusApp is app status structure
// statApp is app status structure
type statApp struct {
TotalCount int64 `json:"total_count"`
Ios IosStatus `json:"ios"`