Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
e34bf9a1
Commit
e34bf9a1
authored
Nov 17, 2016
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'utkarshcmu-or_alerting'
parents
98d1748e
62e8a039
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
142 additions
and
22 deletions
+142
-22
docs/sources/alerting/rules.md
+4
-1
pkg/api/alerting.go
+2
-1
pkg/api/dtos/alerting.go
+6
-5
pkg/services/alerting/conditions/query.go
+7
-1
pkg/services/alerting/eval_context.go
+1
-1
pkg/services/alerting/eval_handler.go
+18
-5
pkg/services/alerting/eval_handler_test.go
+88
-3
pkg/services/alerting/interfaces.go
+1
-0
public/app/features/alerting/alert_def.ts
+6
-0
public/app/features/alerting/alert_tab_ctrl.ts
+4
-0
public/app/features/alerting/partials/alert_tab.html
+5
-5
No files found.
docs/sources/alerting/rules.md
View file @
e34bf9a1
...
...
@@ -55,7 +55,10 @@ Currently the only condition type that exists is a `Query` condition that allows
specify a query letter, time range and an aggregation function. The letter refers to
a query you already have added in the
**Metrics**
tab. The result from the query and the aggregation function is
a single value that is then used in the threshold check. The query used in an alert rule cannot
contain any template variables. Currently we only support
`AND`
operator between conditions.
contain any template variables. Currently we only support
`AND`
and
`OR`
operators between conditions and they are executed serially.
For example, we have 3 conditions in the following order:
`condition:A(evaluates to: TRUE) OR condition:B(evaluates to: FALSE) AND condition:C(evaluates to: TRUE)`
so the result will be calculated as ((TRUE OR FALSE) AND TRUE) = TRUE.
We plan to add other condition types in the future, like
`Other Alert`
, where you can include the state
of another alert in your conditions, and
`Time Of Day`
.
...
...
pkg/api/alerting.go
View file @
e34bf9a1
...
...
@@ -119,7 +119,8 @@ func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
res
:=
backendCmd
.
Result
dtoRes
:=
&
dtos
.
AlertTestResult
{
Firing
:
res
.
Firing
,
Firing
:
res
.
Firing
,
ConditionEvals
:
res
.
ConditionEvals
,
}
if
res
.
Error
!=
nil
{
...
...
pkg/api/dtos/alerting.go
View file @
e34bf9a1
...
...
@@ -35,11 +35,12 @@ type AlertTestCommand struct {
}
type
AlertTestResult
struct
{
Firing
bool
`json:"firing"`
TimeMs
string
`json:"timeMs"`
Error
string
`json:"error,omitempty"`
EvalMatches
[]
*
EvalMatch
`json:"matches,omitempty"`
Logs
[]
*
AlertTestResultLog
`json:"logs,omitempty"`
Firing
bool
`json:"firing"`
ConditionEvals
string
`json:"conditionEvals"`
TimeMs
string
`json:"timeMs"`
Error
string
`json:"error,omitempty"`
EvalMatches
[]
*
EvalMatch
`json:"matches,omitempty"`
Logs
[]
*
AlertTestResultLog
`json:"logs,omitempty"`
}
type
AlertTestResultLog
struct
{
...
...
pkg/services/alerting/conditions/query.go
View file @
e34bf9a1
...
...
@@ -23,6 +23,7 @@ type QueryCondition struct {
Query
AlertQuery
Reducer
QueryReducer
Evaluator
AlertEvaluator
Operator
string
HandleRequest
tsdb
.
HandleRequestFunc
}
...
...
@@ -72,6 +73,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.Conditio
return
&
alerting
.
ConditionResult
{
Firing
:
evalMatchCount
>
0
,
NoDataFound
:
emptySerieCount
==
len
(
seriesList
),
Operator
:
c
.
Operator
,
EvalMatches
:
matches
,
},
nil
}
...
...
@@ -168,8 +170,12 @@ func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, erro
if
err
!=
nil
{
return
nil
,
err
}
condition
.
Evaluator
=
evaluator
operatorJson
:=
model
.
Get
(
"operator"
)
operator
:=
operatorJson
.
Get
(
"type"
)
.
MustString
(
"and"
)
condition
.
Operator
=
operator
return
&
condition
,
nil
}
...
...
pkg/services/alerting/eval_context.go
View file @
e34bf9a1
...
...
@@ -17,7 +17,7 @@ type EvalContext struct {
EvalMatches
[]
*
EvalMatch
Logs
[]
*
ResultLogEntry
Error
error
Description
string
ConditionEvals
string
StartTime
time
.
Time
EndTime
time
.
Time
Rule
*
Rule
...
...
pkg/services/alerting/eval_handler.go
View file @
e34bf9a1
package
alerting
import
(
"strconv"
"strings"
"time"
"github.com/grafana/grafana/pkg/log"
...
...
@@ -21,7 +23,10 @@ func NewEvalHandler() *DefaultEvalHandler {
func
(
e
*
DefaultEvalHandler
)
Eval
(
context
*
EvalContext
)
{
firing
:=
true
for
_
,
condition
:=
range
context
.
Rule
.
Conditions
{
conditionEvals
:=
""
for
i
:=
0
;
i
<
len
(
context
.
Rule
.
Conditions
);
i
++
{
condition
:=
context
.
Rule
.
Conditions
[
i
]
cr
,
err
:=
condition
.
Eval
(
context
)
if
err
!=
nil
{
context
.
Error
=
err
...
...
@@ -32,15 +37,23 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) {
break
}
// break if result has not triggered yet
if
cr
.
Firing
==
false
{
firing
=
false
break
// calculating Firing based on operator
if
cr
.
Operator
==
"or"
{
firing
=
firing
||
cr
.
Firing
}
else
{
firing
=
firing
&&
cr
.
Firing
}
if
i
>
0
{
conditionEvals
=
"["
+
conditionEvals
+
" "
+
strings
.
ToUpper
(
cr
.
Operator
)
+
" "
+
strconv
.
FormatBool
(
cr
.
Firing
)
+
"]"
}
else
{
conditionEvals
=
strconv
.
FormatBool
(
firing
)
}
context
.
EvalMatches
=
append
(
context
.
EvalMatches
,
cr
.
EvalMatches
...
)
}
context
.
ConditionEvals
=
conditionEvals
+
" = "
+
strconv
.
FormatBool
(
firing
)
context
.
Firing
=
firing
context
.
EndTime
=
time
.
Now
()
elapsedTime
:=
context
.
EndTime
.
Sub
(
context
.
StartTime
)
/
time
.
Millisecond
...
...
pkg/services/alerting/eval_handler_test.go
View file @
e34bf9a1
...
...
@@ -8,12 +8,13 @@ import (
)
type
conditionStub
struct
{
firing
bool
matches
[]
*
EvalMatch
firing
bool
operator
string
matches
[]
*
EvalMatch
}
func
(
c
*
conditionStub
)
Eval
(
context
*
EvalContext
)
(
*
ConditionResult
,
error
)
{
return
&
ConditionResult
{
Firing
:
c
.
firing
,
EvalMatches
:
c
.
matches
},
nil
return
&
ConditionResult
{
Firing
:
c
.
firing
,
EvalMatches
:
c
.
matches
,
Operator
:
c
.
operator
},
nil
}
func
TestAlertingExecutor
(
t
*
testing
.
T
)
{
...
...
@@ -29,6 +30,7 @@ func TestAlertingExecutor(t *testing.T) {
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
true
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"true = true"
)
})
Convey
(
"Show return false with not passing asdf"
,
func
()
{
...
...
@@ -41,6 +43,89 @@ func TestAlertingExecutor(t *testing.T) {
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
false
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[true AND false] = false"
)
})
Convey
(
"Show return true if any of the condition is passing with OR operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"or"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
true
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[true OR false] = true"
)
})
Convey
(
"Show return false if any of the condition is failing with AND operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"and"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
false
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[true AND false] = false"
)
})
Convey
(
"Show return true if one condition is failing with nested OR operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"or"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
true
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[[true AND true] OR false] = true"
)
})
Convey
(
"Show return false if one condition is passing with nested OR operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"or"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
false
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[[true AND false] OR false] = false"
)
})
Convey
(
"Show return false if a condition is failing with nested AND operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"and"
},
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
false
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[[true AND false] AND true] = false"
)
})
Convey
(
"Show return true if a condition is passing with nested OR operator"
,
func
()
{
context
:=
NewEvalContext
(
context
.
TODO
(),
&
Rule
{
Conditions
:
[]
Condition
{
&
conditionStub
{
firing
:
true
,
operator
:
"and"
},
&
conditionStub
{
firing
:
false
,
operator
:
"or"
},
&
conditionStub
{
firing
:
true
,
operator
:
"or"
},
},
})
handler
.
Eval
(
context
)
So
(
context
.
Firing
,
ShouldEqual
,
true
)
So
(
context
.
ConditionEvals
,
ShouldEqual
,
"[[true OR false] OR true] = true"
)
})
})
}
pkg/services/alerting/interfaces.go
View file @
e34bf9a1
...
...
@@ -24,6 +24,7 @@ type Notifier interface {
type
ConditionResult
struct
{
Firing
bool
NoDataFound
bool
Operator
string
EvalMatches
[]
*
EvalMatch
}
...
...
public/app/features/alerting/alert_def.ts
View file @
e34bf9a1
...
...
@@ -28,6 +28,11 @@ var evalFunctions = [
{
text
:
'HAS NO VALUE'
,
value
:
'no_value'
}
];
var
evalOperators
=
[
{
text
:
'OR'
,
value
:
'or'
},
{
text
:
'AND'
,
value
:
'and'
},
];
var
reducerTypes
=
[
{
text
:
'avg()'
,
value
:
'avg'
},
{
text
:
'min()'
,
value
:
'min'
},
...
...
@@ -116,6 +121,7 @@ export default {
getStateDisplayModel
:
getStateDisplayModel
,
conditionTypes
:
conditionTypes
,
evalFunctions
:
evalFunctions
,
evalOperators
:
evalOperators
,
noDataModes
:
noDataModes
,
executionErrorModes
:
executionErrorModes
,
reducerTypes
:
reducerTypes
,
...
...
public/app/features/alerting/alert_tab_ctrl.ts
View file @
e34bf9a1
...
...
@@ -18,6 +18,7 @@ export class AlertTabCtrl {
alert
:
any
;
conditionModels
:
any
;
evalFunctions
:
any
;
evalOperators
:
any
;
noDataModes
:
any
;
executionErrorModes
:
any
;
addNotificationSegment
;
...
...
@@ -41,6 +42,7 @@ export class AlertTabCtrl {
this
.
$scope
.
ctrl
=
this
;
this
.
subTabIndex
=
0
;
this
.
evalFunctions
=
alertDef
.
evalFunctions
;
this
.
evalOperators
=
alertDef
.
evalOperators
;
this
.
conditionTypes
=
alertDef
.
conditionTypes
;
this
.
noDataModes
=
alertDef
.
noDataModes
;
this
.
executionErrorModes
=
alertDef
.
executionErrorModes
;
...
...
@@ -194,6 +196,7 @@ export class AlertTabCtrl {
query
:
{
params
:
[
'A'
,
'5m'
,
'now'
]},
reducer
:
{
type
:
'avg'
,
params
:
[]},
evaluator
:
{
type
:
'gt'
,
params
:
[
null
]},
operator
:
{
type
:
'and'
},
};
}
...
...
@@ -250,6 +253,7 @@ export class AlertTabCtrl {
cm
.
queryPart
=
new
QueryPart
(
source
.
query
,
alertDef
.
alertQueryDef
);
cm
.
reducerPart
=
alertDef
.
createReducerPart
(
source
.
reducer
);
cm
.
evaluator
=
source
.
evaluator
;
cm
.
operator
=
source
.
operator
;
return
cm
;
}
...
...
public/app/features/alerting/partials/alert_tab.html
View file @
e34bf9a1
...
...
@@ -38,23 +38,23 @@
<h5
class=
"section-heading"
>
Conditions
</h5>
<div
class=
"gf-form-inline"
ng-repeat=
"conditionModel in ctrl.conditionModels"
>
<div
class=
"gf-form"
>
<
span
class=
"gf-form-label query-keyword width-5"
ng-if=
"$index"
>
AND
</span
>
<
metric-segment-model
css-class=
"query-keyword width-5"
ng-if=
"$index"
property=
"conditionModel.operator.type"
options=
"ctrl.evalOperators"
custom=
"false"
></metric-segment-model
>
<span
class=
"gf-form-label query-keyword width-5"
ng-if=
"$index===0"
>
WHEN
</span>
</div>
<div
class=
"gf-form"
>
<query-part-editor
class=
"gf-form-label query-part"
part=
"conditionModel.reducerPart"
handle-event=
"ctrl.handleReducerPartEvent(conditionModel, $event)"
>
<query-part-editor
class=
"gf-form-label query-part
width-5
"
part=
"conditionModel.reducerPart"
handle-event=
"ctrl.handleReducerPartEvent(conditionModel, $event)"
>
</query-part-editor>
<span
class=
"gf-form-label query-keyword"
>
OF
</span>
</div>
<div
class=
"gf-form"
>
<query-part-editor
class=
"gf-form-label query-part"
part=
"conditionModel.queryPart"
handle-event=
"ctrl.handleQueryPartEvent(conditionModel, $event)"
>
<query-part-editor
class=
"gf-form-label query-part
width-10
"
part=
"conditionModel.queryPart"
handle-event=
"ctrl.handleQueryPartEvent(conditionModel, $event)"
>
</query-part-editor>
</div>
<div
class=
"gf-form"
>
<metric-segment-model
property=
"conditionModel.evaluator.type"
options=
"ctrl.evalFunctions"
custom=
"false"
css-class=
"query-keyword"
on-change=
"ctrl.evaluatorTypeChanged(conditionModel.evaluator)"
></metric-segment-model>
<input
class=
"gf-form-input max-width-
7
"
type=
"number"
step=
"any"
ng-hide=
"conditionModel.evaluator.params.length === 0"
ng-model=
"conditionModel.evaluator.params[0]"
ng-change=
"ctrl.evaluatorParamsChanged()"
></input>
<input
class=
"gf-form-input max-width-
9
"
type=
"number"
step=
"any"
ng-hide=
"conditionModel.evaluator.params.length === 0"
ng-model=
"conditionModel.evaluator.params[0]"
ng-change=
"ctrl.evaluatorParamsChanged()"
></input>
<label
class=
"gf-form-label query-keyword"
ng-show=
"conditionModel.evaluator.params.length === 2"
>
TO
</label>
<input
class=
"gf-form-input max-width-
7
"
type=
"number"
step=
"any"
ng-if=
"conditionModel.evaluator.params.length === 2"
ng-model=
"conditionModel.evaluator.params[1]"
ng-change=
"ctrl.evaluatorParamsChanged()"
></input>
<input
class=
"gf-form-input max-width-
9
"
type=
"number"
step=
"any"
ng-if=
"conditionModel.evaluator.params.length === 2"
ng-model=
"conditionModel.evaluator.params[1]"
ng-change=
"ctrl.evaluatorParamsChanged()"
></input>
</div>
<div
class=
"gf-form"
>
<label
class=
"gf-form-label"
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment