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
b9f46668
Unverified
Commit
b9f46668
authored
Aug 09, 2018
by
David
Committed by
GitHub
Aug 09, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #12846 from grafana/davkal/explore-rules-expansion
Explore: expand recording rules for queries
parents
1c63f7a6
9d66eeb1
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
173 additions
and
7 deletions
+173
-7
public/app/containers/Explore/Explore.tsx
+4
-0
public/app/containers/Explore/PromQueryField.jest.tsx
+41
-1
public/app/containers/Explore/PromQueryField.tsx
+19
-1
public/app/plugins/datasource/prometheus/datasource.ts
+75
-2
public/app/plugins/datasource/prometheus/specs/datasource.jest.ts
+31
-0
public/vendor/css/rc-cascader.scss
+3
-3
No files found.
public/app/containers/Explore/Explore.tsx
View file @
b9f46668
...
...
@@ -169,6 +169,10 @@ export class Explore extends React.Component<any, IExploreState> {
const
historyKey
=
`grafana.explore.history.
${
datasourceId
}
`
;
const
history
=
store
.
getObject
(
historyKey
,
[]);
if
(
datasource
.
init
)
{
datasource
.
init
();
}
this
.
setState
(
{
datasource
,
...
...
public/app/containers/Explore/PromQueryField.jest.tsx
View file @
b9f46668
...
...
@@ -3,7 +3,7 @@ import Enzyme, { shallow } from 'enzyme';
import
Adapter
from
'enzyme-adapter-react-16'
;
import
Plain
from
'slate-plain-serializer'
;
import
PromQueryField
from
'./PromQueryField'
;
import
PromQueryField
,
{
groupMetricsByPrefix
,
RECORDING_RULES_GROUP
}
from
'./PromQueryField'
;
Enzyme
.
configure
({
adapter
:
new
Adapter
()
});
...
...
@@ -177,3 +177,43 @@ describe('PromQueryField typeahead handling', () => {
});
});
});
describe
(
'groupMetricsByPrefix()'
,
()
=>
{
it
(
'returns an empty group for no metrics'
,
()
=>
{
expect
(
groupMetricsByPrefix
([])).
toEqual
([]);
});
it
(
'returns options grouped by prefix'
,
()
=>
{
expect
(
groupMetricsByPrefix
([
'foo_metric'
])).
toMatchObject
([
{
value
:
'foo'
,
children
:
[
{
value
:
'foo_metric'
,
},
],
},
]);
});
it
(
'returns options without prefix as toplevel option'
,
()
=>
{
expect
(
groupMetricsByPrefix
([
'metric'
])).
toMatchObject
([
{
value
:
'metric'
,
},
]);
});
it
(
'returns recording rules grouped separately'
,
()
=>
{
expect
(
groupMetricsByPrefix
([
':foo_metric:'
])).
toMatchObject
([
{
value
:
RECORDING_RULES_GROUP
,
children
:
[
{
value
:
':foo_metric:'
,
},
],
},
]);
});
});
public/app/containers/Explore/PromQueryField.tsx
View file @
b9f46668
...
...
@@ -28,6 +28,7 @@ const HISTORY_ITEM_COUNT = 5;
const
HISTORY_COUNT_CUTOFF
=
1000
*
60
*
60
*
24
;
// 24h
const
METRIC_MARK
=
'metric'
;
const
PRISM_LANGUAGE
=
'promql'
;
export
const
RECORDING_RULES_GROUP
=
'__recording_rules__'
;
export
const
wrapLabel
=
(
label
:
string
)
=>
({
label
});
export
const
setFunctionMove
=
(
suggestion
:
Suggestion
):
Suggestion
=>
{
...
...
@@ -52,7 +53,22 @@ export function addHistoryMetadata(item: Suggestion, history: any[]): Suggestion
}
export
function
groupMetricsByPrefix
(
metrics
:
string
[],
delimiter
=
'_'
):
CascaderOption
[]
{
return
_
.
chain
(
metrics
)
// Filter out recording rules and insert as first option
const
ruleRegex
=
/:
\w
+:/
;
const
ruleNames
=
metrics
.
filter
(
metric
=>
ruleRegex
.
test
(
metric
));
const
rulesOption
=
{
label
:
'Recording rules'
,
value
:
RECORDING_RULES_GROUP
,
children
:
ruleNames
.
slice
()
.
sort
()
.
map
(
name
=>
({
label
:
name
,
value
:
name
})),
};
const
options
=
ruleNames
.
length
>
0
?
[
rulesOption
]
:
[];
const
metricsOptions
=
_
.
chain
(
metrics
)
.
filter
(
metric
=>
!
ruleRegex
.
test
(
metric
))
.
groupBy
(
metric
=>
metric
.
split
(
delimiter
)[
0
])
.
map
((
metricsForPrefix
:
string
[],
prefix
:
string
):
CascaderOption
=>
{
const
prefixIsMetric
=
metricsForPrefix
.
length
===
1
&&
metricsForPrefix
[
0
]
===
prefix
;
...
...
@@ -65,6 +81,8 @@ export function groupMetricsByPrefix(metrics: string[], delimiter = '_'): Cascad
})
.
sortBy
(
'label'
)
.
value
();
return
[...
options
,
...
metricsOptions
];
}
export
function
willApplySuggestion
(
...
...
public/app/plugins/datasource/prometheus/datasource.ts
View file @
b9f46668
...
...
@@ -82,7 +82,7 @@ export function addLabelToQuery(query: string, key: string, value: string): stri
return
parts
.
join
(
''
);
}
export
function
determineQueryHints
(
series
:
any
[]):
any
[]
{
export
function
determineQueryHints
(
series
:
any
[]
,
datasource
?:
any
):
any
[]
{
const
hints
=
series
.
map
((
s
,
i
)
=>
{
const
query
:
string
=
s
.
query
;
const
index
:
number
=
s
.
responseIndex
;
...
...
@@ -138,12 +138,56 @@ export function determineQueryHints(series: any[]): any[] {
}
}
// Check for recording rules expansion
if
(
datasource
&&
datasource
.
ruleMappings
)
{
const
mapping
=
datasource
.
ruleMappings
;
const
mappingForQuery
=
Object
.
keys
(
mapping
).
reduce
((
acc
,
ruleName
)
=>
{
if
(
query
.
search
(
ruleName
)
>
-
1
)
{
return
{
...
acc
,
[
ruleName
]:
mapping
[
ruleName
],
};
}
return
acc
;
},
{});
if
(
_
.
size
(
mappingForQuery
)
>
0
)
{
const
label
=
'Query contains recording rules.'
;
return
{
label
,
index
,
fix
:
{
label
:
'Expand rules'
,
action
:
{
type
:
'EXPAND_RULES'
,
query
,
index
,
mapping
:
mappingForQuery
,
},
},
};
}
}
// No hint found
return
null
;
});
return
hints
;
}
export
function
extractRuleMappingFromGroups
(
groups
:
any
[])
{
return
groups
.
reduce
(
(
mapping
,
group
)
=>
group
.
rules
.
filter
(
rule
=>
rule
.
type
===
'recording'
).
reduce
(
(
acc
,
rule
)
=>
({
...
acc
,
[
rule
.
name
]:
rule
.
query
,
}),
mapping
),
{}
);
}
export
function
prometheusRegularEscape
(
value
)
{
if
(
typeof
value
===
'string'
)
{
return
value
.
replace
(
/'/g
,
"
\\\\
'"
);
...
...
@@ -162,6 +206,7 @@ export class PrometheusDatasource {
type: string;
editorSrc: string;
name: string;
ruleMappings: { [index: string]: string };
supportsExplore: boolean;
supportMetrics: boolean;
url: string;
...
...
@@ -189,6 +234,11 @@ export class PrometheusDatasource {
this.queryTimeout = instanceSettings.jsonData.queryTimeout;
this.httpMethod = instanceSettings.jsonData.httpMethod || '
GET
';
this.resultTransformer = new ResultTransformer(templateSrv);
this.ruleMappings = {};
}
init() {
this.loadRules();
}
_request(url, data?, options?: any) {
...
...
@@ -312,7 +362,7 @@ export class PrometheusDatasource {
result = [...result, ...series];
if (queries[index].hinting) {
const queryHints = determineQueryHints(series);
const queryHints = determineQueryHints(series
, this
);
hints = [...hints, ...queryHints];
}
});
...
...
@@ -525,6 +575,21 @@ export class PrometheusDatasource {
return state;
}
loadRules() {
this.metadataRequest('
/
api
/
v1
/
rules
')
.then(res => res.data || res.json())
.then(body => {
const groups = _.get(body, ['
data
', '
groups
']);
if (groups) {
this.ruleMappings = extractRuleMappingFromGroups(groups);
}
})
.catch(e => {
console.log('
Rules
API
is
experimental
.
Ignore
next
error
.
');
console.error(e);
});
}
modifyQuery(query: string, action: any): string {
switch (action.type) {
case '
ADD_FILTER
': {
...
...
@@ -536,6 +601,14 @@ export class PrometheusDatasource {
case '
ADD_RATE
': {
return `rate(${query}[5m])`;
}
case '
EXPAND_RULES
': {
const mapping = action.mapping;
if (mapping) {
const ruleNames = Object.keys(mapping);
const rulesRegex = new RegExp(`(
\\
s|^)(${ruleNames.join('
|
')})(
\\
s|$|
\\
()`, '
ig
');
return query.replace(rulesRegex, (match, pre, name, post) => mapping[name]);
}
}
default:
return query;
}
...
...
public/app/plugins/datasource/prometheus/specs/datasource.jest.ts
View file @
b9f46668
...
...
@@ -4,6 +4,7 @@ import q from 'q';
import
{
alignRange
,
determineQueryHints
,
extractRuleMappingFromGroups
,
PrometheusDatasource
,
prometheusSpecialRegexEscape
,
prometheusRegularEscape
,
...
...
@@ -229,6 +230,36 @@ describe('PrometheusDatasource', () => {
});
});
describe
(
'extractRuleMappingFromGroups()'
,
()
=>
{
it
(
'returns empty mapping for no rule groups'
,
()
=>
{
expect
(
extractRuleMappingFromGroups
([])).
toEqual
({});
});
it
(
'returns a mapping for recording rules only'
,
()
=>
{
const
groups
=
[
{
rules
:
[
{
name
:
'HighRequestLatency'
,
query
:
'job:request_latency_seconds:mean5m{job="myjob"} > 0.5'
,
type
:
'alerting'
,
},
{
name
:
'job:http_inprogress_requests:sum'
,
query
:
'sum(http_inprogress_requests) by (job)'
,
type
:
'recording'
,
},
],
file
:
'/rules.yaml'
,
interval
:
60
,
name
:
'example'
,
},
];
const
mapping
=
extractRuleMappingFromGroups
(
groups
);
expect
(
mapping
).
toEqual
({
'job:http_inprogress_requests:sum'
:
'sum(http_inprogress_requests) by (job)'
});
});
});
describe
(
'Prometheus regular escaping'
,
()
=>
{
it
(
'should not escape non-string'
,
()
=>
{
expect
(
prometheusRegularEscape
(
12
)).
toEqual
(
12
);
...
...
public/vendor/css/rc-cascader.scss
View file @
b9f46668
...
...
@@ -16,7 +16,7 @@
}
.rc-cascader-menus.slide-up-enter
,
.rc-cascader-menus.slide-up-appear
{
animation-duration
:
.3s
;
animation-duration
:
0
.3s
;
animation-fill-mode
:
both
;
transform-origin
:
0
0
;
opacity
:
0
;
...
...
@@ -24,7 +24,7 @@
animation-play-state
:
paused
;
}
.rc-cascader-menus.slide-up-leave
{
animation-duration
:
.3s
;
animation-duration
:
0
.3s
;
animation-fill-mode
:
both
;
transform-origin
:
0
0
;
opacity
:
1
;
...
...
@@ -66,7 +66,7 @@
.rc-cascader-menu-item
{
height
:
32px
;
line-height
:
32px
;
padding
:
0
16px
;
padding
:
0
2
.5em
0
16px
;
cursor
:
pointer
;
white-space
:
nowrap
;
overflow
:
hidden
;
...
...
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