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
7c339f07
Commit
7c339f07
authored
Sep 30, 2016
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(alerting): show alertin state in panel header, closes #6136
parent
2c4524bb
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
194 additions
and
53 deletions
+194
-53
pkg/api/alerting.go
+19
-0
pkg/api/api.go
+1
-0
pkg/models/alert.go
+15
-0
pkg/services/alerting/extractor.go
+2
-2
pkg/services/alerting/extractor_test.go
+0
-2
pkg/services/sqlstore/alert.go
+17
-0
public/app/features/alerting/alert_tab_ctrl.ts
+30
-29
public/app/features/alerting/partials/alert_tab.html
+2
-2
public/app/features/annotations/annotations_srv.ts
+40
-4
public/app/features/dashboard/dashnav/dashnav.ts
+1
-1
public/app/features/panel/metrics_panel_ctrl.ts
+3
-1
public/app/features/panel/panel_directive.ts
+21
-1
public/app/features/panel/panel_menu.js
+1
-0
public/app/partials/panelgeneral.html
+2
-2
public/app/plugins/panel/graph/graph.ts
+1
-1
public/app/plugins/panel/graph/module.ts
+7
-3
public/app/plugins/panel/graph/thresholds_form.ts
+1
-1
public/app/plugins/panel/table/editor.html
+1
-4
public/sass/pages/_alerting.scss
+30
-0
No files found.
pkg/api/alerting.go
View file @
7c339f07
...
...
@@ -25,6 +25,25 @@ func ValidateOrgAlert(c *middleware.Context) {
}
}
func
GetAlertStatesForDashboard
(
c
*
middleware
.
Context
)
Response
{
dashboardId
:=
c
.
QueryInt64
(
"dashboardId"
)
if
dashboardId
==
0
{
return
ApiError
(
400
,
"Missing query parameter dashboardId"
,
nil
)
}
query
:=
models
.
GetAlertStatesForDashboardQuery
{
OrgId
:
c
.
OrgId
,
DashboardId
:
c
.
QueryInt64
(
"dashboardId"
),
}
if
err
:=
bus
.
Dispatch
(
&
query
);
err
!=
nil
{
return
ApiError
(
500
,
"Failed to fetch alert states"
,
err
)
}
return
Json
(
200
,
query
.
Result
)
}
// GET /api/alerts
func
GetAlerts
(
c
*
middleware
.
Context
)
Response
{
query
:=
models
.
GetAlertsQuery
{
...
...
pkg/api/api.go
View file @
7c339f07
...
...
@@ -254,6 +254,7 @@ func Register(r *macaron.Macaron) {
r
.
Post
(
"/test"
,
bind
(
dtos
.
AlertTestCommand
{}),
wrap
(
AlertTest
))
r
.
Get
(
"/:alertId"
,
ValidateOrgAlert
,
wrap
(
GetAlert
))
r
.
Get
(
"/"
,
wrap
(
GetAlerts
))
r
.
Get
(
"/states-for-dashboard"
,
wrap
(
GetAlertStatesForDashboard
))
})
r
.
Get
(
"/alert-notifications"
,
wrap
(
GetAlertNotifications
))
...
...
pkg/models/alert.go
View file @
7c339f07
...
...
@@ -135,3 +135,18 @@ type GetAlertByIdQuery struct {
Result
*
Alert
}
type
GetAlertStatesForDashboardQuery
struct
{
OrgId
int64
DashboardId
int64
Result
[]
*
AlertStateInfoDTO
}
type
AlertStateInfoDTO
struct
{
Id
int64
`json:"id"`
DashboardId
int64
`json:"dashboardId"`
PanelId
int64
`json:"panelId"`
State
AlertStateType
`json:"state"`
NewStateDate
time
.
Time
`json:"newStateDate"`
}
pkg/services/alerting/extractor.go
View file @
7c339f07
...
...
@@ -74,9 +74,9 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
continue
}
// backward compatability check, can be removed later
enabled
,
hasEnabled
:=
jsonAlert
.
CheckGet
(
"enabled"
)
if
!
hasEnabled
||
!
enabled
.
MustBool
()
{
if
hasEnabled
&&
enabled
.
MustBool
()
==
false
{
continue
}
...
...
pkg/services/alerting/extractor_test.go
View file @
7c339f07
...
...
@@ -42,7 +42,6 @@ func TestAlertRuleExtraction(t *testing.T) {
"name": "name1",
"message": "desc1",
"handler": 1,
"enabled": true,
"frequency": "60s",
"conditions": [
{
...
...
@@ -66,7 +65,6 @@ func TestAlertRuleExtraction(t *testing.T) {
"name": "name2",
"message": "desc2",
"handler": 0,
"enabled": true,
"frequency": "60s",
"severity": "warning",
"conditions": [
...
...
pkg/services/sqlstore/alert.go
View file @
7c339f07
...
...
@@ -17,6 +17,7 @@ func init() {
bus
.
AddHandler
(
"sql"
,
DeleteAlertById
)
bus
.
AddHandler
(
"sql"
,
GetAllAlertQueryHandler
)
bus
.
AddHandler
(
"sql"
,
SetAlertState
)
bus
.
AddHandler
(
"sql"
,
GetAlertStatesForDashboard
)
}
func
GetAlertById
(
query
*
m
.
GetAlertByIdQuery
)
error
{
...
...
@@ -241,3 +242,19 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error {
return
nil
})
}
func
GetAlertStatesForDashboard
(
query
*
m
.
GetAlertStatesForDashboardQuery
)
error
{
var
rawSql
=
`SELECT
id,
dashboard_id,
panel_id,
state,
new_state_date
FROM alert
WHERE org_id = ? AND dashboard_id = ?`
query
.
Result
=
make
([]
*
m
.
AlertStateInfoDTO
,
0
)
err
:=
x
.
Sql
(
rawSql
,
query
.
OrgId
,
query
.
DashboardId
)
.
Find
(
&
query
.
Result
)
return
err
}
public/app/features/alerting/alert_tab_ctrl.ts
View file @
7c339f07
...
...
@@ -48,19 +48,18 @@ export class AlertTabCtrl {
$onInit
()
{
this
.
addNotificationSegment
=
this
.
uiSegmentSrv
.
newPlusButton
();
this
.
initModel
();
this
.
validateModel
();
// subscribe to graph threshold handle changes
var
thresholdChangedEventHandler
=
this
.
graphThresholdChanged
.
bind
(
this
);
this
.
panelCtrl
.
events
.
on
(
'threshold-changed'
,
thresholdChangedEventHandler
);
// set panel alert edit mode
// set panel alert edit mode
this
.
$scope
.
$on
(
"$destroy"
,
()
=>
{
this
.
panelCtrl
.
events
.
off
(
"threshold-changed"
,
thresholdChangedEventHandler
);
this
.
panelCtrl
.
editingThresholds
=
false
;
this
.
panelCtrl
.
render
();
});
// subscribe to graph threshold handle changes
this
.
panelCtrl
.
events
.
on
(
'threshold-changed'
,
this
.
graphThresholdChanged
.
bind
(
this
));
// build notification model
// build notification model
this
.
notifications
=
[];
this
.
alertNotifications
=
[];
this
.
alertHistory
=
[];
...
...
@@ -68,21 +67,8 @@ export class AlertTabCtrl {
return
this
.
backendSrv
.
get
(
'/api/alert-notifications'
).
then
(
res
=>
{
this
.
notifications
=
res
;
_
.
each
(
this
.
alert
.
notifications
,
item
=>
{
var
model
=
_
.
find
(
this
.
notifications
,
{
id
:
item
.
id
});
if
(
model
)
{
model
.
iconClass
=
this
.
getNotificationIcon
(
model
.
type
);
this
.
alertNotifications
.
push
(
model
);
}
});
_
.
each
(
this
.
notifications
,
item
=>
{
if
(
item
.
isDefault
)
{
item
.
iconClass
=
this
.
getNotificationIcon
(
item
.
type
);
item
.
bgColor
=
"#00678b"
;
this
.
alertNotifications
.
push
(
item
);
}
});
this
.
initModel
();
this
.
validateModel
();
});
}
...
...
@@ -143,9 +129,8 @@ export class AlertTabCtrl {
}
initModel
()
{
var
alert
=
this
.
alert
=
this
.
panel
.
alert
=
this
.
panel
.
alert
||
{
enabled
:
false
};
if
(
!
this
.
alert
.
enabled
)
{
var
alert
=
this
.
alert
=
this
.
panel
.
alert
;
if
(
!
alert
)
{
return
;
}
...
...
@@ -169,6 +154,22 @@ export class AlertTabCtrl {
ThresholdMapper
.
alertToGraphThresholds
(
this
.
panel
);
for
(
let
addedNotification
of
alert
.
notifications
)
{
var
model
=
_
.
find
(
this
.
notifications
,
{
id
:
addedNotification
.
id
});
if
(
model
)
{
model
.
iconClass
=
this
.
getNotificationIcon
(
model
.
type
);
this
.
alertNotifications
.
push
(
model
);
}
}
for
(
let
notification
of
this
.
notifications
)
{
if
(
notification
.
isDefault
)
{
notification
.
iconClass
=
this
.
getNotificationIcon
(
notification
.
type
);
notification
.
bgColor
=
"#00678b"
;
this
.
alertNotifications
.
push
(
notification
);
}
}
this
.
panelCtrl
.
editingThresholds
=
true
;
this
.
panelCtrl
.
render
();
}
...
...
@@ -193,7 +194,7 @@ export class AlertTabCtrl {
}
validateModel
()
{
if
(
!
this
.
alert
.
enabled
)
{
if
(
!
this
.
alert
)
{
return
;
}
...
...
@@ -310,17 +311,17 @@ export class AlertTabCtrl {
icon
:
'fa-trash'
,
yesText
:
'Delete'
,
onConfirm
:
()
=>
{
this
.
alert
=
this
.
panel
.
alert
=
{
enabled
:
false
};
delete
this
.
panel
.
alert
;
this
.
alert
=
null
;
this
.
panel
.
thresholds
=
[];
this
.
conditionModels
=
[];
this
.
panelCtrl
.
render
();
}
});
}
enable
()
{
this
.
alert
.
enabled
=
true
;
this
.
panel
.
alert
=
{}
;
this
.
initModel
();
}
...
...
public/app/features/alerting/partials/alert_tab.html
View file @
7c339f07
<div
class=
"edit-tab-with-sidemenu"
ng-if=
"ctrl.alert
.enabled
"
>
<div
class=
"edit-tab-with-sidemenu"
ng-if=
"ctrl.alert"
>
<aside
class=
"edit-sidemenu-aside"
>
<ul
class=
"edit-sidemenu"
>
<li
ng-class=
"{active: ctrl.subTabIndex === 0}"
>
...
...
@@ -151,7 +151,7 @@
</div>
</div>
<div
class=
"gf-form-group"
ng-if=
"!ctrl.alert
.enabled
"
>
<div
class=
"gf-form-group"
ng-if=
"!ctrl.alert"
>
<div
class=
"gf-form-button-row"
>
<button
class=
"btn btn-inverse"
ng-click=
"ctrl.enable()"
>
<i
class=
"icon-gf icon-gf-alert"
></i>
...
...
public/app/features/annotations/annotations_srv.ts
View file @
7c339f07
...
...
@@ -9,6 +9,7 @@ import coreModule from 'app/core/core_module';
export
class
AnnotationsSrv
{
globalAnnotationsPromise
:
any
;
alertStatesPromise
:
any
;
/** @ngInject */
constructor
(
private
$rootScope
,
...
...
@@ -22,14 +23,27 @@ export class AnnotationsSrv {
clearCache
()
{
this
.
globalAnnotationsPromise
=
null
;
this
.
alertStatesPromise
=
null
;
}
getAnnotations
(
options
)
{
return
this
.
$q
.
all
([
this
.
getGlobalAnnotations
(
options
),
this
.
getPanelAnnotations
(
options
)
]).
then
(
allResults
=>
{
return
_
.
flattenDeep
(
allResults
);
this
.
getPanelAnnotations
(
options
),
this
.
getAlertStates
(
options
)
]).
then
(
results
=>
{
// combine the annotations and flatten results
var
annotations
=
_
.
flattenDeep
([
results
[
0
],
results
[
1
]]);
// look for alert state for this panel
var
alertState
=
_
.
find
(
results
[
2
],
{
panelId
:
options
.
panel
.
id
});
return
{
annotations
:
annotations
,
alertState
:
alertState
,
};
}).
catch
(
err
=>
{
this
.
$rootScope
.
appEvent
(
'alert-error'
,
[
'Annotations failed'
,
(
err
.
message
||
err
)]);
});
...
...
@@ -39,7 +53,7 @@ export class AnnotationsSrv {
var
panel
=
options
.
panel
;
var
dashboard
=
options
.
dashboard
;
if
(
panel
&&
panel
.
alert
&&
panel
.
alert
.
enabled
)
{
if
(
panel
&&
panel
.
alert
)
{
return
this
.
backendSrv
.
get
(
'/api/annotations'
,
{
from
:
options
.
range
.
from
.
valueOf
(),
to
:
options
.
range
.
to
.
valueOf
(),
...
...
@@ -54,6 +68,28 @@ export class AnnotationsSrv {
return
this
.
$q
.
when
([]);
}
getAlertStates
(
options
)
{
if
(
!
options
.
dashboard
.
id
)
{
return
this
.
$q
.
when
([]);
}
// ignore if no alerts
if
(
options
.
panel
&&
!
options
.
panel
.
alert
)
{
return
this
.
$q
.
when
([]);
}
if
(
options
.
range
.
raw
.
to
!==
'now'
)
{
return
this
.
$q
.
when
([]);
}
if
(
this
.
alertStatesPromise
)
{
return
this
.
alertStatesPromise
;
}
this
.
alertStatesPromise
=
this
.
backendSrv
.
get
(
'/api/alerts/states-for-dashboard'
,
{
dashboardId
:
options
.
dashboard
.
id
});
return
this
.
alertStatesPromise
;
}
getGlobalAnnotations
(
options
)
{
var
dashboard
=
options
.
dashboard
;
...
...
public/app/features/dashboard/dashnav/dashnav.ts
View file @
7c339f07
...
...
@@ -159,7 +159,7 @@ export class DashNavCtrl {
var
confirmText
=
""
;
var
text2
=
$scope
.
dashboard
.
title
;
var
alerts
=
$scope
.
dashboard
.
rows
.
reduce
((
memo
,
row
)
=>
{
memo
+=
row
.
panels
.
filter
(
panel
=>
panel
.
alert
&&
panel
.
alert
.
enabled
).
length
;
memo
+=
row
.
panels
.
filter
(
panel
=>
panel
.
alert
).
length
;
return
memo
;
},
0
);
...
...
public/app/features/panel/metrics_panel_ctrl.ts
View file @
7c339f07
...
...
@@ -131,7 +131,9 @@ class MetricsPanelCtrl extends PanelCtrl {
var
intervalOverride
=
this
.
panel
.
interval
;
// if no panel interval check datasource
if
(
!
intervalOverride
&&
this
.
datasource
&&
this
.
datasource
.
interval
)
{
if
(
intervalOverride
)
{
intervalOverride
=
this
.
templateSrv
.
replace
(
intervalOverride
,
this
.
panel
.
scopedVars
);
}
else
if
(
this
.
datasource
&&
this
.
datasource
.
interval
)
{
intervalOverride
=
this
.
datasource
.
interval
;
}
...
...
public/app/features/panel/panel_directive.ts
View file @
7c339f07
...
...
@@ -6,7 +6,7 @@ import $ from 'jquery';
var
module
=
angular
.
module
(
'grafana.directives'
);
var
panelTemplate
=
`
<div class="panel-container"
ng-class="{'panel-transparent': ctrl.panel.transparent}"
>
<div class="panel-container">
<div class="panel-header">
<span class="alert-error panel-error small pointer" ng-if="ctrl.error" ng-click="ctrl.openInspector()">
<span data-placement="top" bs-tooltip="ctrl.error">
...
...
@@ -65,6 +65,26 @@ module.directive('grafanaPanel', function() {
link
:
function
(
scope
,
elem
)
{
var
panelContainer
=
elem
.
find
(
'.panel-container'
);
var
ctrl
=
scope
.
ctrl
;
// the reason for handling these classes this way is for performance
// limit the watchers on panels etc
ctrl
.
events
.
on
(
'render'
,
()
=>
{
panelContainer
.
toggleClass
(
'panel-transparent'
,
ctrl
.
panel
.
transparent
===
true
);
panelContainer
.
toggleClass
(
'panel-has-alert'
,
ctrl
.
panel
.
alert
!==
undefined
);
if
(
panelContainer
.
hasClass
(
'panel-has-alert'
))
{
panelContainer
.
removeClass
(
'panel-alert-state--ok panel-alert-state--alerting'
);
}
// set special class for ok, or alerting states
if
(
ctrl
.
alertState
)
{
if
(
ctrl
.
alertState
.
state
===
'ok'
||
ctrl
.
alertState
.
state
===
'alerting'
)
{
panelContainer
.
addClass
(
'panel-alert-state--'
+
ctrl
.
alertState
.
state
);
}
}
});
scope
.
$watchGroup
([
'ctrl.fullscreen'
,
'ctrl.containerHeight'
],
function
()
{
panelContainer
.
css
({
minHeight
:
ctrl
.
containerHeight
});
elem
.
toggleClass
(
'panel-fullscreen'
,
ctrl
.
fullscreen
?
true
:
false
);
...
...
public/app/features/panel/panel_menu.js
View file @
7c339f07
...
...
@@ -12,6 +12,7 @@ function (angular, $, _, Tether) {
.
directive
(
'panelMenu'
,
function
(
$compile
,
linkSrv
)
{
var
linkTemplate
=
'<span class="panel-title drag-handle pointer">'
+
'<span class="icon-gf panel-alert-icon"></span>'
+
'<span class="panel-title-text drag-handle">{{ctrl.panel.title | interpolateTemplateVars:this}}</span>'
+
'<span class="panel-links-btn"><i class="fa fa-external-link"></i></span>'
+
'<span class="panel-time-info" ng-show="ctrl.timeInfo"><i class="fa fa-clock-o"></i> {{ctrl.timeInfo}}</span>'
+
...
...
public/app/partials/panelgeneral.html
View file @
7c339f07
...
...
@@ -8,11 +8,11 @@
<span
class=
"gf-form-label width-6"
>
Span
</span>
<select
class=
"gf-form-input gf-size-auto"
ng-model=
"ctrl.panel.span"
ng-options=
"f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"
></select>
</div>
<div
class=
"gf-form
max-width-26
"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-8"
>
Height
</span>
<input
type=
"text"
class=
"gf-form-input max-width-6"
ng-model=
'ctrl.panel.height'
placeholder=
"100px"
></input>
<editor-checkbox
text=
"Transparent"
model=
"ctrl.panel.transparent"
></editor-checkbox>
</div>
<gf-form-switch
class=
"gf-form"
label=
"Transparent"
checked=
"ctrl.panel.transparent"
on-change=
"ctrl.render()"
></gf-form-switch>
</div>
<div
class=
"gf-form-inline"
>
...
...
public/app/plugins/panel/graph/graph.ts
View file @
7c339f07
...
...
@@ -62,7 +62,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) {
if
(
!
data
)
{
return
;
}
annotations
=
data
.
annotations
||
annotations
;
annotations
=
ctrl
.
annotations
;
render_panel
();
});
...
...
public/app/plugins/panel/graph/module.ts
View file @
7c339f07
...
...
@@ -22,6 +22,9 @@ class GraphCtrl extends MetricsPanelCtrl {
hiddenSeries
:
any
=
{};
seriesList
:
any
=
[];
dataList
:
any
=
[];
annotations
:
any
=
[];
alertState
:
any
;
annotationsPromise
:
any
;
datapointsCount
:
number
;
datapointsOutside
:
boolean
;
...
...
@@ -167,11 +170,11 @@ class GraphCtrl extends MetricsPanelCtrl {
onDataError
(
err
)
{
this
.
seriesList
=
[];
this
.
annotations
=
[];
this
.
render
([]);
}
onDataReceived
(
dataList
)
{
this
.
dataList
=
dataList
;
this
.
seriesList
=
this
.
processor
.
getSeriesList
({
dataList
:
dataList
,
range
:
this
.
range
});
...
...
@@ -186,9 +189,10 @@ class GraphCtrl extends MetricsPanelCtrl {
}
}
this
.
annotationsPromise
.
then
(
annotations
=>
{
this
.
annotationsPromise
.
then
(
result
=>
{
this
.
loading
=
false
;
this
.
seriesList
.
annotations
=
annotations
;
this
.
alertState
=
result
.
alertState
;
this
.
annotations
=
result
.
annotations
;
this
.
render
(
this
.
seriesList
);
},
()
=>
{
this
.
loading
=
false
;
...
...
public/app/plugins/panel/graph/thresholds_form.ts
View file @
7c339f07
...
...
@@ -13,7 +13,7 @@ export class ThresholdFormCtrl {
constructor
(
$scope
)
{
this
.
panel
=
this
.
panelCtrl
.
panel
;
if
(
this
.
panel
.
alert
&&
this
.
panel
.
alert
.
enabled
)
{
if
(
this
.
panel
.
alert
)
{
this
.
disabled
=
true
;
}
...
...
public/app/plugins/panel/table/editor.html
View file @
7c339f07
...
...
@@ -34,10 +34,7 @@
ng-change=
"editor.render()"
ng-model-onblur
>
</div>
<gf-form-switch
class=
"gf-form"
label-class=
"width-4"
label=
"Scroll"
checked=
"editor.panel.scroll"
change=
"editor.render()"
></gf-form-switch>
<gf-form-switch
class=
"gf-form"
label-class=
"width-4"
label=
"Scroll"
checked=
"editor.panel.scroll"
on-change=
"editor.render()"
></gf-form-switch>
<div
class=
"gf-form max-width-17"
>
<label
class=
"gf-form-label width-6"
>
Font size
</label>
<div
class=
"gf-form-select-wrapper max-width-15"
>
...
...
public/sass/pages/_alerting.scss
View file @
7c339f07
...
...
@@ -38,3 +38,33 @@
top
:
2px
;
}
}
.panel-has-alert
{
.panel-alert-icon
:before
{
content
:
"\e611"
;
position
:
relative
;
top
:
1px
;
left
:
-3px
;
}
}
.panel-alert-state
{
&
--alerting
{
box-shadow
:
0
0
10px
$critical
;
.panel-alert-icon
:before
{
color
:
$critical
;
content
:
"\e610"
;
}
}
&
--ok
{
//box-shadow: 0 0 5px rgba(0,200,0,10.8);
.panel-alert-icon
:before
{
color
:
$online
;
content
:
"\e610"
;
}
}
}
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