Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
spring-boot-admin
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
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
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
openSource
spring-boot-admin
Commits
6644673b
Commit
6644673b
authored
May 13, 2018
by
Johannes Edmeier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add tabular view for metrics
parent
c5f4b1c5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
446 additions
and
22 deletions
+446
-22
package-lock.json
spring-boot-admin-server-ui/package-lock.json
+0
-0
package.json
spring-boot-admin-server-ui/package.json
+12
-12
base.scss
...ot-admin-server-ui/src/main/frontend/assets/css/base.scss
+8
-0
sba-panel.vue
...dmin-server-ui/src/main/frontend/components/sba-panel.vue
+30
-2
popper.js
...ot-admin-server-ui/src/main/frontend/directives/popper.js
+1
-1
instance.js
...ot-admin-server-ui/src/main/frontend/services/instance.js
+6
-1
collections.js
...ot-admin-server-ui/src/main/frontend/utils/collections.js
+22
-0
notification-filter-settings.vue
...ntend/views/applications/notification-filter-settings.vue
+4
-4
index.js
spring-boot-admin-server-ui/src/main/frontend/views/index.js
+2
-1
index.js
...dmin-server-ui/src/main/frontend/views/instances/index.js
+8
-0
index.vue
...er-ui/src/main/frontend/views/instances/metrics/index.vue
+169
-0
metric.vue
...r-ui/src/main/frontend/views/instances/metrics/metric.vue
+182
-0
index.vue
...admin-server-ui/src/main/frontend/views/journal/index.vue
+2
-1
No files found.
spring-boot-admin-server-ui/package-lock.json
View file @
6644673b
This diff is collapsed.
Click to expand it.
spring-boot-admin-server-ui/package.json
View file @
6644673b
...
...
@@ -9,10 +9,10 @@
"test:watch"
:
"jest --watch"
},
"dependencies"
:
{
"@fortawesome/fontawesome"
:
"^1.1.
7
"
,
"@fortawesome/fontawesome-free-brands"
:
"^5.0.1
2
"
,
"@fortawesome/fontawesome-free-regular"
:
"^5.0.1
2
"
,
"@fortawesome/fontawesome-free-solid"
:
"^5.0.1
2
"
,
"@fortawesome/fontawesome"
:
"^1.1.
8
"
,
"@fortawesome/fontawesome-free-brands"
:
"^5.0.1
3
"
,
"@fortawesome/fontawesome-free-regular"
:
"^5.0.1
3
"
,
"@fortawesome/fontawesome-free-solid"
:
"^5.0.1
3
"
,
"@fortawesome/vue-fontawesome"
:
"0.0.22"
,
"axios"
:
"^0.18.0"
,
"bulma"
:
"^0.7.1"
,
...
...
@@ -30,7 +30,7 @@
"moment"
:
"^2.22.1"
,
"moment-shortformat"
:
"^2.1.0"
,
"popper.js"
:
"^1.14.3"
,
"pretty-bytes"
:
"^
4.0.2
"
,
"pretty-bytes"
:
"^
5.0.0
"
,
"resize-observer-polyfill"
:
"^1.5.0"
,
"rxjs"
:
"^5.5.10"
,
"vue"
:
"^2.5.16"
,
...
...
@@ -39,18 +39,18 @@
"yamljs"
:
"^0.3.0"
},
"devDependencies"
:
{
"@vue/test-utils"
:
"^1.0.0-beta.1
5
"
,
"autoprefixer"
:
"^8.
4.1
"
,
"@vue/test-utils"
:
"^1.0.0-beta.1
6
"
,
"autoprefixer"
:
"^8.
5.0
"
,
"babel-core"
:
"^6.26.3"
,
"babel-eslint"
:
"^8.2.3"
,
"babel-jest"
:
"^22.4.3"
,
"babel-loader"
:
"^7.1.4"
,
"babel-plugin-lodash"
:
"^3.3.2"
,
"babel-polyfill"
:
"^6.26.0"
,
"babel-preset-env"
:
"^1.
6.1
"
,
"babel-preset-env"
:
"^1.
7.0
"
,
"babel-preset-stage-2"
:
"^6.24.1"
,
"clean-webpack-plugin"
:
"^0.1.19"
,
"cross-env"
:
"^5.1.
4
"
,
"cross-env"
:
"^5.1.
5
"
,
"css-hot-loader"
:
"^1.3.9"
,
"css-loader"
:
"^0.28.11"
,
"css-mqpacker"
:
"^6.0.2"
,
...
...
@@ -68,7 +68,7 @@
"lodash-webpack-plugin"
:
"^0.11.5"
,
"node-sass"
:
"^4.9.0"
,
"optimize-css-assets-webpack-plugin"
:
"^3.2.0"
,
"postcss-loader"
:
"^2.1.
4
"
,
"postcss-loader"
:
"^2.1.
5
"
,
"sass-loader"
:
"^6.0.7"
,
"style-loader"
:
"^0.20.3"
,
"url-loader"
:
"^0.6.2"
,
...
...
@@ -76,8 +76,8 @@
"vue-loader"
:
"^14.2.2"
,
"vue-svg-loader"
:
"^0.5.0"
,
"vue-template-compiler"
:
"^2.5.16"
,
"webpack"
:
"^3.1
1
.0"
,
"webpack-bundle-analyzer"
:
"^2.11.
1
"
"webpack"
:
"^3.1
2
.0"
,
"webpack-bundle-analyzer"
:
"^2.11.
2
"
},
"browserslist"
:
[
"> 2%"
,
...
...
spring-boot-admin-server-ui/src/main/frontend/assets/css/base.scss
View file @
6644673b
...
...
@@ -81,6 +81,14 @@ body {
}
}
p
.is-loading
{
&
:
:
before
{
display
:
inline-block
;
right
:
0
.25em
;
@include
loader
;
}
}
//hero as card-header
.card
.hero
{
padding
:
0
.75rem
;
...
...
spring-boot-admin-server-ui/src/main/frontend/components/sba-panel.vue
View file @
6644673b
...
...
@@ -16,8 +16,14 @@
<
template
>
<div
class=
"card panel"
>
<header
v-if=
"title"
class=
"card-header"
>
<p
v-text=
"title"
class=
"card-header-title"
/>
<header
v-if=
"title || $slots['header']"
class=
"card-header"
>
<p
class=
"card-header-title"
>
<span
v-text=
"title"
/>
<slot
name=
"header"
/>
</p>
<div
class=
"panel__close"
>
<sba-icon-button
v-if=
"closeable"
:icon=
"['far', 'times-circle']"
@
click
.
stop=
"close"
/>
</div>
</header>
<div
v-if=
"$slots['default']"
class=
"card-content"
>
<slot/>
...
...
@@ -26,18 +32,40 @@
</
template
>
<
script
>
import
SbaIconButton
from
'./sba-icon-button'
;
export
default
{
components
:
{
SbaIconButton
},
props
:
{
title
:
{
type
:
String
,
required
:
true
},
closeable
:
{
type
:
Boolean
,
default
:
false
}
},
methods
:
{
close
(
event
)
{
this
.
$emit
(
'close'
,
event
);
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
.panel
{
margin-bottom
:
1
.5rem
;
&
__close
{
margin-right
:
0
.75em
;
color
:
$grey-light
;
display
:
flex
;
align-items
:
center
;
justify-self
:
flex-end
;
}
}
</
style
>
spring-boot-admin-server-ui/src/main/frontend/directives/popper.js
View file @
6644673b
...
...
@@ -19,7 +19,7 @@ import Popper from 'popper.js';
const
poppers
=
new
WeakMap
();
const
bind
=
(
el
,
binding
)
=>
{
const
reference
=
document
.
getElementById
(
binding
.
value
)
;
const
reference
=
typeof
binding
.
value
===
'string'
?
document
.
getElementById
(
binding
.
value
)
:
binding
.
value
;
if
(
reference
)
{
const
popper
=
new
Popper
(
reference
,
el
);
poppers
.
set
(
el
,
popper
);
...
...
spring-boot-admin-server-ui/src/main/frontend/services/instance.js
View file @
6644673b
...
...
@@ -55,7 +55,12 @@ class Instance {
}
async
fetchMetric
(
metric
,
tags
)
{
const
params
=
tags
?
{
tag
:
_
.
entries
(
tags
).
map
(([
name
,
value
])
=>
`
${
name
}
:
${
value
}
`
).
join
(
','
)}
:
{};
const
params
=
tags
?
{
tag
:
_
.
entries
(
tags
)
.
filter
(([,
value
])
=>
typeof
value
!==
'undefined'
&&
value
!==
null
)
.
map
(([
name
,
value
])
=>
`
${
name
}
:
${
value
}
`
)
.
join
(
','
)
}
:
{};
return
axios
.
get
(
uri
`instances/
${
this
.
id
}
/actuator/metrics/
${
metric
}
`
,
{
headers
:
{
'Accept'
:
actuatorMimeTypes
},
params
...
...
spring-boot-admin-server-ui/src/main/frontend/utils/collections.js
0 → 100644
View file @
6644673b
/*
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export
const
compareBy
=
mapper
=>
(
a
,
b
)
=>
{
const
valA
=
mapper
(
a
);
const
valB
=
mapper
(
b
);
return
valA
>
valB
?
1
:
valA
<
valB
?
-
1
:
0
;
};
spring-boot-admin-server-ui/src/main/frontend/views/applications/notification-filter-settings.vue
View file @
6644673b
...
...
@@ -29,8 +29,8 @@
</span>
</p>
</div>
<div
class=
"field"
>
<div
class=
"control
is-pulled-right
"
>
<div
class=
"field
is-grouped is-grouped-right
"
>
<div
class=
"control"
>
<button
class=
"button is-warning"
:class=
"
{'is-loading' : actionState === 'executing'}"
@click.stop="addFilter">
<font-awesome-icon
icon=
"bell-slash"
/>
Suppress
</button>
...
...
@@ -44,8 +44,8 @@
<strong
v-text=
"activeFilter.expiry ? activeFilter.expiry.locale('en').fromNow(true) : 'ever' "
/>
.
</p>
</div>
<div
class=
"field"
>
<div
class=
"control
is-pulled-right
"
>
<div
class=
"field
is-grouped is-grouped-right
"
>
<div
class=
"control"
>
<button
class=
"button"
:class=
"
{'is-loading' : actionState === 'executing'}" @click.stop="deleteActiveFilter">
<font-awesome-icon
icon=
"bell"
/>
Unsuppress
</button>
...
...
spring-boot-admin-server-ui/src/main/frontend/views/index.js
View file @
6644673b
...
...
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import
{
compareBy
}
from
'@/utils/collections'
;
import
{
view
as
aboutView
}
from
'./about'
;
import
{
view
as
applicationView
}
from
'./applications'
;
import
instanceViews
from
'./instances'
;
...
...
@@ -52,7 +53,7 @@ export default router => {
views
.
register
(
aboutView
);
views
.
register
(
wallboardView
);
instanceViews
.
forEach
(
views
.
register
);
views
.
sort
(
(
a
,
b
)
=>
a
.
order
-
b
.
order
);
views
.
sort
(
compareBy
(
v
=>
v
.
order
)
);
router
.
addRoutes
([{
path
:
'/'
,
redirect
:
{
name
:
'applications'
}}]);
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/index.js
View file @
6644673b
...
...
@@ -23,6 +23,7 @@ import sbaInstancesJolokia from './jolokia';
import
sbaInstancesLiquibase
from
'./liquibase'
;
import
sbaInstancesLogfile
from
'./logfile'
;
import
sbaInstancesLoggers
from
'./loggers'
;
import
sbaInstancesMetrics
from
'./metrics'
;
import
sbaInstancesSessions
from
'./sessions'
;
import
sbaInstancesShell
from
'./shell'
;
import
sbaInstancesThreaddump
from
'./threaddump'
;
...
...
@@ -32,6 +33,8 @@ export default [{
children
:
[{
path
:
''
,
component
:
sbaInstancesDetails
,
props
:
true
,
name
:
'instance/details'
},
{
path
:
'metrics'
,
component
:
sbaInstancesMetrics
,
props
:
true
,
name
:
'instance/metrics'
},
{
path
:
'env'
,
component
:
sbaInstancesEnv
,
props
:
true
,
name
:
'instance/env'
},
{
path
:
'logfile'
,
component
:
sbaInstancesLogfile
,
props
:
true
,
name
:
'instance/logfile'
...
...
@@ -57,6 +60,11 @@ export default [{
handle
:
'Details'
,
order
:
0
},
{
name
:
'instance/metrics'
,
handle
:
'Metrics'
,
order
:
50
,
isActive
:
({
instance
})
=>
instance
.
hasEndpoint
(
'metrics'
)
},
{
name
:
'instance/env'
,
handle
:
'Environment'
,
order
:
100
,
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/metrics/index.vue
0 → 100644
View file @
6644673b
<!--
- Copyright 2014-2018 the original author or authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-->
<
template
>
<section
class=
"section"
>
<div
class=
"container"
>
<div
v-if=
"error"
class=
"message is-danger"
>
<div
class=
"message-body"
>
<strong>
<font-awesome-icon
class=
"has-text-danger"
icon=
"exclamation-triangle"
/>
Fetching metrics failed.
</strong>
<p
v-text=
"error.message"
/>
</div>
</div>
<form
@
submit
.
prevent=
"handleSubmit"
class=
"field"
>
<div
class=
"field"
v-if=
"availableMetrics.length > 0"
>
<div
class=
"control"
>
<div
class=
"select"
>
<select
v-model=
"selectedMetric"
>
<option
v-for=
"metric in availableMetrics"
v-text=
"metric"
:key=
"metric"
/>
</select>
</div>
</div>
</div>
<div>
<p
v-if=
"stateFetchingTags === 'executing'"
class=
"is-loading"
>
Fetching available tags
</p>
<div
class=
"box"
v-if=
"availableTags"
>
<div
class=
"field is-horizontal"
v-for=
"tag in availableTags"
:key=
"tag.tag"
>
<div
class=
"field-label"
>
<label
class=
"label"
v-text=
"tag.tag"
/>
</div>
<div
class=
"field-body"
>
<div
class=
"control"
>
<div
class=
"select"
>
<select
v-model=
"selectedTags[tag.tag]"
>
<option
:value=
"undefined"
>
-
</option>
<option
v-for=
"value in tag.values"
:key=
"value"
:value=
"value"
v-text=
"value"
/>
</select>
</div>
</div>
</div>
</div>
<p
v-if=
"availableTags && availableTags.length === 0"
>
No tags available.
</p>
<div
class=
"field is-grouped is-grouped-right"
>
<div
class=
"control"
>
<button
type=
"submit"
class=
"button is-primary"
>
Add Metric
</button>
</div>
</div>
</div>
</div>
</form>
<metric
v-for=
"metric in metrics"
:key=
"metric.name"
:metric-name=
"metric.name"
:tag-selections=
"metric.tagSelections"
:instance=
"instance"
@
remove=
"removeMetric"
/>
</div>
</section>
</
template
>
<
script
>
import
Instance
from
'@/services/instance'
;
import
_
from
'lodash'
;
import
Metric
from
'./metric'
;
export
default
{
components
:
{
Metric
},
props
:
{
instance
:
{
type
:
Instance
,
required
:
true
}
},
data
:
()
=>
({
metrics
:
[],
error
:
null
,
availableMetrics
:
[],
selectedMetric
:
null
,
stateFetchingTags
:
null
,
availableTags
:
null
,
selectedTags
:
null
}),
created
()
{
this
.
fetchMetricIndex
();
},
watch
:
{
selectedMetric
:
'fetchAvailableTags'
},
methods
:
{
handleSubmit
()
{
this
.
addMetric
(
this
.
selectedMetric
,
this
.
selectedTags
)
},
removeMetric
(
metricName
,
idxTagSelection
)
{
const
idxMetric
=
this
.
metrics
.
findIndex
(
m
=>
m
.
name
===
metricName
);
if
(
idxMetric
>=
0
)
{
const
metric
=
this
.
metrics
[
idxMetric
];
if
(
idxTagSelection
<
metric
.
tagSelections
.
length
)
{
metric
.
tagSelections
.
splice
(
idxTagSelection
,
1
);
}
if
(
metric
.
tagSelections
.
length
===
0
)
{
this
.
metrics
.
splice
(
idxMetric
,
1
)
}
}
},
addMetric
(
metricName
,
tagSelection
=
{})
{
if
(
metricName
)
{
const
metric
=
this
.
metrics
.
find
(
m
=>
m
.
name
===
metricName
);
if
(
metric
)
{
metric
.
tagSelections
=
[...
metric
.
tagSelections
,
{...
tagSelection
}]
}
else
{
this
.
metrics
=
_
.
sortBy
([...
this
.
metrics
,
{
name
:
metricName
,
tagSelections
:
[{...
tagSelection
}]
}],
[
m
=>
m
.
name
]);
}
}
},
async
fetchMetricIndex
()
{
this
.
error
=
null
;
try
{
const
res
=
await
this
.
instance
.
fetchMetrics
();
this
.
availableMetrics
=
res
.
data
.
names
;
this
.
availableMetrics
.
sort
();
this
.
selectedMetric
=
this
.
availableMetrics
[
0
];
}
catch
(
error
)
{
console
.
warn
(
'Fetching metric index failed:'
,
error
);
this
.
hasLoaded
=
true
;
this
.
error
=
error
;
}
},
async
fetchAvailableTags
(
metricName
)
{
this
.
availableTags
=
null
;
this
.
stateFetchingTags
=
'executing'
;
try
{
const
response
=
await
this
.
instance
.
fetchMetric
(
metricName
);
this
.
availableTags
=
response
.
data
.
availableTags
;
this
.
stateFetchingTags
=
'completed'
;
this
.
selectedTags
=
{};
this
.
availableTags
.
forEach
(
t
=>
this
.
selectedTags
[
t
.
tag
]
=
undefined
);
}
catch
(
error
)
{
console
.
warn
(
'Fetching metric tags failed:'
,
error
);
this
.
stateFetchingTags
=
'failed'
;
}
}
}
}
</
script
>
spring-boot-admin-server-ui/src/main/frontend/views/instances/metrics/metric.vue
0 → 100644
View file @
6644673b
<!--
- Copyright 2014-2018 the original author or authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-->
<
template
>
<table
class=
"metrics table is-fullwidth is-bordered is-narrow"
>
<thead>
<tr>
<th
v-text=
"metricName"
/>
<th
class=
"metrics__statistic-name"
v-for=
"statistic in statistics"
:key=
"`head-$
{statistic.name}`">
<span
v-text=
"statistic.name"
/>
<div
class=
"select is-small is-pulled-right"
>
<select
v-model=
"statistic.type"
>
<option
:value=
"undefined"
>
-
</option>
<option
value=
"integer"
>
Integer
</option>
<option
value=
"float"
>
Float
</option>
<option
value=
"duration"
>
Duration
</option>
<option
value=
"millis"
>
Milliseconds
</option>
<option
value=
"bytes"
>
Bytes
</option>
</select>
</div>
</th>
<td/>
</tr>
</thead>
<tbody>
<tr
v-for=
"(tags, idx) in tagSelections"
:key=
"idx"
>
<td>
<span
v-text=
"getLabel(tags)"
/>
<span
class=
"has-text-warning"
v-if=
"errors[idx]"
:title=
"errors[idx]"
>
<font-awesome-icon
icon=
"exclamation-triangle"
/>
</span>
</td>
<td
class=
"metrics__statistic-value"
v-for=
"statistic in statistics"
:key=
"`value-$
{idx}-${statistic.name}`"
v-text="getValue(measurements[idx], statistic)"
/>
<td>
<sba-icon-button
:icon=
"'trash'"
@
click
.
stop=
"handleRemove(idx)"
/>
</td>
</tr>
</tbody>
</table>
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
Instance
from
'@/services/instance'
;
import
{
Observable
}
from
'@/utils/rxjs'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
import
prettyBytes
from
'pretty-bytes'
;
const
formatDuration
=
value
=>
{
const
duration
=
moment
.
duration
(
value
*
1000
);
return
`
${
Math
.
floor
(
duration
.
asDays
())}
d
${
duration
.
hours
()}
h
${
duration
.
minutes
()}
m
${
duration
.
seconds
()}
s`
;
};
const
formatMillis
=
value
=>
{
return
`
${
moment
.
duration
(
value
*
1000
).
asMilliseconds
().
toFixed
(
0
)}
ms`
;
};
export
default
{
name
:
'Metric'
,
mixins
:
[
subscribing
],
props
:
{
metricName
:
{
type
:
String
,
required
:
true
},
instance
:
{
type
:
Instance
,
required
:
true
},
tagSelections
:
{
type
:
Array
,
default
:
()
=>
[{}]
}
},
data
:
()
=>
({
measurements
:
[],
statistics
:
[],
errors
:
[],
}),
methods
:
{
handleRemove
(
idx
)
{
this
.
$emit
(
'remove'
,
this
.
metricName
,
idx
);
},
getValue
(
measurements
,
statistic
)
{
const
measurement
=
measurements
&&
measurements
.
find
(
m
=>
m
.
statistic
===
statistic
.
name
);
if
(
!
measurement
)
{
return
undefined
;
}
const
type
=
statistic
&&
statistic
.
type
;
switch
(
type
)
{
case
'integer'
:
return
measurement
.
value
.
toFixed
(
0
);
case
'float'
:
return
measurement
.
value
.
toFixed
(
4
);
case
'duration'
:
return
formatDuration
(
measurement
.
value
);
case
'millis'
:
return
formatMillis
(
measurement
.
value
);
case
'bytes'
:
return
prettyBytes
(
measurement
.
value
);
default
:
return
measurement
.
value
;
}
},
getLabel
(
tags
)
{
return
_
.
entries
(
tags
).
filter
(([,
value
])
=>
typeof
value
!==
'undefined'
)
.
map
(
pair
=>
pair
.
join
(
'='
))
.
join
(
' '
);
},
async
fetchMetric
(
tags
,
idx
)
{
try
{
const
response
=
await
this
.
instance
.
fetchMetric
(
this
.
metricName
,
tags
);
this
.
$set
(
this
.
errors
,
idx
,
null
);
this
.
$set
(
this
.
measurements
,
idx
,
response
.
data
.
measurements
);
if
(
idx
===
0
)
{
response
.
data
.
measurements
.
map
(
m
=>
m
.
statistic
)
.
filter
(
s
=>
!
this
.
statistics
.
some
(
stat
=>
stat
.
name
===
s
))
.
map
(
s
=>
({
name
:
s
,
type
:
undefined
}))
.
forEach
(
s
=>
this
.
statistics
.
push
(
s
));
}
}
catch
(
error
)
{
console
.
warn
(
`Fetching metric
${
this
.
metricName
}
failed:`
,
error
);
this
.
errors
[
idx
]
=
error
;
}
},
fetchAllTags
()
{
return
Observable
.
from
(
this
.
tagSelections
).
concatMap
(
this
.
fetchMetric
);
},
createSubscription
()
{
const
vm
=
this
;
return
Observable
.
timer
(
0
,
2500
)
.
concatMap
(
vm
.
fetchAllTags
)
.
subscribe
({
next
:
()
=>
{
}
});
}
},
watch
:
{
tagSelections
(
newVal
,
oldVal
)
{
newVal
.
map
((
v
,
i
)
=>
[
v
,
i
])
.
filter
(([
v
])
=>
!
oldVal
.
includes
(
v
))
.
forEach
(([
v
,
i
])
=>
this
.
fetchMetric
(
v
,
i
));
}
}
}
</
script
>
<
style
lang=
"scss"
>
table
.metrics
{
table-layout
:
fixed
;
}
.metrics
{
&
__statistic-name
*
{
vertical-align
:
middle
;
}
&
__statistic-value
{
text-align
:
right
;
}
}
</
style
>
spring-boot-admin-server-ui/src/main/frontend/views/journal/index.vue
View file @
6644673b
...
...
@@ -63,6 +63,7 @@
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
Instance
from
'@/services/instance'
;
import
{
compareBy
}
from
'@/utils/collections'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
...
...
@@ -120,7 +121,7 @@
try
{
this
.
addEvents
((
await
Instance
.
fetchEvents
()).
data
);
this
.
error
=
null
;
this
.
events
.
sort
(
(
a
,
b
)
=>
b
.
timestamp
-
a
.
timestamp
)
this
.
events
.
sort
(
compareBy
(
v
=>
v
.
timestamp
));
}
catch
(
error
)
{
console
.
warn
(
'Fetching events failed:'
,
error
);
this
.
error
=
error
;
...
...
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