reducer.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package conditions
  15. import (
  16. "math"
  17. "sort"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/pkg/utils"
  20. "yunion.io/x/onecloud/pkg/apis/monitor"
  21. "yunion.io/x/onecloud/pkg/monitor/validators"
  22. )
  23. type Reducer interface {
  24. Reduce(series *monitor.TimeSeries) (*float64, []string)
  25. GetType() monitor.ReducerType
  26. GetParams() []float64
  27. }
  28. // queryReducer reduces an timeseries to a float
  29. type queryReducer struct {
  30. // Type is how the timeseries should be reduced.
  31. // Ex: avg, sum, max, min, count
  32. Type monitor.ReducerType
  33. Params []float64
  34. }
  35. func (s *queryReducer) GetParams() []float64 {
  36. return s.Params
  37. }
  38. func (s *queryReducer) GetType() monitor.ReducerType {
  39. return s.Type
  40. }
  41. func getRoundFloat(point monitor.TimePoint) float64 {
  42. pv := point.Value()
  43. if pv < 1 {
  44. return pv
  45. }
  46. return math.Trunc(pv)
  47. }
  48. func (s *queryReducer) Reduce(series *monitor.TimeSeries) (*float64, []string) {
  49. if len(series.Points) == 0 {
  50. return nil, nil
  51. }
  52. value := float64(0)
  53. allNull := true
  54. valArr := make([]string, 0)
  55. switch s.Type {
  56. case monitor.REDUCER_AVG:
  57. validPointsCount := 0
  58. for _, point := range series.Points {
  59. if point.IsValid() {
  60. value += point.Value()
  61. validPointsCount++
  62. allNull = false
  63. }
  64. }
  65. if validPointsCount > 0 {
  66. value = value / float64(validPointsCount)
  67. }
  68. case monitor.REDUCER_SUM:
  69. for _, point := range series.Points {
  70. if point.IsValid() {
  71. value += point.Value()
  72. allNull = false
  73. }
  74. }
  75. case monitor.REDUCER_MIN:
  76. value = math.MaxFloat64
  77. for _, point := range series.Points {
  78. if point.IsValid() {
  79. allNull = false
  80. pv := getRoundFloat(point)
  81. if value > pv {
  82. value = pv
  83. }
  84. }
  85. }
  86. case monitor.REDUCER_MAX:
  87. value = -math.MaxFloat64
  88. for _, point := range series.Points {
  89. if point.IsValid() {
  90. allNull = false
  91. pv := getRoundFloat(point)
  92. if value < pv {
  93. value = pv
  94. }
  95. }
  96. }
  97. case monitor.REDUCER_COUNT:
  98. value = float64(len(series.Points))
  99. allNull = false
  100. case monitor.REDUCER_LAST:
  101. points := series.Points
  102. for i := len(points) - 1; i >= 0; i-- {
  103. if points[i].IsValid() {
  104. value = points[i].Value()
  105. valArr = points[i].PointValueStr()
  106. allNull = false
  107. break
  108. }
  109. }
  110. case monitor.REDUCER_MEDIAN:
  111. var values []float64
  112. for _, v := range series.Points {
  113. if v.IsValid() {
  114. allNull = false
  115. values = append(values, v.Value())
  116. }
  117. }
  118. if len(values) >= 1 {
  119. sort.Float64s(values)
  120. length := len(values)
  121. if length%2 == 1 {
  122. value = values[(length-1)/2]
  123. } else {
  124. value = (values[(length/2)-1] + values[length/2]) / 2
  125. }
  126. }
  127. case monitor.REDUCER_DIFF:
  128. allNull, value = calculateDiff(series, allNull, value, diff)
  129. case monitor.REDUCER_DELTA:
  130. allNull, value = calculateDelta(series, allNull, value)
  131. case monitor.REDUCER_PERCENT_DIFF:
  132. allNull, value = calculateDiff(series, allNull, value, percentDiff)
  133. case monitor.REDUCER_COUNT_NON_NULL:
  134. for _, v := range series.Points {
  135. if v.IsValid() {
  136. value++
  137. }
  138. }
  139. if value > 0 {
  140. allNull = false
  141. }
  142. case monitor.REDUCER_PERCENTILE:
  143. var values []float64
  144. for _, v := range series.Points {
  145. if v.IsValid() {
  146. allNull = false
  147. values = append(values, v.Value())
  148. }
  149. }
  150. pNum := float64(95)
  151. if len(s.Params) != 0 {
  152. pNum = s.Params[0]
  153. }
  154. if len(values) >= 1 {
  155. sort.Float64s(values)
  156. length := len(values)
  157. index := math.Floor(float64(length) * pNum / float64(100))
  158. value = values[int64(index)]
  159. }
  160. }
  161. if allNull {
  162. return nil, nil
  163. }
  164. return &value, valArr
  165. }
  166. func newSimpleReducer(cond *monitor.Condition) *queryReducer {
  167. return &queryReducer{
  168. Type: monitor.ReducerType(cond.Type),
  169. Params: cond.Params,
  170. }
  171. }
  172. func newSimpleReducerByType(typ string) *queryReducer {
  173. return &queryReducer{
  174. Type: monitor.ReducerType(typ),
  175. Params: []float64{},
  176. }
  177. }
  178. func calculateDiff(series *monitor.TimeSeries, allNull bool, value float64, fn func(float64, float64) float64) (bool, float64) {
  179. var (
  180. points = series.Points
  181. first float64
  182. i int
  183. )
  184. // get the newest point
  185. for i = len(points) - 1; i >= 0; i-- {
  186. if points[i].IsValid() {
  187. allNull = false
  188. first = points[i].Value()
  189. break
  190. }
  191. }
  192. if i >= 1 {
  193. // get the oldest point
  194. for i := 0; i < len(points); i++ {
  195. if points[i].IsValid() {
  196. allNull = false
  197. val := fn(first, points[i].Value())
  198. value = math.Abs(val)
  199. break
  200. }
  201. }
  202. }
  203. return allNull, value
  204. }
  205. var diff = func(newest, oldest float64) float64 {
  206. return newest - oldest
  207. }
  208. var percentDiff = func(newest, oldest float64) float64 {
  209. return (newest - oldest) / oldest * 100
  210. }
  211. func calculateDelta(series *monitor.TimeSeries, allNull bool, value float64) (bool, float64) {
  212. var (
  213. points = series.Points
  214. first float64
  215. i int
  216. )
  217. // get the newest point
  218. for i = len(points) - 1; i >= 0; i-- {
  219. if points[i].IsValid() {
  220. allNull = false
  221. first = points[i].Value()
  222. break
  223. }
  224. }
  225. if i >= 1 {
  226. // get the oldest point
  227. for i := 0; i < len(points); i++ {
  228. if points[i].IsValid() {
  229. allNull = false
  230. value = first - points[i].Value()
  231. break
  232. }
  233. }
  234. }
  235. return allNull, value
  236. }
  237. func NewAlertReducer(cond *monitor.Condition) (Reducer, error) {
  238. if len(cond.Operators) == 0 {
  239. return newSimpleReducer(cond), nil
  240. }
  241. if utils.IsInStringArray(cond.Operators[0], validators.CommonAlertReducerFieldOpts) {
  242. return newMathReducer(cond)
  243. }
  244. return nil, errors.Wrapf(errors.Error("reducer operator is illegal"), "operator: %s", cond.Operators[0])
  245. }