#1 fix & opt.

- opt.: interval must <= 10s
- fix: speed wrong push unit
- fix: wrong segments count
- fix: threshold: parse endIdx
This commit is contained in:
lollipopkit 2023-07-15 19:31:38 +08:00
parent 9c6953c178
commit 5913686cec
11 changed files with 68 additions and 79 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
.config
.config
server_box_monitor

View File

@ -72,8 +72,10 @@
//
// type: webhook, ios, serverchan (more to come)
// iface: interface for the push type
// success_body_regex: regex to match the response body
// success_code: response code to match
// body_regex: regex to match the response body
// code: response code to match
//
// {{key}} and {{value}} will be replaced with the key and value of the check
"pushes": [
{
// This is a example for QQ Group message
@ -113,14 +115,9 @@
"name": "ServerBox iOS App",
// You can get it from settings page of ServerBox iOS app
"token": "YOUR_TOKEN",
// {{key}} and {{value}} will be replaced with the key and value of the check
"title": "Server Notification",
// {{key}} and {{value}} will be replaced with the key and value of the check
"content": "{{key}}: {{value}}",
// Regex to match the response body
// If the response body matches the regex, the push is considered successful
"body_regex": ".*",
// If the response code equals, the push is considered successful
"code": 200
}
},

View File

@ -67,10 +67,11 @@
}
],
// 推送规则
//
// 类型 type: webhook, ios, server酱 (以后有更多)
// 接口 iface: 推送类型的接口
// body成功正则 body_regex: 正则表达式匹配响应体
// 成功code code: 响应码匹配
// body_regex: 正则表达式匹配响应体
// code: 响应码匹配
"pushes": [
{
// 这是一个推送到QQ群的例子

View File

@ -16,9 +16,9 @@ var (
type AppConfig struct {
Version int `json:"version"`
// Such as "30s" or "5m".
// Valid time units are "s", "m", "h".
// Values less than 10 seconds are not allowed.
// Such as "7s".
// Valid time units are "s".
// Values bigger than 10 seconds are not allowed.
Interval string `json:"interval"`
Rules []Rule `json:"rules"`
Pushes []Push `json:"pushes"`
@ -61,13 +61,13 @@ func GetInterval() time.Duration {
}
d, err := time.ParseDuration(ac.Interval)
if err == nil {
if d < res.DefaultInterval {
log.Warn("[CONFIG] interval is too short, use default interval: 1m")
if d > res.DefaultInterval {
log.Warn("[CONFIG] interval is too long, use default interval")
return res.DefaultInterval
}
return d
}
log.Warn("[CONFIG] parse interval failed: %v, use default interval: 1m", err)
log.Warn("[CONFIG] parse interval failed: %v", err)
return res.DefaultInterval
}

View File

@ -280,7 +280,7 @@ func (r *Rule) shouldNotifyNetwork(s []networkStatus, t *Threshold) (bool, *Push
}
return ok, &PushPair{
Key: r.Matcher,
Value: speed.String(),
Value: speed.String()+"/s",
}, nil
case ThresholdTypeSize:
size := Size(0)

View File

@ -4,15 +4,12 @@ import (
"fmt"
"strconv"
"strings"
"time"
)
const programKilo float64 = 1024
var (
sizeSuffix = []string{"b", "k", "m", "g", "t"}
zeroSpeed = Speed{0, time.Second}
)
// Size is a type that represents a size in bytes.
@ -26,6 +23,7 @@ func (s Size) String() string {
return fmt.Sprintf("%.1f %s", temp, sizeSuffix[nth])
}
temp = temp / programKilo
nth++
}
}
func ParseToSize(s string) (Size, error) {
@ -52,42 +50,3 @@ func ParseToSize(s string) (Size, error) {
}
return Size(temp), nil
}
type Speed struct {
Size
Time time.Duration
}
func (s *Speed) String() (string, error) {
if s.Time == 0 {
return "", fmt.Errorf("time equals zero: %#v", s)
}
return fmt.Sprintf("%s/s", Size(float64(s.Size)/s.Time.Seconds()).String()), nil
}
func (s *Speed) Compare(other *Speed) (int, error) {
if s.Time == 0 || other.Time == 0 {
return 0, fmt.Errorf("time equals zero: %#v, %#v", s, other)
}
return int(float64(s.Size)/s.Time.Seconds() - float64(other.Size)/other.Time.Seconds()), nil
}
func ParseToSpeed(s string) (*Speed, error) {
s = strings.ToLower(s)
if s == "0" {
return &zeroSpeed, nil
}
splited := strings.Split(s, "/")
if len(splited) != 2 {
return nil, fmt.Errorf("invalid speed: %s", s)
}
size, err := ParseToSize(splited[0])
if err != nil {
return nil, err
}
time, err := time.ParseDuration(splited[1])
if err != nil {
return nil, err
}
return &Speed{size, time}, nil
}

24
model/size_test.go Normal file
View File

@ -0,0 +1,24 @@
package model_test
import (
"testing"
"github.com/lollipopkit/server_box_monitor/model"
)
func TestParseToSize(t *testing.T) {
_parseSize("1m", model.Size(1024*1024), t)
_parseSize("1M", model.Size(1024*1024), t)
_parseSize("3k", model.Size(3*1024), t)
_parseSize("7b", model.Size(7), t)
}
func _parseSize(s string, expect model.Size, t *testing.T) {
size, err := model.ParseToSize(s)
if err != nil {
t.Error(err)
}
if size != expect {
t.Errorf("expect %s, got %s", expect, size)
}
}

View File

@ -146,8 +146,8 @@ func ParseStatus(s string) error {
segments[i] = strings.TrimSpace(segments[i])
segments[i] = strings.Trim(segments[i], "\n")
}
if len(segments) != 10 {
return ErrInvalidShellOutput
if len(segments) != 7 {
return errors.Join(ErrInvalidShellOutput, fmt.Errorf("expect 7 segments, but got %d", len(segments)))
}
err := parseNetworkStatus(segments[1])
if err != nil {

View File

@ -32,13 +32,13 @@ func ParseToThreshold(s string) (*Threshold, error) {
} else if strings.HasSuffix(s, "/s") { // 10m/s
thresholdType = ThresholdTypeSpeed
// 10m/s -> m/s -> 3
endIdx = runesLen - 3
// 10m/s -> "/s" -> 2
endIdx = runesLen - 2
} else if util.Contains(sizeSuffix, string(runes[runesLen-1])) { // 10m
// 10m -> m -> 1
// 10m -> "" -> 0
thresholdType = ThresholdTypeSize
endIdx = runesLen - 1
endIdx = runesLen
} else if strings.HasSuffix(s, "c") { // 32c
thresholdType = ThresholdTypeTemperature
@ -68,10 +68,23 @@ func ParseToThreshold(s string) (*Threshold, error) {
startIdx = 0
}
value, err := strconv.ParseFloat(string(runes[startIdx:endIdx]), 64)
if err != nil {
return nil, err
var value float64
var err error
switch thresholdType {
case ThresholdTypeSize, ThresholdTypeSpeed:
size, err := ParseToSize(string(runes[startIdx:endIdx]))
if err != nil {
return nil, err
}
value = float64(size)
default:
value, err = strconv.ParseFloat(string(runes[startIdx:endIdx]), 64)
if err != nil {
return nil, err
}
}
return &Threshold{
ThresholdType: thresholdType,
Value: value,

View File

@ -30,7 +30,7 @@ var (
)
const (
DefaultInterval = time.Second * 30
DefaultInterval = time.Second * 7
)
func init() {

View File

@ -17,8 +17,6 @@ import (
var (
pushPairs = []*model.PushPair{}
pushPairsLock = new(sync.RWMutex)
lastPushTime time.Time
checkInterval = time.Second * 3
)
func init() {
@ -35,20 +33,20 @@ func init() {
}
func Start() {
go runMonitor()
go runWeb()
go runCheck()
// 阻塞主线程
select {}
}
func runMonitor() {
func runCheck() {
err := model.ReadAppConfig()
if err != nil {
log.Err("[CONFIG] Read app config error: %v", err)
panic(err)
}
for range time.NewTicker(checkInterval).C {
for range time.NewTicker(model.GetInterval()).C {
err = model.RefreshStatus()
status := model.Status
if err != nil {
@ -59,7 +57,7 @@ func runMonitor() {
for _, rule := range model.Config.Rules {
notify, pushPair, err := rule.ShouldNotify(status)
if err != nil {
if !strings.Contains(err.Error(), "not ready") {
if !strings.Contains(err.Error(), model.ErrNotReady.Error()) {
log.Warn("[RULE] %s error: %v", rule.Id(), err)
}
}
@ -75,10 +73,6 @@ func runMonitor() {
continue
}
if time.Now().Sub(lastPushTime) < model.GetInterval() {
continue
}
log.Info("[PUSH] %d to push", len(pushPairs))
pushPairsLock.RLock()