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
e5aeb006
Unverified
Commit
e5aeb006
authored
Oct 31, 2018
by
David
Committed by
GitHub
Oct 31, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #13918 from grafana/davkal/explore-syntax-start
Explore: async starts of language provider
parents
ee5b37eb
4f959648
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
105 additions
and
54 deletions
+105
-54
public/app/plugins/datasource/logging/components/LoggingQueryField.tsx
+36
-14
public/app/plugins/datasource/logging/language_provider.ts
+37
-33
public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
+16
-5
public/app/plugins/datasource/prometheus/language_provider.ts
+1
-1
public/app/types/explore.ts
+5
-1
public/sass/pages/_explore.scss
+10
-0
No files found.
public/app/plugins/datasource/logging/components/LoggingQueryField.tsx
View file @
e5aeb006
...
...
@@ -92,25 +92,44 @@ class LoggingQueryField extends React.PureComponent<LoggingQueryFieldProps, Logg
componentDidMount
()
{
if
(
this
.
languageProvider
)
{
this
.
languageProvider
.
start
().
then
(()
=>
this
.
onReceiveMetrics
());
this
.
languageProvider
.
start
()
.
then
(
remaining
=>
{
remaining
.
map
(
task
=>
task
.
then
(
this
.
onReceiveMetrics
).
catch
(()
=>
{}));
})
.
then
(()
=>
this
.
onReceiveMetrics
());
}
}
loadOptions
=
(
selectedOptions
:
CascaderOption
[])
=>
{
const
targetOption
=
selectedOptions
[
selectedOptions
.
length
-
1
];
this
.
setState
(
state
=>
{
const
nextOptions
=
state
.
logLabelOptions
.
map
(
option
=>
{
if
(
option
.
value
===
targetOption
.
value
)
{
return
{
...
option
,
loading
:
true
,
};
}
return
option
;
});
return
{
logLabelOptions
:
nextOptions
};
});
this
.
languageProvider
.
fetchLabelValues
(
targetOption
.
value
)
.
then
(
this
.
onReceiveMetrics
)
.
catch
(()
=>
{});
};
onChangeLogLabels
=
(
values
:
string
[],
selectedOptions
:
CascaderOption
[])
=>
{
let
query
;
if
(
selectedOptions
.
length
===
1
)
{
if
(
selectedOptions
[
0
].
children
.
length
===
0
)
{
query
=
selectedOptions
[
0
].
value
;
}
else
{
// Ignore click on group
return
;
}
}
else
{
if
(
selectedOptions
.
length
===
2
)
{
const
key
=
selectedOptions
[
0
].
value
;
const
value
=
selectedOptions
[
1
].
value
;
query
=
`{
${
key
}
="
${
value
}
"}`
;
}
const
query
=
`{
${
key
}
="
${
value
}
"}`
;
this
.
onChangeQuery
(
query
,
true
);
}
};
onChangeQuery
=
(
value
:
string
,
override
?:
boolean
)
=>
{
...
...
@@ -165,12 +184,15 @@ class LoggingQueryField extends React.PureComponent<LoggingQueryFieldProps, Logg
const
{
error
,
hint
,
initialQuery
}
=
this
.
props
;
const
{
logLabelOptions
,
syntaxLoaded
}
=
this
.
state
;
const
cleanText
=
this
.
languageProvider
?
this
.
languageProvider
.
cleanText
:
undefined
;
const
chooserText
=
syntaxLoaded
?
'Log labels'
:
'Loading labels...'
;
return
(
<
div
className=
"prom-query-field"
>
<
div
className=
"prom-query-field-tools"
>
<
Cascader
options=
{
logLabelOptions
}
onChange=
{
this
.
onChangeLogLabels
}
>
<
button
className=
"btn navbar-button navbar-button--tight"
>
Log labels
</
button
>
<
Cascader
options=
{
logLabelOptions
}
onChange=
{
this
.
onChangeLogLabels
}
loadData=
{
this
.
loadOptions
}
>
<
button
className=
"btn navbar-button navbar-button--tight"
disabled=
{
!
syntaxLoaded
}
>
{
chooserText
}
</
button
>
</
Cascader
>
</
div
>
<
div
className=
"prom-query-field-wrapper"
>
...
...
public/app/plugins/datasource/logging/language_provider.ts
View file @
e5aeb006
...
...
@@ -12,9 +12,9 @@ import {
import
{
parseSelector
}
from
'app/plugins/datasource/prometheus/language_utils'
;
import
PromqlSyntax
from
'app/plugins/datasource/prometheus/promql'
;
const
DEFAULT_KEYS
=
[
'job'
,
'
instan
ce'
];
const
DEFAULT_KEYS
=
[
'job'
,
'
namespa
ce'
];
const
EMPTY_SELECTOR
=
'{}'
;
const
HISTORY_ITEM_COUNT
=
5
;
const
HISTORY_ITEM_COUNT
=
10
;
const
HISTORY_COUNT_CUTOFF
=
1000
*
60
*
60
*
24
;
// 24h
const
wrapLabel
=
(
label
:
string
)
=>
({
label
});
...
...
@@ -65,7 +65,7 @@ export default class LoggingLanguageProvider extends LanguageProvider {
start
=
()
=>
{
if
(
!
this
.
started
)
{
this
.
started
=
true
;
return
Promise
.
all
([
this
.
fetchLogLabels
()]
);
return
this
.
fetchLogLabels
(
);
}
return
Promise
.
resolve
([]);
};
...
...
@@ -118,35 +118,36 @@ export default class LoggingLanguageProvider extends LanguageProvider {
getLabelCompletionItems
({
text
,
wrapperClasses
,
labelKey
,
value
}:
TypeaheadInput
):
TypeaheadOutput
{
let
context
:
string
;
let
refresher
:
Promise
<
any
>
=
null
;
const
suggestions
:
CompletionItemGroup
[]
=
[];
const
line
=
value
.
anchorBlock
.
getText
();
const
cursorOffset
:
number
=
value
.
anchorOffset
;
//
Get normalized selector
let
selector
;
//
Use EMPTY_SELECTOR until series API is implemented for facetting
const
selector
=
EMPTY_SELECTOR
;
let
parsedSelector
;
try
{
parsedSelector
=
parseSelector
(
line
,
cursorOffset
);
selector
=
parsedSelector
.
selector
;
}
catch
{
selector
=
EMPTY_SELECTOR
;
}
const
containsMetric
=
selector
.
indexOf
(
'__name__='
)
>
-
1
;
}
catch
{}
const
existingKeys
=
parsedSelector
?
parsedSelector
.
labelKeys
:
[];
if
((
text
&&
text
.
match
(
/^!
?
=~
?
/
))
||
_
.
includes
(
wrapperClasses
,
'attr-value'
))
{
// Label values
if
(
labelKey
&&
this
.
labelValues
[
selector
]
&&
this
.
labelValues
[
selector
][
labelKey
]
)
{
if
(
labelKey
&&
this
.
labelValues
[
selector
])
{
const
labelValues
=
this
.
labelValues
[
selector
][
labelKey
];
if
(
labelValues
)
{
context
=
'context-label-values'
;
suggestions
.
push
({
label
:
`Label values for "
${
labelKey
}
"`
,
items
:
labelValues
.
map
(
wrapLabel
),
});
}
else
{
refresher
=
this
.
fetchLabelValues
(
labelKey
);
}
}
}
else
{
// Label keys
const
labelKeys
=
this
.
labelKeys
[
selector
]
||
(
containsMetric
?
null
:
DEFAULT_KEYS
)
;
const
labelKeys
=
this
.
labelKeys
[
selector
]
||
DEFAULT_KEYS
;
if
(
labelKeys
)
{
const
possibleKeys
=
_
.
difference
(
labelKeys
,
existingKeys
);
if
(
possibleKeys
.
length
>
0
)
{
...
...
@@ -156,7 +157,7 @@ export default class LoggingLanguageProvider extends LanguageProvider {
}
}
return
{
context
,
suggestions
};
return
{
context
,
refresher
,
suggestions
};
}
async
fetchLogLabels
()
{
...
...
@@ -165,29 +166,18 @@ export default class LoggingLanguageProvider extends LanguageProvider {
const
res
=
await
this
.
request
(
url
);
const
body
=
await
(
res
.
data
||
res
.
json
());
const
labelKeys
=
body
.
data
.
slice
().
sort
();
const
labelKeysBySelector
=
{
this
.
labelKeys
=
{
...
this
.
labelKeys
,
[
EMPTY_SELECTOR
]:
labelKeys
,
};
const
labelValuesByKey
=
{};
this
.
logLabelOptions
=
[];
for
(
const
key
of
labelKeys
)
{
const
valuesUrl
=
`/api/prom/label/
${
key
}
/values`
;
const
res
=
await
this
.
request
(
valuesUrl
);
const
body
=
await
(
res
.
data
||
res
.
json
());
const
values
=
body
.
data
.
slice
().
sort
();
labelValuesByKey
[
key
]
=
values
;
this
.
logLabelOptions
.
push
({
label
:
key
,
value
:
key
,
children
:
values
.
map
(
value
=>
({
label
:
value
,
value
})),
});
}
this
.
labelValues
=
{
[
EMPTY_SELECTOR
]:
labelValuesByKey
};
this
.
labelKeys
=
labelKeysBySelector
;
this
.
logLabelOptions
=
labelKeys
.
map
(
key
=>
({
label
:
key
,
value
:
key
,
isLeaf
:
false
}));
// Pre-load values for default labels
return
labelKeys
.
filter
(
key
=>
DEFAULT_KEYS
.
indexOf
(
key
)
>
-
1
).
map
(
key
=>
this
.
fetchLabelValues
(
key
));
}
catch
(
e
)
{
console
.
error
(
e
);
}
return
[];
}
async
fetchLabelValues
(
key
:
string
)
{
...
...
@@ -195,14 +185,28 @@ export default class LoggingLanguageProvider extends LanguageProvider {
try
{
const
res
=
await
this
.
request
(
url
);
const
body
=
await
(
res
.
data
||
res
.
json
());
const
values
=
body
.
data
.
slice
().
sort
();
// Add to label options
this
.
logLabelOptions
=
this
.
logLabelOptions
.
map
(
keyOption
=>
{
if
(
keyOption
.
value
===
key
)
{
return
{
...
keyOption
,
children
:
values
.
map
(
value
=>
({
label
:
value
,
value
})),
};
}
return
keyOption
;
});
// Add to key map
const
exisingValues
=
this
.
labelValues
[
EMPTY_SELECTOR
];
const
v
alues
=
{
const
nextV
alues
=
{
...
exisingValues
,
[
key
]:
body
.
data
,
[
key
]:
values
,
};
this
.
labelValues
=
{
...
this
.
labelValues
,
[
EMPTY_SELECTOR
]:
v
alues
,
[
EMPTY_SELECTOR
]:
nextV
alues
,
};
}
catch
(
e
)
{
console
.
error
(
e
);
...
...
public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
View file @
e5aeb006
...
...
@@ -131,7 +131,12 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
componentDidMount
()
{
if
(
this
.
languageProvider
)
{
this
.
languageProvider
.
start
().
then
(()
=>
this
.
onReceiveMetrics
());
this
.
languageProvider
.
start
()
.
then
(
remaining
=>
{
remaining
.
map
(
task
=>
task
.
then
(
this
.
onReceiveMetrics
).
catch
(()
=>
{}));
})
.
then
(()
=>
this
.
onReceiveMetrics
());
}
}
...
...
@@ -186,10 +191,13 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
// Build metrics tree
const
metricsByPrefix
=
groupMetricsByPrefix
(
metrics
);
const
histogramOptions
=
histogramMetrics
.
map
(
hm
=>
({
label
:
hm
,
value
:
hm
}));
const
metricsOptions
=
[
{
label
:
'Histograms'
,
value
:
HISTOGRAM_GROUP
,
children
:
histogramOptions
},
const
metricsOptions
=
histogramMetrics
.
length
>
0
?
[
{
label
:
'Histograms'
,
value
:
HISTOGRAM_GROUP
,
children
:
histogramOptions
,
isLeaf
:
false
},
...
metricsByPrefix
,
];
]
:
metricsByPrefix
;
this
.
setState
({
metricsOptions
,
syntaxLoaded
:
true
});
};
...
...
@@ -222,12 +230,15 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
const
{
error
,
hint
,
initialQuery
}
=
this
.
props
;
const
{
metricsOptions
,
syntaxLoaded
}
=
this
.
state
;
const
cleanText
=
this
.
languageProvider
?
this
.
languageProvider
.
cleanText
:
undefined
;
const
chooserText
=
syntaxLoaded
?
'Metrics'
:
'Loading matrics...'
;
return
(
<
div
className=
"prom-query-field"
>
<
div
className=
"prom-query-field-tools"
>
<
Cascader
options=
{
metricsOptions
}
onChange=
{
this
.
onChangeMetrics
}
>
<
button
className=
"btn navbar-button navbar-button--tight"
>
Metrics
</
button
>
<
button
className=
"btn navbar-button navbar-button--tight"
disabled=
{
!
syntaxLoaded
}
>
{
chooserText
}
</
button
>
</
Cascader
>
</
div
>
<
div
className=
"prom-query-field-wrapper"
>
...
...
public/app/plugins/datasource/prometheus/language_provider.ts
View file @
e5aeb006
...
...
@@ -74,7 +74,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
start
=
()
=>
{
if
(
!
this
.
started
)
{
this
.
started
=
true
;
return
Promise
.
all
([
this
.
fetchMetricNames
(),
this
.
fetchHistogramMetrics
()]);
return
this
.
fetchMetricNames
().
then
(()
=>
[
this
.
fetchHistogramMetrics
()]);
}
return
Promise
.
resolve
([]);
};
...
...
public/app/types/explore.ts
View file @
e5aeb006
...
...
@@ -85,7 +85,11 @@ export interface HistoryItem {
export
abstract
class
LanguageProvider
{
datasource
:
any
;
request
:
(
url
)
=>
Promise
<
any
>
;
start
:
()
=>
Promise
<
any
>
;
/**
* Returns a promise that resolves with a task list when main syntax is loaded.
* Task list consists of secondary promises that load more detailed language features.
*/
start
:
()
=>
Promise
<
any
[]
>
;
}
export
interface
TypeaheadInput
{
...
...
public/sass/pages/_explore.scss
View file @
e5aeb006
...
...
@@ -329,6 +329,16 @@
text-align
:
right
;
}
// React-component cascade fix: show "loading" even though item can expand
.rc-cascader-menu-item-loading
:after
{
position
:
absolute
;
right
:
12px
;
content
:
'loading'
;
color
:
#767980
;
font-style
:
italic
;
}
// TODO Experimental
.cheat-sheet-item
{
...
...
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