diff --git a/etc/template/wecom.tpl b/etc/template/wecom.tpl
index 5a16cd61..5a901511 100644
--- a/etc/template/wecom.tpl
+++ b/etc/template/wecom.tpl
@@ -1,7 +1,9 @@
**级别状态**: {{if .IsRecovered}}S{{.Severity}} Recovered{{else}}S{{.Severity}} Triggered{{end}}
**规则标题**: {{.RuleName}}{{if .RuleNote}}
-**规则备注**: {{.RuleNote}}{{end}}
-**监控指标**: {{.TagsJSON}}
-{{if .IsRecovered}}**恢复时间**:{{timeformat .LastEvalTime}}{{else}}**触发时间**: {{timeformat .TriggerTime}}
+**规则备注**: {{.RuleNote}}{{end}}{{if .TargetIdent}}
+**监控对象**: {{.TargetIdent}}{{end}}
+**监控指标**: {{.TagsJSON}}{{if not .IsRecovered}}
**触发时值**: {{.TriggerValue}}{{end}}
+{{if .IsRecovered}}**恢复时间**: {{timeformat .LastEvalTime}}{{else}}**首次触发时间**: {{timeformat .FirstTriggerTime}}{{end}}
+{{$time_duration := sub now.Unix .FirstTriggerTime }}{{if .IsRecovered}}{{$time_duration = sub .LastEvalTime .FirstTriggerTime }}{{end}}**持续时长**: {{humanizeDurationInterface $time_duration}}
**发送时间**: {{timestamp}}
\ No newline at end of file
diff --git a/src/pkg/tplx/common.go b/src/pkg/tplx/common.go
index bf81e9c5..68950f64 100644
--- a/src/pkg/tplx/common.go
+++ b/src/pkg/tplx/common.go
@@ -4,6 +4,7 @@ import (
"fmt"
"html/template"
"math"
+ "reflect"
"regexp"
"strconv"
"time"
@@ -33,6 +34,10 @@ func Timestamp(pattern ...string) string {
return time.Now().Format(defp)
}
+func Now() time.Time {
+ return time.Now()
+}
+
func Args(args ...interface{}) map[string]interface{} {
result := make(map[string]interface{})
for i, a := range args {
@@ -95,11 +100,27 @@ func Humanize1024(s string) string {
return fmt.Sprintf("%.4g%s", v, prefix)
}
+func ToString(v interface{}) string {
+ return fmt.Sprint(v)
+}
+
func HumanizeDuration(s string) string {
v, err := strconv.ParseFloat(s, 64)
if err != nil {
return s
}
+ return HumanizeDurationFloat64(v)
+}
+
+func HumanizeDurationInterface(i interface{}) string {
+ f, err := ToFloat64(i)
+ if err != nil {
+ return ToString(i)
+ }
+ return HumanizeDurationFloat64(f)
+}
+
+func HumanizeDurationFloat64(v float64) string {
if math.IsNaN(v) || math.IsInf(v, 0) {
return fmt.Sprintf("%.4g", v)
}
@@ -155,3 +176,179 @@ func HumanizePercentageH(s string) string {
}
return fmt.Sprintf("%.2f%%", v)
}
+
+// Add returns the sum of a and b.
+func Add(a, b interface{}) (interface{}, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Int() + bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Int() + int64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Int()) + bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int64(av.Uint()) + bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Uint() + bv.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Uint()) + bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Float32, reflect.Float64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Float() + float64(bv.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Float() + float64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return av.Float() + bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("add: unknown type for %q (%T)", bv, b)
+ }
+ default:
+ return nil, fmt.Errorf("add: unknown type for %q (%T)", av, a)
+ }
+}
+
+// Subtract returns the difference of b from a.
+func Subtract(a, b interface{}) (interface{}, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Int() - bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Int() - int64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Int()) - bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int64(av.Uint()) - bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Uint() - bv.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Uint()) - bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Float32, reflect.Float64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Float() - float64(bv.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Float() - float64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return av.Float() - bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("subtract: unknown type for %q (%T)", bv, b)
+ }
+ default:
+ return nil, fmt.Errorf("subtract: unknown type for %q (%T)", av, a)
+ }
+}
+
+// Multiply returns the product of a and b.
+func Multiply(a, b interface{}) (interface{}, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Int() * bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Int() * int64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Int()) * bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int64(av.Uint()) * bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Uint() * bv.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Uint()) * bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Float32, reflect.Float64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Float() * float64(bv.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Float() * float64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return av.Float() * bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("multiply: unknown type for %q (%T)", bv, b)
+ }
+ default:
+ return nil, fmt.Errorf("multiply: unknown type for %q (%T)", av, a)
+ }
+}
+
+// Divide returns the division of b from a.
+func Divide(a, b interface{}) (interface{}, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Int() / bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Int() / int64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Int()) / bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return int64(av.Uint()) / bv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Uint() / bv.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return float64(av.Uint()) / bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
+ }
+ case reflect.Float32, reflect.Float64:
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return av.Float() / float64(bv.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return av.Float() / float64(bv.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return av.Float() / bv.Float(), nil
+ default:
+ return nil, fmt.Errorf("divide: unknown type for %q (%T)", bv, b)
+ }
+ default:
+ return nil, fmt.Errorf("divide: unknown type for %q (%T)", av, a)
+ }
+}
diff --git a/src/pkg/tplx/conv.go b/src/pkg/tplx/conv.go
new file mode 100644
index 00000000..552706c4
--- /dev/null
+++ b/src/pkg/tplx/conv.go
@@ -0,0 +1,73 @@
+package tplx
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// ToFloat64 convert interface to float64
+func ToFloat64(val interface{}) (float64, error) {
+ switch v := val.(type) {
+ case string:
+ if f, err := strconv.ParseFloat(v, 64); err == nil {
+ return f, nil
+ }
+
+ // try int
+ if i, err := strconv.ParseInt(v, 0, 64); err == nil {
+ return float64(i), nil
+ }
+
+ // try bool
+ b, err := strconv.ParseBool(v)
+ if err == nil {
+ if b {
+ return 1, nil
+ } else {
+ return 0, nil
+ }
+ }
+
+ if v == "Yes" || v == "yes" || v == "YES" || v == "Y" || v == "ON" || v == "on" || v == "On" || v == "ok" || v == "up" {
+ return 1, nil
+ }
+
+ if v == "No" || v == "no" || v == "NO" || v == "N" || v == "OFF" || v == "off" || v == "Off" || v == "fail" || v == "err" || v == "down" {
+ return 0, nil
+ }
+
+ return 0, fmt.Errorf("unparseable value %v", v)
+ case float64:
+ return v, nil
+ case uint64:
+ return float64(v), nil
+ case uint32:
+ return float64(v), nil
+ case uint16:
+ return float64(v), nil
+ case uint8:
+ return float64(v), nil
+ case uint:
+ return float64(v), nil
+ case int64:
+ return float64(v), nil
+ case int32:
+ return float64(v), nil
+ case int16:
+ return float64(v), nil
+ case int8:
+ return float64(v), nil
+ case bool:
+ if v {
+ return 1, nil
+ } else {
+ return 0, nil
+ }
+ case int:
+ return float64(v), nil
+ case float32:
+ return float64(v), nil
+ default:
+ return strconv.ParseFloat(fmt.Sprint(v), 64)
+ }
+}
diff --git a/src/pkg/tplx/tplx.go b/src/pkg/tplx/tplx.go
index 240840ee..2c494dbd 100644
--- a/src/pkg/tplx/tplx.go
+++ b/src/pkg/tplx/tplx.go
@@ -8,20 +8,27 @@ import (
)
var TemplateFuncMap = template.FuncMap{
- "escape": url.PathEscape,
- "unescaped": Unescaped,
- "urlconvert": Urlconvert,
- "timeformat": Timeformat,
- "timestamp": Timestamp,
- "args": Args,
- "reReplaceAll": ReReplaceAll,
- "match": regexp.MatchString,
- "toUpper": strings.ToUpper,
- "toLower": strings.ToLower,
- "contains": strings.Contains,
- "humanize": Humanize,
- "humanize1024": Humanize1024,
- "humanizeDuration": HumanizeDuration,
- "humanizePercentage": HumanizePercentage,
- "humanizePercentageH": HumanizePercentageH,
+ "escape": url.PathEscape,
+ "unescaped": Unescaped,
+ "urlconvert": Urlconvert,
+ "timeformat": Timeformat,
+ "timestamp": Timestamp,
+ "args": Args,
+ "reReplaceAll": ReReplaceAll,
+ "match": regexp.MatchString,
+ "toUpper": strings.ToUpper,
+ "toLower": strings.ToLower,
+ "contains": strings.Contains,
+ "humanize": Humanize,
+ "humanize1024": Humanize1024,
+ "humanizeDuration": HumanizeDuration,
+ "humanizeDurationInterface": HumanizeDurationInterface,
+ "humanizePercentage": HumanizePercentage,
+ "humanizePercentageH": HumanizePercentageH,
+ "add": Add,
+ "sub": Subtract,
+ "mul": Multiply,
+ "div": Divide,
+ "now": Now,
+ "toString": ToString,
}