Commit 58d396f9 by Johannes Edmeier

Add 'tail -f'-style logfile view.

closes #237
parent 57f2d4bc
<div class="container content">
<div class="container">
<table class="table table-striped">
<col style="width:30%">
<col style="width:auto">
......@@ -8,9 +8,9 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="entry in summary" >
<td style="word-break: break-all;" >{{ entry.key }}</td>
<td style="word-break: break-all;" >{{ entry.value }}</td>
<tr ng-repeat="entry in summary">
<td style="word-break: break-all;">{{ entry.key }}</td>
<td style="word-break: break-all;">{{ entry.value }}</td>
</tr>
</tbody>
</table>
......@@ -20,15 +20,17 @@
<col style="width:auto">
<thead>
<tr>
<th>Process</th><th>Running</th><th>Completed</th>
<th>Process</th>
<th>Running</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="process in processes" >
<td style="word-break: break-all;" >{{ process.name }}</td>
<td style="word-break: break-all;" >{{ process.running }}</td>
<td style="word-break: break-all;" >{{ process.completed }}</td>
<tr ng-repeat="process in processes">
<td style="word-break: break-all;">{{ process.name }}</td>
<td style="word-break: break-all;">{{ process.running }}</td>
<td style="word-break: break-all;">{{ process.completed }}</td>
</tr>
</tbody>
</table>
</div>
</div>
\ No newline at end of file
<div class="alert alert-error" ng-if="error">
Unable to connect to Command Metric Stream '{{streamUrl}}'.<br>
<b>Error:</b> {{ error }}
<div class="container">
<div class="alert alert-error" ng-if="error">
Unable to connect to Command Metric Stream '{{streamUrl}}'.<br>
<b>Error:</b> {{ error }}
</div>
<sba-info-panel panel-title="Cuircuit Breakers">
<sba-hystrix-command event-source="hystrixStream"></sba-hystrix-command>
</sba-info-panel>
<sba-info-panel panel-title="Thread Pools">
<sba-hystrix-thread-pool event-source="hystrixStream"></sba-hystrix-thread-pool>
</sba-info-panel>
</div>
<sba-info-panel panel-title="Cuircuit Breakers">
<sba-hystrix-command event-source="hystrixStream"></sba-hystrix-command>
</sba-info-panel>
<sba-info-panel panel-title="Thread Pools">
<sba-hystrix-thread-pool event-source="hystrixStream"></sba-hystrix-thread-pool>
</sba-info-panel>
\ No newline at end of file
......@@ -48,7 +48,6 @@
<script src="dependencies.js" type="text/javascript"></script>
<script type="text/javascript">
sbaModules = [];
</script>
<script src="core.js" type="text/javascript"></script>
<script src="all-modules.js" type="text/javascript"></script>
......@@ -58,7 +57,6 @@
strictDi: true
});
});
</script>
</body>
......
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div style="display: flex; flex-wrap: wrap; justify-content: space-around;">
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Application" raw="api/applications/{{ application.id }}/info">
<table class="table">
<tr ng-repeat="(key, value) in info">
<td ng-bind="key"></td>
<td style="white-space: pre" ng-bind-html="value | yaml | linkify:50"></td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Health" raw="api/applications/{{ application.id }}/health">
<sba-health-status health="health"></sba-health-status>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Memory" raw="api/applications/{{ application.id }}/metrics/mem.*%7Cheap.*">
<sba-memory-stats metrics=metrics></sba-memory-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px"panel-title="JVM" raw="api/applications/{{ application.id }}/metrics/systemload.*%7Cclasses.*%7Cuptime%7Cprocessors%7Cthreads.*">
<sba-jvm-stats metrics="metrics"></sba-jvm-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Garbage Collection" raw="api/applications/{{ application.id }}/metrics/gc.*">
<sba-gc-stats metrics="metrics"></sba-gc-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Servlet Container" raw="api/applications/{{ application.id }}/metrics/httpsessions.*" ng-show="metrics['httpsessions.active'] != null">
<sba-servlet-container-stats metrics="metrics"></sba-servlet-container-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Datasources" raw="api/applications/{{ application.id }}/metrics/datasource.*" ng-show="hasDatasources">
<sba-datasource-stats metrics="metrics"></sba-datasource-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Caches" raw="api/applications/{{ application.id }}/metrics/cache.*" ng-show="hasCaches">
<sba-cache-stats metrics="metrics"></sba-cache-stats>
</sba-info-panel>
<div class="container" ui-view>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div style="display: flex; flex-wrap: wrap; justify-content: space-around;">
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Application" raw="api/applications/{{ application.id }}/info">
<table class="table">
<tr ng-repeat="(key, value) in info">
<td ng-bind="key"></td>
<td style="white-space: pre" ng-bind-html="value | yaml | linkify:50"></td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Health" raw="api/applications/{{ application.id }}/health">
<sba-health-status health="health"></sba-health-status>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Memory" raw="api/applications/{{ application.id }}/metrics/mem.*%7Cheap.*">
<sba-memory-stats metrics=metrics></sba-memory-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px"panel-title="JVM" raw="api/applications/{{ application.id }}/metrics/systemload.*%7Cclasses.*%7Cuptime%7Cprocessors%7Cthreads.*">
<sba-jvm-stats metrics="metrics"></sba-jvm-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Garbage Collection" raw="api/applications/{{ application.id }}/metrics/gc.*">
<sba-gc-stats metrics="metrics"></sba-gc-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Servlet Container" raw="api/applications/{{ application.id }}/metrics/httpsessions.*" ng-show="metrics['httpsessions.active'] != null">
<sba-servlet-container-stats metrics="metrics"></sba-servlet-container-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Datasources" raw="api/applications/{{ application.id }}/metrics/datasource.*" ng-show="hasDatasources">
<sba-datasource-stats metrics="metrics"></sba-datasource-stats>
</sba-info-panel>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Caches" raw="api/applications/{{ application.id }}/metrics/cache.*" ng-show="hasCaches">
<sba-cache-stats metrics="metrics"></sba-cache-stats>
</sba-info-panel>
</div>
</div>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<sba-info-panel panel-title="Active profiles" raw="api/applications/{{ application.id }}/env">
<table class="table">
<tr>
<td>{{profiles.join(', ') || '-'}}</td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel panel-title="Environment manager" ng-show="refreshSupported">
<sba-environment-manager environment="env" application="application" on-environment-changed="refresh"></sba-environment-manager>
</sba-info-panel>
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
</div>
<sba-info-panel category="PropertySource" panel-title="{{source.name}}" ng-repeat="source in env" ng-show="!searchFilter || (source.value | filter:searchFilter).length > 0">
<table class="table">
<col width="38%">
<div class="container">
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<sba-info-panel panel-title="Active profiles" raw="api/applications/{{ application.id }}/env">
<table class="table">
<tr>
<td>{{profiles.join(', ') || '-'}}</td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel panel-title="Environment manager" ng-show="refreshSupported">
<sba-environment-manager environment="env" application="application" on-environment-changed="refresh"></sba-environment-manager>
</sba-info-panel>
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
</div>
<sba-info-panel category="PropertySource" panel-title="{{source.name}}" ng-repeat="source in env" ng-show="!searchFilter || (source.value | filter:searchFilter).length > 0">
<table class="table">
<col width="38%">
<col width="62%">
<tr ng-repeat="property in source.value | filter:searchFilter">
<td style="word-break: break-all;">{{property.name}}</td>
<td style="word-break: break-all;">{{property.value}}</td>
</tr>
<tr ng-if="source.value.length == 0">
<td colspan="2">-</td>
</tr>
</table>
</sba-info-panel>
<tr ng-repeat="property in source.value | filter:searchFilter">
<td style="word-break: break-all;">{{property.name}}</td>
<td style="word-break: break-all;">{{property.value}}</td>
</tr>
<tr ng-if="source.value.length == 0">
<td colspan="2">-</td>
</tr>
</table>
</sba-info-panel>
</div>
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
</div>
<sba-info-panel panel-title="Flyway Migrations" raw="api/applications/{{ application.id }}/flyway">
<table class="table">
<thead>
<th>Type</th>
<th>Checksum</th>
<th>Version</th>
<th>Description</th>
<th>Script</th>
<th>State</th>
<th>Installed</th>
<th>Execution Time</th>
</thead>
<tbody>
<tr ng-repeat="migration in migrations | filter:searchFilter">
<td ng-bind="migration.type"></td>
<td ng-bind="migration.checksum"></td>
<td ng-bind="migration.version"></td>
<td ng-bind="migration.description"></td>
<td ng-bind="migration.script"></td>
<td><span class="label" ng-class="{
<div class="container">
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
</div>
<sba-info-panel panel-title="Flyway Migrations" raw="api/applications/{{ application.id }}/flyway">
<table class="table">
<thead>
<th>Type</th>
<th>Checksum</th>
<th>Version</th>
<th>Description</th>
<th>Script</th>
<th>State</th>
<th>Installed</th>
<th>Execution Time</th>
</thead>
<tbody>
<tr ng-repeat="migration in migrations | filter:searchFilter">
<td ng-bind="migration.type"></td>
<td ng-bind="migration.checksum"></td>
<td ng-bind="migration.version"></td>
<td ng-bind="migration.description"></td>
<td ng-bind="migration.script"></td>
<td><span class="label" ng-class="{
'label-success': inArray(migration.state, successStates),
'label-warning': inArray(migration.state, warningStates),
'label-danger': inArray(migration.state, failedStates)}" ng-bind="migration.state"></span></td>
<td ng-bind="migration.installedOn | date:'dd.MM.yyyy HH:mm:ss.sss'"></td>
<td ng-bind="migration.executionTime + 'ms'"></td>
</tr>
</tbody>
</table>
</sba-info-panel>
<td ng-bind="migration.installedOn | date:'dd.MM.yyyy HH:mm:ss.sss'"></td>
<td ng-bind="migration.executionTime + 'ms'"></td>
</tr>
</tbody>
</table>
</sba-info-panel>
</div>
<div ng-if="errorWhileListing">
<p>To make the JMX section work you need to make the /jolokia-endpoint accessible.
<br/> Include the jolokia-core.jar in your spring-boot-application:
<pre>&lt;dependency&gt;
&lt;groupId>org.jolokia&lt;/groupId&gt;
&lt;artifactId>jolokia-core&lt;/artifactId&gt;
&lt;/dependency&gt;</pre></p>
<div class="container">
<div ng-if="errorWhileListing">
<p>To make the JMX section work you need to make the /jolokia-endpoint accessible.
<br/> Include the jolokia-core.jar in your spring-boot-application:
<pre>&lt;dependency&gt; &lt;groupId>org.jolokia&lt;/groupId&gt; &lt;artifactId>jolokia-core&lt;/artifactId&gt; &lt;/dependency&gt;
</pre>
</p>
</div>
<pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre>
<sba-accordion>
<sba-accordion-group ng-repeat="domain in domains | orderBy:'name' track by domain.name">
<sba-accordion-heading>
<ul class="breadcrumb" style="margin: 0; background-color: #34302D;">
<li style="text-shadow: none;">
<small class="muted">Domain</small>
<a ng-if="domain.selectedBean" ng-click="domain.selectedBean = null">{{domain.name}}</a>
<span ng-if="domain.selectedBean" class="divider">/</span>
<span ng-if="!domain.selectedBean">{{domain.name}}</span>
</li>
<li style="text-shadow: none;" ng-if="domain.selectedBean.name">
<small class="muted">MBean</small> {{domain.selectedBean.name}}
</li>
</ul>
</sba-accordion-heading>
<sba-accordion-body>
<ul class="nav nav-tabs nav-stacked" ng-show="!domain.selectedBean" style="transition: 0.6s ease-in-out left">
<li ng-repeat="bean in domain.beans | orderBy:'name' track by bean.id"><a ng-click="domain.selectedBean = bean"><small class="muted">MBean</small> {{bean.name}}</a></li>
</ul>
<sba-jmx-bean application="application" bean="domain.selectedBean" ng-show="domain.selectedBean"></sba-jmx-bean>
</sba-accordion-body>
</sba-accordion-group>
</sba-accordion>
</div>
<pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre>
<sba-accordion>
<sba-accordion-group ng-repeat="domain in domains | orderBy:'name' track by domain.name">
<sba-accordion-heading>
<ul class="breadcrumb" style="margin: 0; background-color: #34302D;">
<li style="text-shadow: none;">
<small class="muted">Domain</small>
<a ng-if="domain.selectedBean" ng-click="domain.selectedBean = null">{{domain.name}}</a>
<span ng-if="domain.selectedBean" class="divider">/</span>
<span ng-if="!domain.selectedBean">{{domain.name}}</span>
</li>
<li style="text-shadow: none;" ng-if="domain.selectedBean.name">
<small class="muted">MBean</small> {{domain.selectedBean.name}}
</li>
</ul>
</sba-accordion-heading>
<sba-accordion-body>
<ul class="nav nav-tabs nav-stacked" ng-show="!domain.selectedBean" style="transition: 0.6s ease-in-out left">
<li ng-repeat="bean in domain.beans | orderBy:'name' track by bean.id"><a ng-click="domain.selectedBean = bean"><small class="muted">MBean</small> {{bean.name}}</a></li>
</ul>
<sba-jmx-bean application="application" bean="domain.selectedBean" ng-show="domain.selectedBean"></sba-jmx-bean>
</sba-accordion-body>
</sba-accordion-group>
</sba-accordion>
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<div class="container">
<div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
</div>
<sba-liquibase-change-log-entry ng-repeat="entry in changeLog | filter:searchFilter" change-log-entry="entry" />
</div>
<sba-liquibase-change-log-entry ng-repeat="entry in changeLog | filter:searchFilter" change-log-entry="entry" />
.logview {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
white-space: pre-wrap;
padding: 5px;
}
\ No newline at end of file
/*
* Copyright 2016 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.
*/
'use strict';
require('./logfileViewer.css');
module.exports = {
bindings: {
url: '@logfileUrl',
interval: '@?refreshInterval',
initialSize: '@?initialSize',
onAppend: '&onAppend'
},
controller: function ($timeout, Logtail, $element, $document, $filter) {
'ngInject';
var ctrl = this;
ctrl.$onInit = function () {
ctrl.interval = ctrl.interval || 1000;
ctrl.initialSize = ctrl.initialSize || 300 * 1024;
ctrl.logtail = new Logtail(ctrl.url, ctrl.initialSize);
ctrl.totalSize = 0;
ctrl.currentSize = 0;
var logview = $element.children('.logview');
var doRefresh = function () {
ctrl.logtail.refresh().then(function (chunk) {
ctrl.totalSize = chunk.totalSize;
if (chunk.first) {
logview.empty();
var skippedBytes = Math.max(chunk.totalSize - ctrl.initialSize, 0);
if (skippedBytes > 0) {
logview.append($document[0].createTextNode('... first ' + $filter('humanBytes')(skippedBytes) + ' skipped ...\n\n'));
}
}
if (chunk.addendum !== null) {
logview.append($document[0].createTextNode(chunk.addendum));
if (ctrl.onAppend) {
ctrl.onAppend();
}
}
ctrl.promise = $timeout(doRefresh, ctrl.interval);
}).catch(function (response) {
ctrl.error = response;
});
};
doRefresh();
};
ctrl.$onDestroy = function () {
if (ctrl.promise) {
$timeout.cancel(ctrl.promise);
}
};
},
template: require('./logfileViewer.tpl.html')
};
<div class="logview" ng-bind="$ctrl.data"></div>
<div class="alert alert-error" ng-if="$ctrl.error">
<b>Error while reading logfile:</b> <span data-ng-bind="$ctrl.error.status"></span> - <span data-ng-bind="$ctrl.error.statusText"></span>
</div>
/*
* Copyright 2014 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.
*/
'use strict';
var angular = require('angular');
module.exports = function ($scope, $window, $document) {
'ngInject';
$scope.follow = false;
$scope.toggleFollow = function () {
$scope.follow = !$scope.follow;
};
$scope.scrollToTop = function () {
$window.scrollTo($window.scrollX, 0);
};
$scope.updateview = function () {
if ($scope.follow) {
$window.scrollTo($window.scrollX, $document[0].body.scrollHeight);
}
};
angular.element($window).bind('scroll', function () {
var atBottom = ($window.innerHeight + $window.scrollY) >= $document[0].body.offsetHeight;
if (!atBottom && $scope.follow) {
$scope.follow = false;
$scope.$apply();
}
});
};
#download {
position: fixed;
bottom: 40px;
right: 40px;
}
#top {
text-align: right;
padding: 5px;
}
#follow {
position: fixed;
top: 100px;
right: 40px;
}
\ No newline at end of file
......@@ -20,13 +20,25 @@ var angular = require('angular');
var module = angular.module('sba-applications-logfile', ['sba-applications']);
global.sbaModules.push(module.name);
module.run(function ($sce, $http, ApplicationViews) {
require('./css/module.css');
module.component('sbaLogfileViewer', require('./components/logfileViewer.js'));
module.service('Logtail', require('./services/logtail.js'));
module.controller('logfileCtrl', require('./controllers/logfileCtrl.js'));
module.config(function ($stateProvider) {
$stateProvider.state('applications.logfile', {
url: '/logfile',
templateUrl: 'applications-logfile/views/logfile.html',
controller: 'logfileCtrl'
});
});
module.run(function (ApplicationViews, $sce, $http) {
ApplicationViews.register({
order: 1,
title: $sce.trustAsHtml('<i class="fa fa-file-text-o fa-fw"></i>Log'),
href: 'api/applications/{id}/logfile',
target: '_blank',
state: 'applications.logfile',
show: function (application) {
if (!application.managementUrl || !application.statusInfo.status || application.statusInfo.status === 'OFFLINE') {
return false;
......@@ -38,5 +50,5 @@ module.run(function ($sce, $http, ApplicationViews) {
});
}
});
});
/*
* Copyright 2016 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.
*/
'use strict';
module.exports = function ($http) {
'ngInject';
var Logtail = function (url, initialSize) {
this.url = url;
this.initialSize = initialSize || (300 * 1024);
this.logSize = 0;
};
Logtail.prototype.refresh = function () {
var self = this;
var initial = (self.logSize === 0);
var range;
if (initial) {
range = '-' + self.initialSize.toString();
} else {
// Reload the last byte to avoid a 416: Range unsatisfiable.
// If the response has length = 1 the file hasn't beent changed.
// If the response status is 416 the logfile has been truncated.
range = (self.logSize - 1).toString() + '-';
}
return $http.get(self.url, {
headers: {
'Range': 'bytes=' + range
}
}).then(function (response) {
var contentSize = parseInt(response.headers('Content-Length'));
if (response.status === 206) {
self.logSize = parseInt(response.headers('Content-Range').split('/')[1]);
} else if (response.status === 200) {
if (!initial) {
throw 'Expected 206 - Partial Content on subsequent requests.';
}
self.logSize = contentSize;
} else {
throw 'Unexpected resopnse status: ' + response.status;
}
var chunk = {
totalSize: self.logSize,
first: false,
addendum: null
};
if (initial) {
chunk.first = true;
if (contentSize < self.logSize) {
// In case of a partial response find the first line break.
chunk.addendum = response.data.substring(response.data.indexOf('\n') + 1);
} else {
chunk.addendum = response.data;
}
} else if (response.data.length > 1) {
// Remove the first byte which has been part of the previos response.
chunk.addendum = response.data.substring(1);
}
return chunk;
});
};
return Logtail;
};
<div id="download">
<button class="btn" ng-class="{'active btn-primary' : follow }" ng-click="toggleFollow()"><i class="fa fa-long-arrow-down"></i> Follow</button>
<a class="btn btn-success" target="_blank" href="api/applications/{{application.id}}/logfile"><i class="fa fa-cloud-download"></i> Download</a>
</div>
<sba-logfile-viewer logfile-url="api/applications/{{application.id}}/logfile" on-append="updateview()"></sba-logfile-viewer>
<div id="top">
<button class="btn btn-small" ng-click="scrollToTop()">Top <i class="fa fa-caret-up"></i></button>
</div>
\ No newline at end of file
<div ng-if="errorWhileListing">
<p>To make the logging section work you need to make the /jolokia-endpoint accessible.
<br/> Include the jolokia-core.jar in your spring-boot-application:
<pre>&lt;dependency&gt;
&lt;groupId>org.jolokia&lt;/groupId&gt;
&lt;artifactId>jolokia-core&lt;/artifactId&gt;
&lt;/dependency&gt;</pre></p>
<p>Please note that the logging section currently only works with Logback.
<br/> To make the section work with Logback please activate the JMXConfigurator in your <b>logback.xml</b>:
<pre>&lt;configuration&gt;
&lt;include resource="org/springframework/boot/logging/logback/base.xml"/&gt;
&lt;jmxConfigurator/&gt;
&lt;/configuration&gt;</pre></p>
</div>
<pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre>
<div ng-show="loggers">
<form>
<div class="input-prepend input-append">
<button class="btn" title="Show package-level loggers" ng-class="{'btn-inverse': showPackageLoggers}" ng-click="togglePackageLoggers()"><i class="fa fa-folder-o"></i></button>
<input placeholder="Filter by name ..." class="input-xxlarge" type="search" ng-model="filterLogger.name" />
<button class="btn" title="reload list" ng-click="refreshLoggers()"><i class="fa fa-repeat"></i></button>
<span title="filtered / total" class="add-on">{{ filteredLoggers.length }}/{{ loggers.length }}</span>
</div>
</form>
<table class="table table-hover">
<tbody>
<tr ng-repeat="logger in (filteredLoggers = (loggers | filter:packageFilter | filter:filterLogger) ) | limitTo: limit track by logger.name">
<td>
<sba-logger value="logger" on-level-changed="refreshLoggers"></sba-logger>
</td>
</tr>
<tr ng-show="limit < loggers.length">
<td>
<button class="btn btn-link btn-block" ng-click="limit = limit + 10">show more</button>
</td>
</tr>
<tr ng-show="limit < loggers.length">
<td>
<button class="btn btn-link btn-block" ng-click="limit = loggers.length">show all</button>
</td>
</tr>
</tbody>
</table>
<div class="container">
<div ng-if="errorWhileListing">
<p>To make the logging section work you need to make the /jolokia-endpoint accessible.
<br/> Include the jolokia-core.jar in your spring-boot-application:
<pre>&lt;dependency&gt; &lt;groupId>org.jolokia&lt;/groupId&gt; &lt;artifactId>jolokia-core&lt;/artifactId&gt; &lt;/dependency&gt;</pre>
</p>
<p>Please note that the logging section currently only works with Logback.
<br/> To make the section work with Logback please activate the JMXConfigurator in your <b>logback.xml</b>:
<pre>&lt;configuration&gt; &lt;include resource="org/springframework/boot/logging/logback/base.xml"/&gt; &lt;jmxConfigurator/&gt;&lt;/configuration&gt;</pre>
</p>
</div>
<pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre>
<div ng-show="loggers">
<form>
<div class="input-prepend input-append">
<button class="btn" title="Show package-level loggers" ng-class="{'btn-inverse': showPackageLoggers}" ng-click="togglePackageLoggers()"><i class="fa fa-folder-o"></i></button>
<input placeholder="Filter by name ..." class="input-xxlarge" type="search" ng-model="filterLogger.name" />
<button class="btn" title="reload list" ng-click="refreshLoggers()"><i class="fa fa-repeat"></i></button>
<span title="filtered / total" class="add-on">{{ filteredLoggers.length }}/{{ loggers.length }}</span>
</div>
</form>
<table class="table table-hover">
<tbody>
<tr ng-repeat="logger in (filteredLoggers = (loggers | filter:packageFilter | filter:filterLogger) ) | limitTo: limit track by logger.name">
<td>
<sba-logger value="logger" on-level-changed="refreshLoggers"></sba-logger>
</td>
</tr>
<tr ng-show="limit < loggers.length">
<td>
<button class="btn btn-link btn-block" ng-click="limit = limit + 10">show more</button>
</td>
</tr>
<tr ng-show="limit < loggers.length">
<td>
<button class="btn btn-link btn-block" ng-click="limit = loggers.length">show all</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
<div class="container">
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<sba-info-panel panel-title="Counter" raw="api/applications/{{ application.id }}/metrics/counter.*">
<table class="table" ng-if="counters.length > 0">
<tr ng-repeat="counter in counters">
<td>
<sba-simple-metric-bar for-metric="counter" global-max="countersMax"></sba-simple-metric-bar>
</td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel panel-title="Gauges" raw="api/applications/{{ application.id }}/metrics/gauge.*">
<table class="table" ng-if="gauges.length > 0">
<tr ng-if="showRichGauges" ng-repeat="gauge in gauges">
<td>
<sba-rich-metric-bar for-metric="gauge" global-max="gaugesMax"></sba-rich-metric-bar>
</td>
</tr>
<tr ng-if="!showRichGauges" ng-repeat="gauge in gauges">
<td>
<sba-simple-metric-bar for-metric="gauge" global-max="gaugesMax"></sba-simple-metric-bar>
</td>
</tr>
</table>
</sba-info-panel>
</div>
<sba-info-panel panel-title="Counter" raw="api/applications/{{ application.id }}/metrics/counter.*">
<table class="table" ng-if="counters.length > 0">
<tr ng-repeat="counter in counters">
<td>
<sba-simple-metric-bar for-metric="counter" global-max="countersMax"></sba-simple-metric-bar>
</td>
</tr>
</table>
</sba-info-panel>
<sba-info-panel panel-title="Gauges" raw="api/applications/{{ application.id }}/metrics/gauge.*">
<table class="table" ng-if="gauges.length > 0">
<tr ng-if="showRichGauges" ng-repeat="gauge in gauges">
<td>
<sba-rich-metric-bar for-metric="gauge" global-max="gaugesMax"></sba-rich-metric-bar>
</td>
</tr>
<tr ng-if="!showRichGauges" ng-repeat="gauge in gauges">
<td>
<sba-simple-metric-bar for-metric="gauge" global-max="gaugesMax"></sba-simple-metric-bar>
</td>
</tr>
</table>
</sba-info-panel>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div class="row">
<div class="text-center">
<form>
<button class="btn" ng-click="dumpThreads()"><i class="fa fa-repeat"></i> refresh</button>
</form>
<div class="container">
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div class="row">
<div class="text-center">
<form>
<button class="btn" ng-click="dumpThreads()"><i class="fa fa-repeat"></i> refresh</button>
</form>
</div>
</div>
<sba-thread-summary ng-show="dump" threads="dump"></sba-thread-summary>
<sba-accordion>
<sba-thread ng-repeat="thread in dump | orderBy:'threadName' track by thread.threadId" thread="thread"></sba-thread>
</sba-accordion>
</div>
<sba-thread-summary ng-show="dump" threads="dump"></sba-thread-summary>
<sba-accordion>
<sba-thread ng-repeat="thread in dump | orderBy:'threadName' track by thread.threadId" thread="thread"></sba-thread>
</sba-accordion>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div class="form-inline">
<button title="refresh" class="btn" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<div class="input-prepend input-append">
<button title="auto refresh" class="btn" ng-click="toggleAutoRefresh()" ng-class="{'active':refresher != null}">
<div class="container">
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div class="form-inline">
<button title="refresh" class="btn" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<div class="input-prepend input-append">
<button title="auto refresh" class="btn" ng-click="toggleAutoRefresh()" ng-class="{'active':refresher != null}">
<i class="fa fa-refresh" ng-class="{'fa-spin':refresher != null}"></i>
</button>
<input class="input-mini" type="number" min="1" ng-model="refreshInterval" ng-disabled="refresher != null" />
<span class="add-on">sec</span>
<input class="input-mini" type="number" min="1" ng-model="refreshInterval" ng-disabled="refresher != null" />
<span class="add-on">sec</span>
</div>
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
</div>
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<ul class="timeline">
<li ng-repeat="trace in traces | filter:searchFilter">
<sba-trace value="trace"></sba-trace>
</li>
</ul>
</div>
<ul class="timeline">
<li ng-repeat="trace in traces | filter:searchFilter">
<sba-trace value="trace"></sba-trace>
</li>
</ul>
.header--application {
margin-bottom: 40px;
margin-bottom: 20px;
font-size: 15px;
}
.header--application .navbar-inner {
......
......@@ -18,4 +18,4 @@
</div>
</div>
</div>
<div class="container" ui-view></div>
<div ui-view></div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment