github.com/go-graphite/carbonapi@v0.17.0/expr/functions/highestLowest/function.go (about) 1 package highestLowest 2 3 import ( 4 "container/heap" 5 "context" 6 "fmt" 7 "math" 8 "strings" 9 10 "github.com/go-graphite/carbonapi/expr/consolidations" 11 "github.com/go-graphite/carbonapi/expr/helper" 12 "github.com/go-graphite/carbonapi/expr/interfaces" 13 "github.com/go-graphite/carbonapi/expr/types" 14 "github.com/go-graphite/carbonapi/pkg/parser" 15 ) 16 17 type highest struct{} 18 19 func GetOrder() interfaces.Order { 20 return interfaces.Any 21 } 22 23 func New(configFile string) []interfaces.FunctionMetadata { 24 res := make([]interfaces.FunctionMetadata, 0) 25 f := &highest{} 26 functions := []string{"highestAverage", "highestCurrent", "highestMax", "highestMin", "highest", "lowestMax", "lowestMin", "lowestAverage", "lowestCurrent", "lowest"} 27 for _, n := range functions { 28 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 29 } 30 return res 31 } 32 33 // highestAverage(seriesList, n) , highestCurrent(seriesList, n), highestMax(seriesList, n) 34 func (f *highest) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 35 arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 36 if err != nil { 37 return nil, err 38 } 39 40 n := 1 41 if e.ArgsLen() > 1 && e.Target() != "highest" && e.Target() != "lowest" { 42 n, err = e.GetIntArg(1) 43 if err != nil { 44 return nil, err 45 } 46 } 47 48 // we have fewer arguments than we want result series 49 if len(arg) < n { 50 return arg, nil 51 } 52 53 var mh types.MetricHeap 54 55 var compute func([]float64) float64 56 57 isHighest := strings.HasPrefix(e.Target(), "highest") 58 switch e.Target() { 59 case "highest", "lowest": 60 consolidation := "average" 61 switch e.ArgsLen() { 62 case 2: 63 n, err = e.GetIntArg(1) 64 if err != nil { 65 // We need to support case where only function specified 66 n = 1 67 consolidation, err = e.GetStringArgDefault(1, "average") 68 if err != nil { 69 return nil, err 70 } 71 } 72 case 3: 73 n, err = e.GetIntArg(1) 74 75 if err != nil { 76 return nil, err 77 } 78 consolidation, err = e.GetStringArgDefault(2, "average") 79 80 if err != nil { 81 return nil, err 82 } 83 } 84 var ok bool 85 compute, ok = consolidations.ConsolidationToFunc[consolidation] 86 if !ok { 87 return nil, fmt.Errorf("unsupported consolidation function %v", consolidation) 88 } 89 case "highestMax", "lowestMax": 90 compute = consolidations.MaxValue 91 case "highestAverage", "lowestAverage": 92 compute = consolidations.AvgValue 93 case "highestCurrent", "lowestCurrent": 94 compute = consolidations.CurrentValue 95 case "highestMin", "lowestMin": 96 compute = consolidations.MinValue 97 default: 98 return nil, fmt.Errorf("unsupported function %v", e.Target()) 99 } 100 101 var results []*types.MetricData 102 103 if n <= 0 { 104 return results, nil 105 } 106 107 if isHighest { 108 for i, a := range arg { 109 m := compute(a.Values) 110 if math.IsNaN(m) { 111 continue 112 } 113 114 if len(mh) < n { 115 heap.Push(&mh, types.MetricHeapElement{Idx: i, Val: m}) 116 continue 117 } 118 // m is bigger than smallest max found so far 119 if mh[0].Val < m { 120 mh[0].Val = m 121 mh[0].Idx = i 122 heap.Fix(&mh, 0) 123 } 124 } 125 126 results = make([]*types.MetricData, len(mh)) 127 128 // results should be ordered ascending 129 for len(mh) > 0 { 130 v := heap.Pop(&mh).(types.MetricHeapElement) 131 results[len(mh)] = arg[v.Idx] 132 } 133 } else { 134 for i, a := range arg { 135 m := compute(a.Values) 136 heap.Push(&mh, types.MetricHeapElement{Idx: i, Val: m}) 137 } 138 139 results = make([]*types.MetricData, n) 140 141 for i := 0; i < n; i++ { 142 v := heap.Pop(&mh).(types.MetricHeapElement) 143 results[i] = arg[v.Idx] 144 } 145 } 146 147 return results, nil 148 } 149 150 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 151 func (f *highest) Description() map[string]types.FunctionDescription { 152 return map[string]types.FunctionDescription{ 153 "highest": { 154 Name: "highest", 155 Function: "highest(seriesList, n=1, func='average')", 156 Description: "Takes one metric or a wildcard seriesList followed by an integer N and an aggregation function.\nOut of all metrics passed, draws only the N metrics with the highest aggregated value over the\ntime period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=highest(server*.instance*.threads.busy,5,'max')\n\nDraws the 5 servers with the highest number of busy threads.", 157 Module: "graphite.render.functions", 158 Group: "Filter Series", 159 Params: []types.FunctionParam{ 160 { 161 Name: "seriesList", 162 Type: types.SeriesList, 163 Required: true, 164 }, 165 { 166 Name: "n", 167 Type: types.Integer, 168 Required: true, 169 }, 170 { 171 Name: "func", 172 Type: types.String, 173 Default: &types.Suggestion{ 174 Type: types.SString, 175 Value: "average", 176 }, 177 Options: types.StringsToSuggestionList(consolidations.AvailableConsolidationFuncs()), 178 }, 179 }, 180 SeriesChange: true, // function aggregate metrics or change series items count 181 }, 182 "highestAverage": { 183 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the top N metrics with the highest\naverage value for the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=highestAverage(server*.instance*.threads.busy,5)\n\nDraws the top 5 servers with the highest average value.\n\nThis is an alias for :py:func:`highest <highest>` with aggregation ``average``.", 184 Function: "highestAverage(seriesList, n)", 185 Group: "Filter Series", 186 Module: "graphite.render.functions", 187 Name: "highestAverage", 188 Params: []types.FunctionParam{ 189 { 190 Name: "seriesList", 191 Required: true, 192 Type: types.SeriesList, 193 }, 194 { 195 Name: "n", 196 Required: true, 197 Type: types.Integer, 198 }, 199 }, 200 SeriesChange: true, // function aggregate metrics or change series items count 201 }, 202 "highestCurrent": { 203 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the N metrics with the highest value\nat the end of the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=highestCurrent(server*.instance*.threads.busy,5)\n\nDraws the 5 servers with the highest busy threads.\n\nThis is an alias for :py:func:`highest <highest>` with aggregation ``current``.", 204 Function: "highestCurrent(seriesList, n)", 205 Group: "Filter Series", 206 Module: "graphite.render.functions", 207 Name: "highestCurrent", 208 Params: []types.FunctionParam{ 209 { 210 Name: "seriesList", 211 Required: true, 212 Type: types.SeriesList, 213 }, 214 { 215 Name: "n", 216 Required: true, 217 Type: types.Integer, 218 }, 219 }, 220 SeriesChange: true, // function aggregate metrics or change series items count 221 }, 222 "highestMax": { 223 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\n\nOut of all metrics passed, draws only the N metrics with the highest maximum\nvalue in the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=highestMax(server*.instance*.threads.busy,5)\n\nDraws the top 5 servers who have had the most busy threads during the time\nperiod specified.\n\nThis is an alias for :py:func:`highest <highest>` with aggregation ``max``.", 224 Function: "highestMax(seriesList, n)", 225 Group: "Filter Series", 226 Module: "graphite.render.functions", 227 Name: "highestMax", 228 Params: []types.FunctionParam{ 229 { 230 Name: "seriesList", 231 Required: true, 232 Type: types.SeriesList, 233 }, 234 { 235 Name: "n", 236 Required: true, 237 Type: types.Integer, 238 }, 239 }, 240 SeriesChange: true, // function aggregate metrics or change series items count 241 }, 242 "highestMin": { 243 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\n\nOut of all metrics passed, draws only the N metrics with the highest minimum\nvalue in the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=highestMin(server*.instance*.threads.busy,5)\n\nDraws the top 5 servers who have had the highest minimum of all the values during the time\nperiod specified.\n\nThis is an alias for :py:func:`highest <highest>` with aggregation ``min``.", 244 Function: "highestMin(seriesList, n)", 245 Group: "Filter Series", 246 Module: "graphite.render.functions", 247 Name: "highestMin", 248 Params: []types.FunctionParam{ 249 { 250 Name: "seriesList", 251 Required: true, 252 Type: types.SeriesList, 253 }, 254 { 255 Name: "n", 256 Required: true, 257 Type: types.Integer, 258 }, 259 }, 260 SeriesChange: true, // function aggregate metrics or change series items count 261 }, 262 "lowest": { 263 Name: "lowest", 264 Function: "lowest(seriesList, n=1, func='average')", 265 Description: "Takes one metric or a wildcard seriesList followed by an integer N and an aggregation function.\nOut of all metrics passed, draws only the N metrics with the lowest aggregated value over the\ntime period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=lowest(server*.instance*.threads.busy,5,'min')\n\nDraws the 5 servers with the lowest number of busy threads.", 266 Module: "graphite.render.functions", 267 Group: "Filter Series", 268 Params: []types.FunctionParam{ 269 { 270 Name: "seriesList", 271 Type: types.SeriesList, 272 Required: true, 273 }, 274 { 275 Name: "n", 276 Type: types.Integer, 277 Required: true, 278 }, 279 { 280 Name: "func", 281 Type: types.String, 282 Default: &types.Suggestion{ 283 Type: types.SString, 284 Value: "average", 285 }, 286 Options: types.StringsToSuggestionList(consolidations.AvailableConsolidationFuncs()), 287 }, 288 }, 289 SeriesChange: true, // function aggregate metrics or change series items count 290 }, 291 "lowestCurrent": { 292 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the N metrics with the lowest value at\nthe end of the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=lowestCurrent(server*.instance*.threads.busy,5)\n\nDraws the 5 servers with the least busy threads right now.\n\nThis is an alias for :py:func:`lowest <lowest>` with aggregation ``current``.", 293 Function: "lowestCurrent(seriesList, n)", 294 Group: "Filter Series", 295 Module: "graphite.render.functions", 296 Name: "lowestCurrent", 297 Params: []types.FunctionParam{ 298 { 299 Name: "seriesList", 300 Required: true, 301 Type: types.SeriesList, 302 }, 303 { 304 Name: "n", 305 Required: true, 306 Type: types.Integer, 307 }, 308 }, 309 SeriesChange: true, // function aggregate metrics or change series items count 310 }, 311 "lowestAverage": { 312 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the bottom N metrics with the lowest\naverage value for the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=lowestAverage(server*.instance*.threads.busy,5)\n\nDraws the bottom 5 servers with the lowest average value.\n\nThis is an alias for :py:func:`lowest <lowest>` with aggregation ``average``.", 313 Function: "lowestAverage(seriesList, n)", 314 Group: "Filter Series", 315 Module: "graphite.render.functions", 316 Name: "lowestAverage", 317 Params: []types.FunctionParam{ 318 { 319 Name: "seriesList", 320 Required: true, 321 Type: types.SeriesList, 322 }, 323 { 324 Name: "n", 325 Required: true, 326 Type: types.Integer, 327 }, 328 }, 329 SeriesChange: true, // function aggregate metrics or change series items count 330 }, 331 "lowestMax": { 332 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the bottom N metrics with the lowest\nmaximum value for the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=lowestMax(server*.instance*.threads.busy,5)\n\nDraws the bottom 5 servers with the lowest maximum value.\n\nThis is an alias for :py:func:`lowest <lowest>` with aggregation ``max``.", 333 Function: "lowestMax(seriesList, n)", 334 Group: "Filter Series", 335 Module: "graphite.render.functions", 336 Name: "lowestMax", 337 Params: []types.FunctionParam{ 338 { 339 Name: "seriesList", 340 Required: true, 341 Type: types.SeriesList, 342 }, 343 { 344 Name: "n", 345 Required: true, 346 Type: types.Integer, 347 }, 348 }, 349 SeriesChange: true, // function aggregate metrics or change series items count 350 }, 351 "lowestMin": { 352 Description: "Takes one metric or a wildcard seriesList followed by an integer N.\nOut of all metrics passed, draws only the bottom N metrics with the lowest\nminimum value for the time period specified.\n\nExample:\n\n.. code-block:: none\n\n &target=lowestMin(server*.instance*.threads.busy,5)\n\nDraws the bottom 5 servers with the lowest minimum value.\n\nThis is an alias for :py:func:`lowest <lowest>` with aggregation ``min``.", 353 Function: "lowestMin(seriesList, n)", 354 Group: "Filter Series", 355 Module: "graphite.render.functions", 356 Name: "lowestMin", 357 Params: []types.FunctionParam{ 358 { 359 Name: "seriesList", 360 Required: true, 361 Type: types.SeriesList, 362 }, 363 { 364 Name: "n", 365 Required: true, 366 Type: types.Integer, 367 }, 368 }, 369 SeriesChange: true, // function aggregate metrics or change series items count 370 }, 371 } 372 }