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
69a964e3
Commit
69a964e3
authored
Jan 06, 2018
by
Johannes Edmeier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add charts for memory and datasources
Reduce bundlesize by picking parts of fontawesome and d3 Add mixings for subscribing components
parent
b3137edd
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
1018 additions
and
360 deletions
+1018
-360
application.properties
...ample-hazelcast/src/main/resources/application.properties
+16
-1
application.yml
...-boot-admin-sample-war/src/main/resources/application.yml
+4
-0
application.yml
...ring-boot-admin-sample/src/main/resources/application.yml
+3
-0
package-lock.json
spring-boot-admin-server-ui/package-lock.json
+0
-0
package.json
spring-boot-admin-server-ui/package.json
+10
-3
base.scss
...ot-admin-server-ui/src/main/frontend/assets/css/base.scss
+32
-0
index.js
spring-boot-admin-server-ui/src/main/frontend/index.js
+20
-11
subscribing.js
...t-admin-server-ui/src/main/frontend/mixins/subscribing.js
+41
-0
instance.js
...ot-admin-server-ui/src/main/frontend/services/instance.js
+3
-3
d3.js
spring-boot-admin-server-ui/src/main/frontend/utils/d3.js
+36
-0
index.vue
...-server-ui/src/main/frontend/views/applications/index.vue
+21
-23
datasource-chart.vue
...ain/frontend/views/instances/details/datasource-chart.vue
+134
-0
details-datasource.vue
...n/frontend/views/instances/details/details-datasource.vue
+73
-55
details-datasources.vue
.../frontend/views/instances/details/details-datasources.vue
+75
-0
details-health.vue
.../main/frontend/views/instances/details/details-health.vue
+2
-4
details-info.vue
...rc/main/frontend/views/instances/details/details-info.vue
+2
-4
details-memory.vue
.../main/frontend/views/instances/details/details-memory.vue
+130
-0
details-process.vue
...main/frontend/views/instances/details/details-process.vue
+98
-0
details-threads.vue
...main/frontend/views/instances/details/details-threads.vue
+22
-43
index.vue
...er-ui/src/main/frontend/views/instances/details/index.vue
+24
-7
mem-chart.vue
...i/src/main/frontend/views/instances/details/mem-chart.vue
+168
-0
process-uptime.js
...c/main/frontend/views/instances/details/process-uptime.js
+15
-33
index.vue
...server-ui/src/main/frontend/views/instances/env/index.vue
+2
-4
index.vue
...ver-ui/src/main/frontend/views/instances/flyway/index.vue
+2
-4
index.vue
...-ui/src/main/frontend/views/instances/liquibase/index.vue
+2
-4
index.vue
...er-ui/src/main/frontend/views/instances/logfile/index.vue
+8
-20
index.vue
...er-ui/src/main/frontend/views/instances/loggers/index.vue
+2
-4
index.vue
...ui/src/main/frontend/views/instances/threaddump/index.vue
+10
-31
threads-list.vue
...main/frontend/views/instances/threaddump/threads-list.vue
+1
-1
index.vue
...rver-ui/src/main/frontend/views/instances/trace/index.vue
+11
-32
traces-chart.vue
.../src/main/frontend/views/instances/trace/traces-chart.vue
+30
-51
traces-list.vue
...i/src/main/frontend/views/instances/trace/traces-list.vue
+3
-2
index.vue
...admin-server-ui/src/main/frontend/views/journal/index.vue
+17
-20
webpack.config.js
spring-boot-admin-server-ui/webpack.config.js
+1
-0
No files found.
spring-boot-admin-samples/spring-boot-admin-sample-hazelcast/src/main/resources/application.properties
View file @
69a964e3
spring.resources.cachePeriod
=
3600
#
# 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.
#
server.port
=
8081
info.stage
=
test
logging.file
=
/tmp/log.log
...
...
spring-boot-admin-samples/spring-boot-admin-sample-war/src/main/resources/application.yml
View file @
69a964e3
...
...
@@ -10,3 +10,6 @@ management:
endpoints
:
web
:
expose
:
"
*"
endpoint
:
health
:
show-details
:
true
\ No newline at end of file
spring-boot-admin-samples/spring-boot-admin-sample/src/main/resources/application.yml
View file @
69a964e3
...
...
@@ -10,6 +10,9 @@ management:
endpoints
:
web
:
expose
:
"
*"
endpoint
:
health
:
show-details
:
true
spring
:
application
:
...
...
spring-boot-admin-server-ui/package-lock.json
View file @
69a964e3
This diff is collapsed.
Click to expand it.
spring-boot-admin-server-ui/package.json
View file @
69a964e3
...
...
@@ -16,7 +16,14 @@
"@fortawesome/vue-fontawesome"
:
"0.0.22"
,
"axios"
:
"^0.17.1"
,
"bulma"
:
"^0.6.1"
,
"d3"
:
"^4.12.2"
,
"d3-array"
:
"^1.2.1"
,
"d3-axis"
:
"^1.0.8"
,
"d3-brush"
:
"^1.0.4"
,
"d3-scale"
:
"^1.0.7"
,
"d3-selection"
:
"^1.2.0"
,
"d3-shape"
:
"^1.2.0"
,
"d3-time"
:
"^1.0.8"
,
"d3-time-format"
:
"^2.1.1"
,
"event-source-polyfill"
:
"0.0.12"
,
"linkifyjs"
:
"^2.1.5"
,
"lodash"
:
"^4.17.4"
,
...
...
@@ -42,7 +49,7 @@
"clean-webpack-plugin"
:
"^0.1.17"
,
"cross-env"
:
"^5.1.3"
,
"css-hot-loader"
:
"^1.3.5"
,
"css-loader"
:
"^0.28.
4
"
,
"css-loader"
:
"^0.28.
8
"
,
"css-mqpacker"
:
"^6.0.1"
,
"eslint"
:
"^4.14.0"
,
"eslint-plugin-html"
:
"^4.0.1"
,
...
...
@@ -58,7 +65,7 @@
"lodash-webpack-plugin"
:
"^0.11.4"
,
"node-sass"
:
"^4.7.2"
,
"optimize-css-assets-webpack-plugin"
:
"^3.2.0"
,
"postcss-loader"
:
"^2.0.
9
"
,
"postcss-loader"
:
"^2.0.
10
"
,
"sass-loader"
:
"^6.0.6"
,
"style-loader"
:
"^0.19.1"
,
"url-loader"
:
"^0.6.2"
,
...
...
spring-boot-admin-server-ui/src/main/frontend/assets/css/base.scss
View file @
69a964e3
...
...
@@ -50,3 +50,34 @@
white-space
:
nowrap
;
}
}
//D3 Charts
.tick
{
&
line
{
stroke
:
currentColor
;
}
&
text
{
fill
:
currentColor
;
font-size
:
$size-7
;
}
}
path
.domain
{
stroke
:
currentColor
;
}
.
has-bullet
:
:
before
{
background
:
currentColor
;
content
:
''
;
width
:
0
.75em
;
height
:
0
.75em
;
display
:
inline-block
;
margin-right
:
0
.25em
;
}
@each
$name
,
$pair
in
$colors
{
.has-bullet.has-bullet-
#{
$name
}
::before
{
$color
:
nth
(
$pair
,
1
);
background-color
:
$color
;
}
}
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/index.js
View file @
69a964e3
...
...
@@ -14,18 +14,27 @@
* limitations under the License.
*/
import
fontawesome
from
'@fortawesome/fontawesome'
import
{
faGithub
,
faGitter
,
faStackOverflow
}
from
'@fortawesome/fontawesome-free-brands'
import
{
faBan
,
faBook
,
faCheck
,
faDownload
,
faExclamation
,
faMinusCircle
,
faQuestionCircle
,
faStepBackward
,
faStepForward
,
faTimesCircle
,
faTrash
}
from
'@fortawesome/fontawesome-free-solid'
import
FontAwesomeIcon
from
'@fortawesome/vue-fontawesome'
import
fontawesome
from
'@fortawesome/fontawesome'
;
import
faGithub
from
'@fortawesome/fontawesome-free-brands/faGithub'
;
import
faGitter
from
'@fortawesome/fontawesome-free-brands/faGitter'
;
import
faStackOverflow
from
'@fortawesome/fontawesome-free-brands/faStackOverflow'
;
import
faBan
from
'@fortawesome/fontawesome-free-solid/faBan'
;
import
faBook
from
'@fortawesome/fontawesome-free-solid/faBook'
;
import
faCheck
from
'@fortawesome/fontawesome-free-solid/faCheck'
;
import
faDownload
from
'@fortawesome/fontawesome-free-solid/faDownload'
;
import
faExclamation
from
'@fortawesome/fontawesome-free-solid/faExclamation'
;
import
faMinusCircle
from
'@fortawesome/fontawesome-free-solid/faMinusCircle'
;
import
faQuestionCircle
from
'@fortawesome/fontawesome-free-solid/faQuestionCircle'
;
import
faStepBackward
from
'@fortawesome/fontawesome-free-solid/faStepBackward'
;
import
faStepForward
from
'@fortawesome/fontawesome-free-solid/faStepForward'
;
import
faTimesCircle
from
'@fortawesome/fontawesome-free-solid/faTimesCircle'
;
import
faTrash
from
'@fortawesome/fontawesome-free-solid/faTrash'
;
import
FontAwesomeIcon
from
'@fortawesome/vue-fontawesome'
;
import
moment
from
'moment'
;
import
Vue
from
'vue'
import
VueRouter
from
'vue-router'
import
'./assets/css/base.scss'
import
'./assets/img/favicon.png'
import
Vue
from
'vue'
;
import
VueRouter
from
'vue-router'
;
import
'./assets/css/base.scss'
;
import
'./assets/img/favicon.png'
;
import
sbaComponents
from
'./components'
import
sbaAbout
from
'./views/about'
;
import
sbaApplications
from
'./views/applications'
;
...
...
spring-boot-admin-server-ui/src/main/frontend/mixins/subscribing.js
0 → 100644
View file @
69a964e3
/*
* 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
default
{
created
()
{
this
.
subscribe
();
},
beforeDestroy
()
{
this
.
unsubscribe
();
},
methods
:
{
async
subscribe
()
{
if
(
!
this
.
subscription
)
{
this
.
subscription
=
await
this
.
createSubscription
();
}
},
unsubscribe
()
{
if
(
this
.
subscription
)
{
try
{
this
.
subscription
.
unsubscribe
();
}
finally
{
this
.
subscription
=
null
;
}
}
}
}
}
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/services/instance.js
View file @
69a964e3
...
...
@@ -49,7 +49,7 @@ class Instance {
}
async
fetchMetric
(
metric
,
tags
)
{
const
params
=
tags
?
{
tag
s
:
_
.
entries
(
tags
).
map
(([
name
,
value
])
=>
`
${
name
}
:
${
value
}
`
)}
:
{};
const
params
=
tags
?
{
tag
:
_
.
entries
(
tags
).
map
(([
name
,
value
])
=>
`
${
name
}
:
${
value
}
`
).
join
(
','
)}
:
{};
return
axios
.
get
(
`instances/
${
this
.
id
}
/actuator/metrics/
${
metric
}
`
,
{
headers
:
{
'Accept'
:
actuatorMimeTypes
},
params
...
...
@@ -62,8 +62,8 @@ class Instance {
});
}
async
fetchEnv
()
{
return
await
axios
.
get
(
`instances/
${
this
.
id
}
/actuator/env`
,
{
async
fetchEnv
(
name
)
{
return
await
axios
.
get
(
`instances/
${
this
.
id
}
/actuator/env
/
${
name
||
''
}
`
,
{
headers
:
{
'Accept'
:
actuatorMimeTypes
}
});
}
...
...
spring-boot-admin-server-ui/src/main/frontend/utils/d3.js
0 → 100644
View file @
69a964e3
/*
* 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.
*/
import
*
as
array
from
'd3-array'
;
import
*
as
axis
from
'd3-axis'
;
import
*
as
brush
from
'd3-brush'
;
import
*
as
scale
from
'd3-scale'
;
import
*
as
selection
from
'd3-selection'
;
import
*
as
shape
from
'd3-shape'
;
import
*
as
time
from
'd3-time'
;
import
*
as
timeFormat
from
'd3-time-format'
;
export
default
{
...
shape
,
...
scale
,
...
axis
,
...
array
,
...
brush
,
...
time
,
...
timeFormat
,
...
selection
}
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/applications/index.vue
View file @
69a964e3
...
...
@@ -54,18 +54,19 @@
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
Application
from
'@/services/application'
import
*
as
_
from
'lodash'
;
import
applicationsList
from
'./applications-list.vue'
export
default
{
mixins
:
[
subscribing
],
components
:
{
applicationsList
,
},
data
:
()
=>
({
_applications
:
[],
errors
:
[],
subscription
:
null
errors
:
[]
}),
computed
:
{
applications
()
{
...
...
@@ -90,28 +91,25 @@
},
0
);
}
},
async
created
()
{
try
{
this
.
$data
.
_applications
=
(
await
Application
.
list
()).
data
;
}
catch
(
e
)
{
this
.
errors
.
push
(
e
);
}
methods
:
{
async
createSubscription
()
{
try
{
this
.
$data
.
_applications
=
(
await
Application
.
list
()).
data
;
}
catch
(
e
)
{
this
.
errors
.
push
(
e
);
}
this
.
subscription
=
(
await
Application
.
getStream
()).
subscribe
({
next
:
message
=>
{
const
idx
=
this
.
$data
.
_applications
.
findIndex
(
application
=>
application
.
name
===
message
.
data
.
name
);
if
(
idx
>=
0
)
{
this
.
$data
.
_applications
.
splice
(
idx
,
1
,
message
.
data
);
}
else
{
this
.
$data
.
_applications
.
push
(
message
.
data
);
}
},
error
:
err
=>
this
.
errors
.
push
(
err
)
});
},
destroyed
()
{
if
(
this
.
subscription
!==
null
)
{
this
.
subscription
.
unsubscribe
();
return
(
await
Application
.
getStream
()).
subscribe
({
next
:
message
=>
{
const
idx
=
this
.
$data
.
_applications
.
findIndex
(
application
=>
application
.
name
===
message
.
data
.
name
);
if
(
idx
>=
0
)
{
this
.
$data
.
_applications
.
splice
(
idx
,
1
,
message
.
data
);
}
else
{
this
.
$data
.
_applications
.
push
(
message
.
data
);
}
},
error
:
err
=>
this
.
errors
.
push
(
err
)
});
}
}
}
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/datasource-chart.vue
0 → 100644
View file @
69a964e3
<!--
- 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
>
<div
class=
"datasource-chart"
>
<svg
class=
"datasource-chart__svg"
></svg>
</div>
</
template
>
<
script
>
import
d3
from
'@/utils/d3'
;
import
moment
from
'moment'
;
export
default
{
props
:
[
'data'
],
methods
:
{
drawChart
(
_data
)
{
const
vm
=
this
;
const
data
=
_data
.
length
===
1
?
_data
.
concat
([{...
_data
[
0
],
timestamp
:
_data
[
0
].
timestamp
+
1
}])
:
_data
;
///setup x and y scale
const
extent
=
d3
.
extent
(
data
,
d
=>
d
.
timestamp
);
const
x
=
d3
.
scaleTime
()
.
range
([
0
,
vm
.
width
])
.
domain
(
extent
);
const
y
=
d3
.
scaleLinear
()
.
range
([
vm
.
height
,
0
])
.
domain
([
0
,
d3
.
max
(
data
,
d
=>
d
.
active
)
*
1.1
]);
//draw max
const
max
=
vm
.
areas
.
selectAll
(
'.datasource-chart__line--max'
)
.
data
([
data
]);
max
.
enter
().
append
(
'path'
)
.
merge
(
max
)
.
attr
(
'class'
,
'datasource-chart__line--max'
)
.
attr
(
'd'
,
d3
.
line
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y
(
d
=>
y
(
d
.
max
)));
max
.
exit
().
remove
();
//draw areas
const
active
=
vm
.
areas
.
selectAll
(
'.mem-chart__area--active'
)
.
data
([
data
]);
active
.
enter
().
append
(
'path'
)
.
merge
(
active
)
.
attr
(
'class'
,
'mem-chart__area--active'
)
.
attr
(
'd'
,
d3
.
area
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y0
(
y
(
0
))
.
y1
(
d
=>
y
(
d
.
active
)));
active
.
exit
().
remove
();
//draw axis
vm
.
xAxis
.
call
(
d3
.
axisBottom
(
x
)
.
ticks
(
5
)
.
tickFormat
(
d
=>
moment
(
d
).
format
(
"HH:mm:ss"
))
);
vm
.
yAxis
.
call
(
d3
.
axisLeft
(
y
)
.
ticks
(
5
)
);
},
},
mounted
()
{
const
margin
=
{
top
:
5
,
right
:
5
,
bottom
:
30
,
left
:
20
,
};
this
.
width
=
this
.
$el
.
getBoundingClientRect
().
width
-
margin
.
left
-
margin
.
right
;
this
.
height
=
this
.
$el
.
getBoundingClientRect
().
height
-
margin
.
top
-
margin
.
bottom
;
this
.
chartLayer
=
d3
.
select
(
this
.
$el
.
querySelector
(
'.datasource-chart__svg'
))
.
append
(
'g'
)
.
attr
(
'transform'
,
`translate(
${
margin
.
left
}
,
${
margin
.
top
}
)`
);
this
.
xAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'datasource-chart__axis-x'
)
.
attr
(
'transform'
,
`translate(0,
${
this
.
height
}
)`
);
this
.
yAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'datasource-chart__axis-y'
)
.
attr
(
'stroke'
,
null
);
this
.
areas
=
this
.
chartLayer
.
append
(
'g'
);
this
.
drawChart
(
this
.
data
);
},
watch
:
{
data
(
newVal
)
{
this
.
drawChart
(
newVal
);
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
.datasource-chart
{
&
__svg
{
height
:
125px
;
width
:
100%
;
}
&
__area
{
&
--active
{
fill
:
$info
;
opacity
:
0
.8
;
}
}
&
__line--max
{
stroke
:
$grey
;
}
}
</
style
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-datasource.vue
View file @
69a964e3
...
...
@@ -15,77 +15,95 @@
-->
<
template
>
<sba-panel
title=
"DataSources"
v-if=
"hasDataSources"
>
<div
class=
"content"
slot=
"text"
>
<table
class=
"table data-table"
>
<template
v-for=
"dataSource in dataSources"
>
<tr>
<td
:rowspan=
"3"
v-text=
"dataSource.name"
></td>
<td
colspan=
"2"
>
<span
class=
"is-pulled-left"
>
active connections
</span>
<span
v-text=
"dataSource.active"
></span>
<progress
v-if=
"dataSource.max >= 0"
class=
"progress is-small"
:value=
"dataSource.active"
:max=
"dataSource.max"
></progress>
</td>
</tr>
<tr>
<td>
min connections
</td>
<td
class=
"has-text-right"
v-text=
"dataSource.min"
></td>
</tr>
<tr>
<td>
max connections
</td>
<td
class=
"has-text-right"
v-text=
"dataSource.max"
v-if=
"dataSource.max >= 0"
></td>
<td
class=
"has-text-right"
v-else
>
unlimited
</td>
</tr>
</
template
>
</table>
<sba-panel
:title=
"`Datasource: $
{upperFirst(dataSource)}`" v-if="current">
<div
slot=
"text"
>
<div
class=
"level datasource-current"
>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading has-bullet has-bullet-info"
>
Active connections
</p>
<p
v-text=
"current.active"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading"
>
Min connections
</p>
<p
v-text=
"current.min"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading"
>
Max connections
</p>
<p
v-if=
"current.max >= 0"
v-text=
"current.max"
></p>
<p
v-else
>
unlimited
</p>
</div>
</div>
</div>
<datasource-chart
:data=
"chartData"
></datasource-chart>
</div>
</sba-panel>
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
{
Observable
}
from
'@/utils/rxjs'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
import
datasourceChart
from
'./datasource-chart'
;
export
default
{
props
:
[
'instance'
],
props
:
[
'instance'
,
'dataSource'
],
mixins
:
[
subscribing
],
components
:
{
datasourceChart
},
data
:
()
=>
({
hasDataSources
:
false
,
dataSources
:
[],
current
:
null
,
chartData
:
[],
}),
created
()
{
this
.
fetchMetrics
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchMetrics
();
}
instance
()
{
this
.
subscribe
()
},
dataSource
()
{
this
.
current
=
null
;
this
.
chartData
=
[];
}
},
methods
:
{
upperFirst
:
_
.
upperFirst
,
async
fetchMetrics
()
{
if
(
this
.
instance
)
{
try
{
const
response
=
await
this
.
instance
.
fetchMetric
(
'data.source.active.connections'
);
this
.
hasDataSources
=
true
;
const
dataSourcesNames
=
response
.
data
.
availableTags
.
filter
(
tag
=>
tag
.
tag
===
'name'
)[
0
].
values
;
dataSourcesNames
.
forEach
(
this
.
fetchDataSourceMetrics
);
}
catch
(
error
)
{
this
.
hasDataSources
=
false
;
}
}
},
async
fetchDataSourceMetrics
(
name
)
{
const
responseActive
=
this
.
instance
.
fetchMetric
(
'data.source.active.connections'
,
{
name
});
const
responseMin
=
this
.
instance
.
fetchMetric
(
'data.source.min.connections'
,
{
name
});
const
responseMax
=
this
.
instance
.
fetchMetric
(
'data.source.max.connections'
,
{
name
});
const
responseActive
=
this
.
instance
.
fetchMetric
(
'data.source.active.connections'
,
{
name
:
this
.
dataSource
});
const
responseMin
=
this
.
instance
.
fetchMetric
(
'data.source.min.connections'
,
{
name
:
this
.
dataSource
});
const
responseMax
=
this
.
instance
.
fetchMetric
(
'data.source.max.connections'
,
{
name
:
this
.
dataSource
});
this
.
dataSources
.
push
({
name
,
return
{
active
:
(
await
responseActive
).
data
.
measurements
[
0
].
value
,
min
:
(
await
responseMin
).
data
.
measurements
[
0
].
value
,
max
:
(
await
responseMax
).
data
.
measurements
[
0
].
value
});
};
},
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
return
Observable
.
timer
(
0
,
2500
)
.
concatMap
(
this
.
fetchMetrics
)
.
subscribe
({
next
:
data
=>
{
vm
.
current
=
data
;
vm
.
chartData
.
push
({...
data
,
timestamp
:
moment
.
now
().
valueOf
()});
},
errors
:
err
=>
{
vm
.
unsubscribe
();
}
});
}
}
}
}
</
script
>
\ No newline at end of file
</
script
>
<
style
lang=
"scss"
>
.datasource-current
{
margin-bottom
:
0
!
important
;
}
</
style
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-datasources.vue
0 → 100644
View file @
69a964e3
<!--
- 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
>
<div>
<details-datasource
v-for=
"dataSource in dataSources"
:key=
"dataSource"
:instance=
"instance"
:data-source=
"dataSource"
></details-datasource>
</div>
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
{
Observable
}
from
'@/utils/rxjs'
;
import
detailsDatasource
from
'./details-datasource'
;
export
default
{
props
:
[
'instance'
,
'type'
],
mixins
:
[
subscribing
],
components
:
{
detailsDatasource
},
data
:
()
=>
({
dataSources
:
[],
}),
watch
:
{
instance
()
{
this
.
subscribe
();
}
},
methods
:
{
async
fetchDataSources
()
{
if
(
this
.
instance
)
{
try
{
const
response
=
await
this
.
instance
.
fetchMetric
(
'data.source.active.connections'
);
return
response
.
data
.
availableTags
.
filter
(
tag
=>
tag
.
tag
===
'name'
)[
0
].
values
;
}
catch
(
error
)
{
return
[];
}
}
},
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
return
Observable
.
timer
(
0
,
2500
)
.
concatMap
(
this
.
fetchDataSources
)
.
subscribe
({
next
:
names
=>
{
vm
.
dataSources
=
names
},
errors
:
err
=>
{
vm
.
unsubscribe
();
}
});
}
}
}
}
</
script
>
<
style
lang=
"scss"
>
</
style
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-health.vue
View file @
69a964e3
...
...
@@ -37,10 +37,8 @@
this
.
fetchHealth
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchHealth
();
}
instance
()
{
this
.
fetchHealth
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-info.vue
View file @
69a964e3
...
...
@@ -40,10 +40,8 @@
this
.
fetchInfo
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchInfo
();
}
instance
()
{
this
.
fetchInfo
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-memory.vue
0 → 100644
View file @
69a964e3
<!--
- 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
>
<sba-panel
:title=
"`Memory: $
{name}`" v-if="current">
<div
slot=
"text"
>
<div
class=
"level memory-current"
>
<div
class=
"level-item has-text-centered"
v-if=
"current.metaspace"
>
<div>
<p
class=
"heading has-bullet has-bullet-primary"
>
Metaspace
</p>
<p
v-text=
"prettyBytes(current.metaspace)"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading has-bullet has-bullet-info"
>
Used
</p>
<p
v-text=
"prettyBytes(current.used)"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading has-bullet has-bullet-warning"
>
Size
</p>
<p
v-text=
"prettyBytes(current.committed)"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
v-if=
"current.max >= 0"
>
<div>
<p
class=
"heading"
>
Max
</p>
<p
v-text=
"prettyBytes(current.max)"
></p>
</div>
</div>
</div>
<mem-chart
:data=
"chartData"
></mem-chart>
</div>
</sba-panel>
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
{
Observable
}
from
'@/utils/rxjs'
;
import
moment
from
'moment'
;
import
prettyBytes
from
'pretty-bytes'
;
import
memChart
from
'./mem-chart'
;
export
default
{
props
:
[
'instance'
,
'type'
],
mixins
:
[
subscribing
],
components
:
{
memChart
},
data
:
()
=>
({
current
:
null
,
chartData
:
[],
}),
computed
:
{
name
()
{
switch
(
this
.
type
)
{
case
'heap'
:
return
'Heap'
;
case
'nonheap'
:
return
'Non heap'
;
default
:
return
this
.
type
;
}
}
},
watch
:
{
instance
()
{
this
.
subscribe
();
}
},
methods
:
{
prettyBytes
,
async
fetchMetrics
()
{
if
(
this
.
instance
)
{
const
responseMax
=
this
.
instance
.
fetchMetric
(
'jvm.memory.max'
,
{
area
:
this
.
type
});
const
responseUsed
=
this
.
instance
.
fetchMetric
(
'jvm.memory.used'
,
{
area
:
this
.
type
});
const
responeMetaspace
=
this
.
type
===
'nonheap'
?
this
.
instance
.
fetchMetric
(
'jvm.memory.used'
,
{
area
:
this
.
type
,
id
:
'Metaspace'
})
:
null
;
const
responseCommitted
=
this
.
instance
.
fetchMetric
(
'jvm.memory.committed'
,
{
area
:
this
.
type
});
return
{
max
:
(
await
responseMax
).
data
.
measurements
[
0
].
value
,
used
:
(
await
responseUsed
).
data
.
measurements
[
0
].
value
,
metaspace
:
responeMetaspace
?
(
await
responeMetaspace
).
data
.
measurements
[
0
].
value
:
null
,
committed
:
(
await
responseCommitted
).
data
.
measurements
[
0
].
value
};
}
},
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
return
Observable
.
timer
(
0
,
2500
)
.
concatMap
(
this
.
fetchMetrics
)
.
subscribe
({
next
:
data
=>
{
vm
.
current
=
data
;
vm
.
chartData
.
push
({...
data
,
timestamp
:
moment
.
now
().
valueOf
()});
},
errors
:
err
=>
{
vm
.
unsubscribe
();
}
});
}
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
.memory-current
{
margin-bottom
:
0
!
important
;
}
</
style
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-process.vue
0 → 100644
View file @
69a964e3
<!--
- 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
>
<sba-panel
title=
"Process"
v-if=
"pid || metrics['process.uptime'] "
>
<div
class=
"level"
slot=
"text"
>
<div
class=
"level-item has-text-centered"
v-if=
"pid"
>
<div>
<p
class=
"heading"
>
PID
</p>
<p
v-text=
"pid"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
v-if=
"metrics['process.uptime']"
>
<div>
<p
class=
"heading"
>
Uptime
</p>
<p>
<process-uptime
:value=
"metrics['process.uptime']"
></process-uptime>
</p>
</div>
</div>
<div
class=
"level-item has-text-centered"
v-if=
"metrics['system.cpu.count']"
>
<div>
<p
class=
"heading"
>
CPUs
</p>
<p
v-text=
"metrics['system.cpu.count']"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
v-if=
"metrics['system.load.average.1m']"
>
<div>
<p
class=
"heading"
>
System Load (last 1m)
</p>
<p
v-text=
"metrics['system.load.average.1m'].toFixed(2)"
></p>
</div>
</div>
</div>
</sba-panel>
</
template
>
<
script
>
import
_
from
'lodash'
;
import
processUptime
from
'./process-uptime'
export
default
{
props
:
[
'instance'
],
components
:
{
processUptime
},
data
:
()
=>
({
pid
:
null
,
metrics
:
{
'process.uptime'
:
null
,
'system.load.average.1m'
:
null
,
'system.cpu.count'
:
null
,
}
}),
created
()
{
this
.
fetchMetrics
();
this
.
fetchPid
();
},
watch
:
{
instance
()
{
this
.
fetchMetrics
();
this
.
fetchPid
();
}
},
methods
:
{
async
fetchMetrics
()
{
if
(
this
.
instance
)
{
const
vm
=
this
;
_
.
entries
(
vm
.
metrics
).
forEach
(
async
([
name
])
=>
{
const
response
=
await
vm
.
instance
.
fetchMetric
(
name
);
vm
.
metrics
[
name
]
=
response
.
data
.
measurements
[
0
].
value
;
}
)
}
},
async
fetchPid
()
{
if
(
this
.
instance
)
{
const
vm
=
this
;
const
response
=
await
vm
.
instance
.
fetchEnv
(
'PID'
);
vm
.
pid
=
response
.
data
.
property
.
value
;
}
}
}
}
</
script
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-
jvm
.vue
→
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-
threads
.vue
View file @
69a964e3
...
...
@@ -15,56 +15,37 @@
-->
<
template
>
<sba-panel
title=
"JVM"
v-if=
"metrics['process.uptime']"
>
<div
class=
"content"
slot=
"text"
>
<table
class=
"table data-table"
>
<tr>
<td>
Uptime
</td>
<td
colspan=
"2"
>
<jvm-uptime
:value=
"metrics['process.uptime']"
></jvm-uptime>
</td>
</tr>
<tr
v-if=
"metrics['system.load.average.1m']"
>
<td>
System load (last 1m)
</td>
<td
colspan=
"2"
v-text=
"metrics['system.load.average.1m'].toFixed(2)"
></td>
</tr>
<tr>
<td>
Available CPUs
</td>
<td
colspan=
"2"
v-text=
"metrics['system.cpu.count']"
></td>
</tr>
<tr>
<td
rowspan=
"3"
>
Threads
</td>
<td>
live
</td>
<td
v-text=
"metrics['jvm.threads.live']"
></td>
</tr>
<tr>
<td>
peak
</td>
<td
v-text=
"metrics['jvm.threads.peak']"
></td>
</tr>
<tr>
<td>
daemon
</td>
<td
v-text=
"metrics['jvm.threads.daemon']"
></td>
</tr>
</table>
<sba-panel
title=
"Threads"
v-if=
"metrics['jvm.threads.live']"
>
<div
class=
"level"
slot=
"text"
>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading"
>
Live
</p>
<p
v-text=
"metrics['jvm.threads.live']"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading"
>
Live Peak
</p>
<p
v-text=
"metrics['jvm.threads.peak']"
></p>
</div>
</div>
<div
class=
"level-item has-text-centered"
>
<div>
<p
class=
"heading"
>
Daemon
</p>
<p
v-text=
"metrics['jvm.threads.daemon']"
></p>
</div>
</div>
</div>
</sba-panel>
</
template
>
<
script
>
import
_
from
'lodash'
;
import
jvmUptime
from
'./jvm-uptime'
export
default
{
props
:
[
'instance'
],
components
:
{
jvmUptime
},
data
:
()
=>
({
metrics
:
{
'process.uptime'
:
null
,
'system.load.average.1m'
:
null
,
'system.cpu.count'
:
null
,
'jvm.threads.live'
:
null
,
'jvm.threads.peak'
:
null
,
'jvm.threads.daemon'
:
null
...
...
@@ -74,10 +55,8 @@
this
.
fetchMetrics
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchMetrics
();
}
instance
()
{
this
.
fetchMetrics
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/index.vue
View file @
69a964e3
...
...
@@ -20,11 +20,26 @@
<div
class=
"columns"
>
<div
class=
"column"
>
<details-info
:instance=
"instance"
></details-info>
<details-jvm
:instance=
"instance"
></details-jvm>
</div>
<div
class=
"column"
>
<details-health
:instance=
"instance"
></details-health>
<details-datasource
:instance=
"instance"
></details-datasource>
</div>
</div>
<div
class=
"columns"
>
<div
class=
"column"
>
<details-process
:instance=
"instance"
></details-process>
<details-memory
:instance=
"instance"
type=
"heap"
></details-memory>
</div>
<div
class=
"column"
>
<details-threads
:instance=
"instance"
></details-threads>
<details-memory
:instance=
"instance"
type=
"nonheap"
></details-memory>
</div>
</div>
<div
class=
"columns"
>
<div
class=
"column"
>
<details-datasources
:instance=
"instance"
></details-datasources>
</div>
<div
class=
"column"
>
</div>
</div>
</div>
...
...
@@ -32,14 +47,16 @@
</
template
>
<
script
>
import
detailsDatasource
from
'./details-datasource'
;
import
detailsHealth
from
'./details-health.vue'
;
import
detailsInfo
from
'./details-info.vue'
;
import
detailsJvm
from
'./details-jvm.vue'
;
import
detailsDatasources
from
'./details-datasources'
;
import
detailsHealth
from
'./details-health'
;
import
detailsInfo
from
'./details-info'
;
import
detailsMemory
from
'./details-memory'
;
import
detailsProcess
from
'./details-process'
;
import
detailsThreads
from
'./details-threads'
;
export
default
{
components
:
{
detailsHealth
,
detailsInfo
,
details
Jvm
,
detailsDatasource
detailsHealth
,
detailsInfo
,
details
Process
,
detailsThreads
,
detailsDatasources
,
detailsMemory
},
props
:
[
'instance'
]
}
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/mem-chart.vue
0 → 100644
View file @
69a964e3
<!--
- 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
>
<div
class=
"mem-chart"
>
<svg
class=
"mem-chart__svg"
></svg>
</div>
</
template
>
<
script
>
import
d3
from
'@/utils/d3'
;
import
moment
from
'moment'
;
import
prettyBytes
from
'pretty-bytes'
;
export
default
{
props
:
[
'data'
],
data
:
()
=>
({}),
methods
:
{
drawChart
(
_data
)
{
const
vm
=
this
;
const
data
=
_data
.
length
===
1
?
_data
.
concat
([{...
_data
[
0
],
timestamp
:
_data
[
0
].
timestamp
+
1
}])
:
_data
;
///setup x and y scale
const
extent
=
d3
.
extent
(
data
,
d
=>
d
.
timestamp
);
const
x
=
d3
.
scaleTime
()
.
range
([
0
,
vm
.
width
])
.
domain
(
extent
);
const
y
=
d3
.
scaleLinear
()
.
range
([
vm
.
height
,
0
])
.
domain
([
0
,
d3
.
max
(
data
,
d
=>
d
.
committed
)
*
1.1
]);
//draw max
const
max
=
vm
.
areas
.
selectAll
(
'.mem-chart__line--max'
)
.
data
([
data
]);
max
.
enter
().
append
(
'path'
)
.
merge
(
max
)
.
attr
(
'class'
,
'mem-chart__line--max'
)
.
attr
(
'd'
,
d3
.
line
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y
(
d
=>
y
(
d
.
max
)));
max
.
exit
().
remove
();
//draw areas
const
committed
=
vm
.
areas
.
selectAll
(
'.mem-chart__area--committed'
)
.
data
([
data
]);
committed
.
enter
().
append
(
'path'
)
.
merge
(
committed
)
.
attr
(
'class'
,
'mem-chart__area--committed'
)
.
attr
(
'd'
,
d3
.
area
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y0
(
d
=>
y
(
d
.
used
))
.
y1
(
d
=>
y
(
d
.
committed
)));
committed
.
exit
().
remove
();
const
used
=
vm
.
areas
.
selectAll
(
'.mem-chart__area--used'
)
.
data
([
data
]);
used
.
enter
().
append
(
'path'
)
.
merge
(
used
)
.
attr
(
'class'
,
'mem-chart__area--used'
)
.
attr
(
'd'
,
d3
.
area
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y0
(
d
=>
y
(
d
.
metaspace
||
0
))
.
y1
(
d
=>
y
(
d
.
used
)));
used
.
exit
().
remove
();
const
metaspace
=
vm
.
areas
.
selectAll
(
'.mem-chart__area--metaspace'
)
.
data
([
data
]);
metaspace
.
enter
().
append
(
'path'
)
.
merge
(
metaspace
)
.
attr
(
'class'
,
'mem-chart__area--metaspace'
)
.
attr
(
'd'
,
d3
.
area
()
.
x
(
d
=>
x
(
d
.
timestamp
))
.
y0
(
y
(
0
))
.
y1
(
d
=>
y
(
d
.
metaspace
)));
metaspace
.
exit
().
remove
();
//draw axis
vm
.
xAxis
.
call
(
d3
.
axisBottom
(
x
)
.
ticks
(
5
)
.
tickFormat
(
d
=>
moment
(
d
).
format
(
"HH:mm:ss"
))
);
vm
.
yAxis
.
call
(
d3
.
axisLeft
(
y
)
.
ticks
(
5
)
.
tickFormat
(
prettyBytes
)
);
},
},
mounted
()
{
const
margin
=
{
top
:
5
,
right
:
5
,
bottom
:
30
,
left
:
50
,
};
this
.
width
=
this
.
$el
.
getBoundingClientRect
().
width
-
margin
.
left
-
margin
.
right
;
this
.
height
=
this
.
$el
.
getBoundingClientRect
().
height
-
margin
.
top
-
margin
.
bottom
;
this
.
chartLayer
=
d3
.
select
(
this
.
$el
.
querySelector
(
'.mem-chart__svg'
))
.
append
(
'g'
)
.
attr
(
'transform'
,
`translate(
${
margin
.
left
}
,
${
margin
.
top
}
)`
);
this
.
xAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'mem-chart__axis-x'
)
.
attr
(
'transform'
,
`translate(0,
${
this
.
height
}
)`
);
this
.
yAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'mem-chart__axis-y'
)
.
attr
(
'stroke'
,
null
);
this
.
areas
=
this
.
chartLayer
.
append
(
'g'
);
this
.
drawChart
(
this
.
data
);
},
watch
:
{
data
(
newVal
)
{
this
.
drawChart
(
newVal
);
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
.mem-chart
{
&
__svg
{
height
:
125px
;
width
:
100%
;
}
&
__area
{
&
--committed
{
fill
:
$warning
;
opacity
:
0
.8
;
}
&
--used
{
fill
:
$info
;
opacity
:
0
.8
;
}
&
--metaspace
{
fill
:
$primary
;
opacity
:
0
.8
;
}
}
&
__line--max
{
stroke
:
$grey
;
}
}
</
style
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/
jvm
-uptime.js
→
spring-boot-admin-server-ui/src/main/frontend/views/instances/details/
process
-uptime.js
View file @
69a964e3
...
...
@@ -14,11 +14,13 @@
* limitations under the License.
*/
import
{
Observable
}
from
'@/utils/rxjs'
import
subscribing
from
'@/mixins/subscribing'
;
import
{
Observable
}
from
'@/utils/rxjs'
;
import
moment
from
'moment'
;
export
default
{
props
:
[
'value'
],
mixins
:
[
subscribing
],
data
:
()
=>
({
startTs
:
null
,
offset
:
null
...
...
@@ -36,41 +38,21 @@ export default {
}
},
watch
:
{
value
(
newVal
)
{
this
.
stop
();
if
(
newVal
)
{
this
.
start
();
}
}
},
mounted
()
{
if
(
this
.
value
&&
this
.
subscription
==
null
)
{
this
.
start
();
value
()
{
this
.
subscribe
();
}
},
beforeDestroy
()
{
this
.
stop
();
},
methods
:
{
start
()
{
const
vm
=
this
;
this
.
startTs
=
moment
.
now
();
this
.
offset
=
0
;
this
.
subscription
=
Observable
.
timer
(
0
,
1000
).
subscribe
({
next
:
()
=>
{
vm
.
offset
=
moment
.
now
().
valueOf
()
-
this
.
startTs
.
valueOf
();
}
})
},
stop
()
{
this
.
startTs
=
null
;
this
.
offset
=
null
;
if
(
this
.
subscription
)
{
try
{
this
.
subscription
.
unsubscribe
();
}
finally
{
this
.
subscription
=
null
;
}
createSubscription
()
{
if
(
this
.
value
)
{
const
vm
=
this
;
this
.
startTs
=
moment
.
now
();
this
.
offset
=
0
;
return
Observable
.
timer
(
0
,
1000
).
subscribe
({
next
:
()
=>
{
vm
.
offset
=
moment
.
now
().
valueOf
()
-
this
.
startTs
.
valueOf
();
}
})
}
}
}
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/env/index.vue
View file @
69a964e3
...
...
@@ -93,10 +93,8 @@
this
.
fetchEnv
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchEnv
();
}
instance
()
{
this
.
fetchEnv
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/flyway/index.vue
View file @
69a964e3
...
...
@@ -67,10 +67,8 @@
this
.
fetchFlyway
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchFlyway
();
}
instance
()
{
this
.
fetchFlyway
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/liquibase/index.vue
View file @
69a964e3
...
...
@@ -46,10 +46,8 @@
this
.
fetchLiquibase
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchLiquibase
();
}
instance
()
{
this
.
fetchLiquibase
();
}
},
methods
:
{
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/logfile/index.vue
View file @
69a964e3
...
...
@@ -35,17 +35,18 @@
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
{
animationFrame
,
Observable
}
from
'@/utils/rxjs'
;
import
_
from
'lodash'
;
import
prettyBytes
from
'pretty-bytes'
;
export
default
{
props
:
[
'instance'
],
mixins
:
[
subscribing
],
data
:
()
=>
({
subscription
:
null
,
atBottom
:
true
,
atTop
:
false
,
skippedBytes
:
null
,
skippedBytes
:
null
}),
computed
:
{
skippedHumanBytes
()
{
...
...
@@ -53,16 +54,12 @@
}
},
watch
:
{
async
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
stop
();
this
.
start
();
}
async
instance
()
{
this
.
subscribe
();
}
},
created
()
{
this
.
scrollParent
=
null
;
this
.
start
();
},
mounted
()
{
this
.
scrollParent
=
document
.
documentElement
;
...
...
@@ -73,10 +70,10 @@
window
.
removeEventListener
(
'scroll'
,
this
.
onScroll
);
},
methods
:
{
start
()
{
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
this
.
subscription
=
this
.
instance
.
streamLogfile
(
1000
)
return
this
.
instance
.
streamLogfile
(
1000
)
.
do
(
chunk
=>
vm
.
skippedBytes
=
vm
.
skippedBytes
||
chunk
.
skipped
)
.
concatMap
(
chunk
=>
_
.
chunk
(
chunk
.
addendum
.
split
(
/
\r?\n
/
),
250
))
.
map
(
lines
=>
Observable
.
of
(
lines
,
animationFrame
))
...
...
@@ -94,20 +91,11 @@
}
},
errors
:
err
=>
{
vm
.
stop
();
vm
.
unsubscribe
();
}
});
}
},
stop
()
{
if
(
this
.
subscription
)
{
try
{
this
.
subscription
.
unsubscribe
();
}
finally
{
this
.
subscription
=
null
;
}
}
},
onScroll
()
{
this
.
atBottom
=
this
.
scrollParent
.
scrollTop
>=
this
.
scrollParent
.
scrollHeight
-
this
.
scrollParent
.
clientHeight
;
this
.
atTop
=
this
.
scrollParent
.
scrollTop
===
0
;
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/loggers/index.vue
View file @
69a964e3
...
...
@@ -165,10 +165,8 @@
window
.
removeEventListener
(
'scroll'
,
this
.
onScroll
);
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
fetchLoggers
();
}
instance
()
{
this
.
fetchLoggers
();
}
}
}
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/index.vue
View file @
69a964e3
...
...
@@ -25,32 +25,24 @@
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
_
from
'lodash'
;
import
moment
from
'moment-shortformat'
;
import
threadsList
from
'./threads-list'
;
export
default
{
props
:
[
'instance'
],
mixins
:
[
subscribing
],
components
:
{
threadsList
},
data
:
()
=>
({
threads
:
null
,
subscription
:
null
threads
:
null
}),
computed
:
{},
created
()
{
this
.
start
();
},
beforeDestroy
()
{
this
.
stop
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
stop
();
this
.
start
()
}
instance
()
{
this
.
subscribe
()
},
},
methods
:
{
...
...
@@ -100,33 +92,20 @@
entry
.
timeline
[
entry
.
timeline
.
length
-
1
].
end
=
now
;
});
},
start
()
{
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
this
.
subscription
=
this
.
instance
.
streamThreaddump
(
1000
)
return
this
.
instance
.
streamThreaddump
(
1000
)
.
subscribe
({
next
:
threads
=>
{
vm
.
updateTimelines
(
threads
);
},
errors
:
err
=>
{
vm
.
stop
();
vm
.
unsubscribe
();
}
});
}
},
stop
()
{
if
(
this
.
subscription
)
{
try
{
this
.
subscription
.
unsubscribe
();
}
finally
{
this
.
subscription
=
null
;
}
}
}
}
}
</
script
>
<
style
lang=
"scss"
>
</
style
>
\ No newline at end of file
</
script
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/threaddump/threads-list.vue
View file @
69a964e3
...
...
@@ -100,7 +100,7 @@
</div>
</template>
<
script
>
import
*
as
d3
from
'd3'
;
import
d3
from
'@/utils/d3'
import
_
from
'lodash'
;
import
threadTag
from
'./thread-tag'
;
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/trace/index.vue
View file @
69a964e3
...
...
@@ -77,6 +77,7 @@
</
template
>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
moment
from
'moment'
;
import
sbaTracesChart
from
'./traces-chart'
;
import
sbaTracesList
from
'./traces-list'
;
...
...
@@ -144,13 +145,13 @@
}
export
default
{
props
:
[
'instance'
],
mixins
:
[
subscribing
],
components
:
{
sbaTracesList
,
sbaTracesChart
},
props
:
[
'instance'
],
data
:
()
=>
({
traces
:
null
,
subscription
:
null
,
filter
:
null
,
excludeActuator
:
true
,
showSuccess
:
true
,
...
...
@@ -180,45 +181,27 @@
return
this
.
filteredTraces
.
filter
(
trace
=>
!
trace
.
timestamp
.
isBefore
(
start
)
&&
!
trace
.
timestamp
.
isAfter
(
end
));
}
},
created
()
{
this
.
start
();
},
beforeDestroy
()
{
this
.
stop
();
},
watch
:
{
instance
(
newVal
,
oldVal
)
{
if
(
newVal
!==
oldVal
)
{
this
.
stop
();
this
.
start
();
}
instance
()
{
this
.
subscribe
();
},
},
methods
:
{
start
()
{
createSubscription
()
{
const
vm
=
this
;
if
(
this
.
instance
)
{
this
.
subscription
=
this
.
instance
.
streamTrace
(
5
*
1000
)
return
this
.
instance
.
streamTrace
(
5
*
1000
)
.
subscribe
({
next
:
rawTraces
=>
{
const
traces
=
rawTraces
.
map
(
trace
=>
new
Trace
(
trace
.
timestamp
,
trace
.
info
));
vm
.
traces
=
vm
.
traces
?
traces
.
concat
(
vm
.
traces
)
:
traces
;
},
errors
:
err
=>
{
vm
.
stop
();
vm
.
unsubscribe
();
}
});
}
},
stop
()
{
if
(
this
.
subscription
)
{
try
{
this
.
subscription
.
unsubscribe
();
}
finally
{
this
.
subscription
=
null
;
}
}
},
getFilterFn
()
{
let
filterFn
=
null
;
if
(
this
.
actuatorPath
!==
null
&&
this
.
excludeActuator
)
{
...
...
@@ -238,11 +221,7 @@
filterFn
=
addToFilter
(
filterFn
,
(
trace
)
=>
!
trace
.
isServerError
());
}
return
filterFn
;
}
,
}
}
}
</
script
>
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
</
style
>
\ No newline at end of file
</
script
>
\ No newline at end of file
spring-boot-admin-server-ui/src/main/frontend/views/instances/trace/traces-chart.vue
View file @
69a964e3
...
...
@@ -51,7 +51,8 @@
</
template
>
<
script
>
import
*
as
d3
from
'd3'
;
import
d3
from
'@/utils/d3'
;
import
{
event
as
d3Event
}
from
'd3-selection'
;
//see https://github.com/d3/d3/issues/2733#issuecomment-190743489
import
moment
from
'moment-shortformat'
;
const
interval
=
1000
;
...
...
@@ -144,18 +145,18 @@
///setup x and y scale
const
x
=
d3
.
scaleTime
()
.
range
([
0
,
vm
.
width
])
.
domain
(
d3
.
extent
(
data
,
(
d
)
=>
d
.
timeStart
));
.
domain
(
d3
.
extent
(
data
,
d
=>
d
.
timeStart
));
this
.
x
=
x
;
const
y
=
d3
.
scaleLinear
()
.
range
([
vm
.
height
,
0
])
.
domain
([
0
,
d3
.
max
(
data
,
(
d
)
=>
d
.
totalCount
)]);
.
domain
([
0
,
d3
.
max
(
data
,
d
=>
d
.
totalCount
)]);
//
setup
areas
//
draw
areas
const
area
=
d3
.
area
()
.
x
(
(
d
)
=>
x
(
d
.
data
.
timeStart
))
.
y0
(
(
d
)
=>
y
(
d
[
0
]))
.
y1
(
(
d
)
=>
y
(
d
[
1
]));
.
x
(
d
=>
x
(
d
.
data
.
timeStart
))
.
y0
(
d
=>
y
(
d
[
0
]))
.
y1
(
d
=>
y
(
d
[
1
]));
const
stack
=
d3
.
stack
()
.
keys
([
'totalSuccess'
,
'totalClientErrors'
,
'totalServerErrors'
]);
...
...
@@ -165,19 +166,19 @@
d
.
enter
().
append
(
'path'
)
.
merge
(
d
)
.
attr
(
'class'
,
(
d
)
=>
`trace-chart__area trace-chart__area--
${
d
.
key
}
`
)
.
attr
(
'class'
,
d
=>
`trace-chart__area trace-chart__area--
${
d
.
key
}
`
)
.
attr
(
'd'
,
area
);
d
.
exit
().
remove
();
//
setup
axis
//
draw
axis
vm
.
xAxis
.
call
(
d3
.
axisBottom
(
x
)
.
ticks
(
10
)
.
tickFormat
(
d
=>
`-
${
moment
(
d
).
short
(
true
)}
`
)
);
vm
.
yAxis
.
call
(
d3
.
axisRight
(
y
)
.
ticks
(
Math
.
min
(
5
,
d3
.
max
(
data
,
(
d
)
=>
d
.
totalCount
)))
.
ticks
(
Math
.
min
(
5
,
d3
.
max
(
data
,
d
=>
d
.
totalCount
)))
.
tickSize
(
this
.
width
)
).
call
(
axis
=>
axis
.
selectAll
(
'.tick text'
)
...
...
@@ -186,30 +187,30 @@
.
attr
(
'text-anchor'
,
'end'
)
);
//
setup
brush selection
//
draw
brush selection
const
brush
=
d3
.
brushX
()
.
extent
([[
0
,
0
],
[
vm
.
width
,
vm
.
height
]])
.
on
(
'start'
,
()
=>
{
if
(
d3
.
e
vent
.
selection
)
{
if
(
d3
E
vent
.
selection
)
{
vm
.
isBrushing
=
true
;
vm
.
hovered
=
null
;
}
})
.
on
(
'brush'
,
function
()
{
if
(
d3
.
event
.
sourceEvent
===
null
||
d3
.
e
vent
.
sourceEvent
.
type
===
'brush'
)
{
if
(
d3
Event
.
sourceEvent
===
null
||
d3E
vent
.
sourceEvent
.
type
===
'brush'
)
{
return
;
}
if
(
d3
.
e
vent
.
selection
)
{
const
floor
=
Math
.
floor
(
x
.
invert
(
d3
.
e
vent
.
selection
[
0
])
/
interval
)
*
interval
;
const
ceil
=
Math
.
ceil
(
x
.
invert
(
d3
.
e
vent
.
selection
[
1
])
/
interval
)
*
interval
;
d3
.
select
(
this
).
call
(
d3
.
e
vent
.
target
.
move
,
[
floor
,
ceil
].
map
(
x
));
if
(
d3
E
vent
.
selection
)
{
const
floor
=
Math
.
floor
(
x
.
invert
(
d3
E
vent
.
selection
[
0
])
/
interval
)
*
interval
;
const
ceil
=
Math
.
ceil
(
x
.
invert
(
d3
E
vent
.
selection
[
1
])
/
interval
)
*
interval
;
d3
.
select
(
this
).
call
(
d3
E
vent
.
target
.
move
,
[
floor
,
ceil
].
map
(
x
));
vm
.
brushSelection
=
[
floor
,
ceil
];
}
})
.
on
(
'end'
,
()
=>
{
vm
.
isBrushing
=
false
;
if
(
!
d3
.
e
vent
.
selection
)
{
if
(
!
d3
E
vent
.
selection
)
{
vm
.
brushSelection
=
null
;
}
});
...
...
@@ -243,6 +244,14 @@
.
append
(
'g'
)
.
attr
(
'transform'
,
`translate(
${
margin
.
left
}
,
${
margin
.
top
}
)`
);
this
.
xAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'trace-chart__axis-x'
)
.
attr
(
'transform'
,
`translate(0,
${
this
.
height
}
)`
);
this
.
yAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'trace-chart__axis-y'
)
.
attr
(
'stroke'
,
null
);
this
.
areas
=
this
.
chartLayer
.
append
(
'g'
);
this
.
hover
=
this
.
chartLayer
.
append
(
'path'
)
...
...
@@ -252,14 +261,6 @@
this
.
brushGroup
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'trace-chart__brush'
);
this
.
xAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'trace-chart__axis-x'
)
.
attr
(
'transform'
,
`translate(0,
${
this
.
height
}
)`
);
this
.
yAxis
=
this
.
chartLayer
.
append
(
'g'
)
.
attr
(
'class'
,
'trace-chart__axis-y'
)
.
attr
(
'stroke'
,
null
);
this
.
drawChart
(
this
.
chartData
);
},
watch
:
{
...
...
@@ -284,8 +285,6 @@
<
style
lang=
"scss"
>
@import
"~@/assets/css/utilities"
;
$axis-color
:
$grey-darker
;
.trace-chart
{
&
__svg
{
height
:
200px
;
...
...
@@ -331,33 +330,10 @@
fill-opacity
:
1
;
}
&
__axis-x
{
&
.domain
{
stroke
:
$axis-color
;
}
&
.tick
{
&
text
{
fill
:
$axis-color
;
font-size
:
$size-7
;
}
&
line
{
stroke
:
$axis-color
;
}
}
}
&
__axis-y
{
&
.domain
{
stroke
:
none
;
}
&
.tick
{
&
text
{
fill
:
$axis-color
;
font-size
:
$size-7
;
}
&
line
{
stroke
:
$axis-color
;
}
}
.tick
:not
(
:first-of-type
)
{
&
line
{
...
...
@@ -370,14 +346,17 @@
&
__area
{
&
--totalSuccess
{
fill
:
$success
;
opacity
:
0
.8
;
}
&
--totalClientErrors
{
fill
:
$warning
;
opacity
:
0
.8
;
}
&
--totalServerErrors
{
fill
:
$danger
;
opacity
:
0
.8
;
}
}
}
...
...
spring-boot-admin-server-ui/src/main/frontend/views/instances/trace/traces-list.vue
View file @
69a964e3
...
...
@@ -29,13 +29,14 @@
</thead>
<template
v-for=
"trace in traces"
>
<tr
class=
"is-selectable"
:class=
"
{ 'trace--is-detailed' : showDetails[trace.key]
, 'has-text-warning' : trace.isClientError(), 'has-text-danger' : trace.isServerError()
}"
:class=
"
{ 'trace--is-detailed' : showDetails[trace.key] }"
@click="showDetails[trace.key] ? $delete(showDetails, trace.key) : $set(showDetails, trace.key, true)"
:key="trace.key">
<td
v-text=
"trace.timestamp.format('L HH:mm:ss.SSS')"
></td>
<td
v-text=
"trace.method"
></td>
<td
v-text=
"trace.path"
></td>
<td
v-text=
"trace.status"
></td>
<td
v-text=
"trace.status"
:class=
"
{ 'has-text-warning' : trace.isClientError(), 'has-text-danger' : trace.isServerError() }">
</td>
<td
v-text=
"trace.contentType"
></td>
<td
v-text=
"trace.contentLength ? prettyBytes(trace.contentLength) : ''"
></td>
<td
v-text=
"trace.timeTaken !== null && typeof trace.timeTaken !== 'undefined' ? `$
{trace.timeTaken} ms` : ''">
</td>
...
...
spring-boot-admin-server-ui/src/main/frontend/views/journal/index.vue
View file @
69a964e3
...
...
@@ -54,6 +54,7 @@
</template>
<
script
>
import
subscribing
from
'@/mixins/subscribing'
;
import
Instance
from
'@/services/instance'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
...
...
@@ -73,48 +74,44 @@
}
export
default
{
mixins
:
[
subscribing
],
data
:
()
=>
({
_
events
:
[],
events
:
[],
showPayload
:
{},
instanceNames
:
{},
errors
:
[]
}),
computed
:
{
events
()
{
return
this
.
$data
.
_events
.
reverse
();
}
},
methods
:
{
toJson
(
obj
)
{
return
JSON
.
stringify
(
obj
,
null
,
4
);
},
addEvents
(
data
)
{
const
converted
=
data
.
map
(
event
=>
new
Event
(
event
));
this
.
$data
.
_events
=
this
.
$data
.
_events
.
concat
(
converted
);
converted
.
reverse
();
this
.
events
=
converted
.
concat
(
this
.
events
);
const
newInstanceNames
=
converted
.
filter
(
event
=>
event
.
type
===
'REGISTERED'
).
reduce
((
names
,
event
)
=>
({
...
names
,
[
event
.
instance
]:
event
.
payload
.
registration
.
name
}),
{});
_
.
assign
(
this
.
instanceNames
,
newInstanceNames
);
},
async
createSubscription
()
{
return
(
await
Instance
.
getEventStream
()).
subscribe
({
next
:
message
=>
{
this
.
addEvents
([
message
.
data
])
},
error
:
err
=>
this
.
errors
.
push
(
err
)
});
}
},
async
created
()
{
try
{
this
.
addEvents
((
await
Instance
.
fetchEvents
()).
data
);
this
.
events
.
sort
((
a
,
b
)
=>
b
.
timestamp
-
a
.
timestamp
)
}
catch
(
e
)
{
this
.
errors
.
push
(
e
);
}
this
.
subscription
=
(
await
Instance
.
getEventStream
()).
subscribe
({
next
:
message
=>
{
this
.
addEvents
([
message
.
data
])
},
error
:
err
=>
this
.
errors
.
push
(
err
)
});
},
}
}
</
script
>
<
style
lang=
"scss"
>
</
style
>
\ No newline at end of file
</
script
>
\ No newline at end of file
spring-boot-admin-server-ui/webpack.config.js
View file @
69a964e3
...
...
@@ -160,6 +160,7 @@ const config = {
new
BundleAnalyzerPlugin
({
analyzerMode
:
'static'
,
openAnalyzer
:
false
,
reportFilename
:
'../report.html'
})
],
devtool
:
'#eval-source-map'
...
...
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