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"> <table class="table table-striped">
<col style="width:30%"> <col style="width:30%">
<col style="width:auto"> <col style="width:auto">
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="entry in summary" > <tr ng-repeat="entry in summary">
<td style="word-break: break-all;" >{{ entry.key }}</td> <td style="word-break: break-all;">{{ entry.key }}</td>
<td style="word-break: break-all;" >{{ entry.value }}</td> <td style="word-break: break-all;">{{ entry.value }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
...@@ -20,15 +20,17 @@ ...@@ -20,15 +20,17 @@
<col style="width:auto"> <col style="width:auto">
<thead> <thead>
<tr> <tr>
<th>Process</th><th>Running</th><th>Completed</th> <th>Process</th>
<th>Running</th>
<th>Completed</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="process in processes" > <tr ng-repeat="process in processes">
<td style="word-break: break-all;" >{{ process.name }}</td> <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.running }}</td>
<td style="word-break: break-all;" >{{ process.completed }}</td> <td style="word-break: break-all;">{{ process.completed }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
\ No newline at end of file
<div class="alert alert-error" ng-if="error"> <div class="container">
Unable to connect to Command Metric Stream '{{streamUrl}}'.<br> <div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ 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> </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 @@ ...@@ -48,7 +48,6 @@
<script src="dependencies.js" type="text/javascript"></script> <script src="dependencies.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
sbaModules = []; sbaModules = [];
</script> </script>
<script src="core.js" type="text/javascript"></script> <script src="core.js" type="text/javascript"></script>
<script src="all-modules.js" type="text/javascript"></script> <script src="all-modules.js" type="text/javascript"></script>
...@@ -58,7 +57,6 @@ ...@@ -58,7 +57,6 @@
strictDi: true strictDi: true
}); });
}); });
</script> </script>
</body> </body>
......
<div class="alert alert-error" ng-if="error"> <div class="container" ui-view>
<b>Error:</b> {{ error }} <div class="alert alert-error" ng-if="error">
</div> <b>Error:</b> {{ error }}
<div style="display: flex; flex-wrap: wrap; justify-content: space-around;"> </div>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Application" raw="api/applications/{{ application.id }}/info"> <div style="display: flex; flex-wrap: wrap; justify-content: space-around;">
<table class="table"> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Application" raw="api/applications/{{ application.id }}/info">
<tr ng-repeat="(key, value) in info"> <table class="table">
<td ng-bind="key"></td> <tr ng-repeat="(key, value) in info">
<td style="white-space: pre" ng-bind-html="value | yaml | linkify:50"></td> <td ng-bind="key"></td>
</tr> <td style="white-space: pre" ng-bind-html="value | yaml | linkify:50"></td>
</table> </tr>
</sba-info-panel> </table>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Health" raw="api/applications/{{ application.id }}/health"> </sba-info-panel>
<sba-health-status health="health"></sba-health-status> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Health" raw="api/applications/{{ application.id }}/health">
</sba-info-panel> <sba-health-status health="health"></sba-health-status>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Memory" raw="api/applications/{{ application.id }}/metrics/mem.*%7Cheap.*"> </sba-info-panel>
<sba-memory-stats metrics=metrics></sba-memory-stats> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Memory" raw="api/applications/{{ application.id }}/metrics/mem.*%7Cheap.*">
</sba-info-panel> <sba-memory-stats metrics=metrics></sba-memory-stats>
<sba-info-panel class="span6" style="margin-left:0px"panel-title="JVM" raw="api/applications/{{ application.id }}/metrics/systemload.*%7Cclasses.*%7Cuptime%7Cprocessors%7Cthreads.*"> </sba-info-panel>
<sba-jvm-stats metrics="metrics"></sba-jvm-stats> <sba-info-panel class="span6" style="margin-left:0px"panel-title="JVM" raw="api/applications/{{ application.id }}/metrics/systemload.*%7Cclasses.*%7Cuptime%7Cprocessors%7Cthreads.*">
</sba-info-panel> <sba-jvm-stats metrics="metrics"></sba-jvm-stats>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Garbage Collection" raw="api/applications/{{ application.id }}/metrics/gc.*"> </sba-info-panel>
<sba-gc-stats metrics="metrics"></sba-gc-stats> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Garbage Collection" raw="api/applications/{{ application.id }}/metrics/gc.*">
</sba-info-panel> <sba-gc-stats metrics="metrics"></sba-gc-stats>
<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-info-panel>
<sba-servlet-container-stats metrics="metrics"></sba-servlet-container-stats> <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-info-panel> <sba-servlet-container-stats metrics="metrics"></sba-servlet-container-stats>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Datasources" raw="api/applications/{{ application.id }}/metrics/datasource.*" ng-show="hasDatasources"> </sba-info-panel>
<sba-datasource-stats metrics="metrics"></sba-datasource-stats> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Datasources" raw="api/applications/{{ application.id }}/metrics/datasource.*" ng-show="hasDatasources">
</sba-info-panel> <sba-datasource-stats metrics="metrics"></sba-datasource-stats>
<sba-info-panel class="span6" style="margin-left:0px" panel-title="Caches" raw="api/applications/{{ application.id }}/metrics/cache.*" ng-show="hasCaches"> </sba-info-panel>
<sba-cache-stats metrics="metrics"></sba-cache-stats> <sba-info-panel class="span6" style="margin-left:0px" panel-title="Caches" raw="api/applications/{{ application.id }}/metrics/cache.*" ng-show="hasCaches">
</sba-info-panel> <sba-cache-stats metrics="metrics"></sba-cache-stats>
</sba-info-panel>
</div>
</div> </div>
<div class="alert alert-error" ng-if="error"> <div class="container">
<b>Error:</b> {{ error }} <div class="alert alert-error" ng-if="error">
</div> <b>Error:</b> {{ error }}
</div>
<sba-info-panel panel-title="Active profiles" raw="api/applications/{{ application.id }}/env"> <sba-info-panel panel-title="Active profiles" raw="api/applications/{{ application.id }}/env">
<table class="table"> <table class="table">
<tr> <tr>
<td>{{profiles.join(', ') || '-'}}</td> <td>{{profiles.join(', ') || '-'}}</td>
</tr> </tr>
</table> </table>
</sba-info-panel> </sba-info-panel>
<sba-info-panel panel-title="Environment manager" ng-show="refreshSupported">
<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-environment-manager environment="env" application="application" on-environment-changed="refresh"></sba-environment-manager> </sba-info-panel>
</sba-info-panel> <div class="input-append">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
<div class="input-append"> <button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" /> </div>
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button> <sba-info-panel category="PropertySource" panel-title="{{source.name}}" ng-repeat="source in env" ng-show="!searchFilter || (source.value | filter:searchFilter).length > 0">
</div> <table class="table">
<col width="38%">
<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%"> <col width="62%">
<tr ng-repeat="property in source.value | filter:searchFilter"> <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.name}}</td>
<td style="word-break: break-all;">{{property.value}}</td> <td style="word-break: break-all;">{{property.value}}</td>
</tr> </tr>
<tr ng-if="source.value.length == 0"> <tr ng-if="source.value.length == 0">
<td colspan="2">-</td> <td colspan="2">-</td>
</tr> </tr>
</table> </table>
</sba-info-panel> </sba-info-panel>
</div>
<div class="input-append"> <div class="container">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" /> <div class="input-append">
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button> <input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
</div> <button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<sba-info-panel panel-title="Flyway Migrations" raw="api/applications/{{ application.id }}/flyway"> </div>
<table class="table"> <sba-info-panel panel-title="Flyway Migrations" raw="api/applications/{{ application.id }}/flyway">
<thead> <table class="table">
<th>Type</th> <thead>
<th>Checksum</th> <th>Type</th>
<th>Version</th> <th>Checksum</th>
<th>Description</th> <th>Version</th>
<th>Script</th> <th>Description</th>
<th>State</th> <th>Script</th>
<th>Installed</th> <th>State</th>
<th>Execution Time</th> <th>Installed</th>
</thead> <th>Execution Time</th>
<tbody> </thead>
<tr ng-repeat="migration in migrations | filter:searchFilter"> <tbody>
<td ng-bind="migration.type"></td> <tr ng-repeat="migration in migrations | filter:searchFilter">
<td ng-bind="migration.checksum"></td> <td ng-bind="migration.type"></td>
<td ng-bind="migration.version"></td> <td ng-bind="migration.checksum"></td>
<td ng-bind="migration.description"></td> <td ng-bind="migration.version"></td>
<td ng-bind="migration.script"></td> <td ng-bind="migration.description"></td>
<td><span class="label" ng-class="{ <td ng-bind="migration.script"></td>
<td><span class="label" ng-class="{
'label-success': inArray(migration.state, successStates), 'label-success': inArray(migration.state, successStates),
'label-warning': inArray(migration.state, warningStates), 'label-warning': inArray(migration.state, warningStates),
'label-danger': inArray(migration.state, failedStates)}" ng-bind="migration.state"></span></td> '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.installedOn | date:'dd.MM.yyyy HH:mm:ss.sss'"></td>
<td ng-bind="migration.executionTime + 'ms'"></td> <td ng-bind="migration.executionTime + 'ms'"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</sba-info-panel> </sba-info-panel>
</div>
<div ng-if="errorWhileListing"> <div class="container">
<p>To make the JMX section work you need to make the /jolokia-endpoint accessible. <div ng-if="errorWhileListing">
<br/> Include the jolokia-core.jar in your spring-boot-application: <p>To make the JMX section work you need to make the /jolokia-endpoint accessible.
<pre>&lt;dependency&gt; <br/> Include the jolokia-core.jar in your spring-boot-application:
&lt;groupId>org.jolokia&lt;/groupId&gt; <pre>&lt;dependency&gt; &lt;groupId>org.jolokia&lt;/groupId&gt; &lt;artifactId>jolokia-core&lt;/artifactId&gt; &lt;/dependency&gt;
&lt;artifactId>jolokia-core&lt;/artifactId&gt; </pre>
&lt;/dependency&gt;</pre></p> </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> </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"> <div class="container">
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" /> <div class="input-append">
<button class="btn" title="reload list" ng-click="refresh()"><i class="fa fa-repeat"></i></button> <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> </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'); ...@@ -20,13 +20,25 @@ var angular = require('angular');
var module = angular.module('sba-applications-logfile', ['sba-applications']); var module = angular.module('sba-applications-logfile', ['sba-applications']);
global.sbaModules.push(module.name); 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({ ApplicationViews.register({
order: 1, order: 1,
title: $sce.trustAsHtml('<i class="fa fa-file-text-o fa-fw"></i>Log'), title: $sce.trustAsHtml('<i class="fa fa-file-text-o fa-fw"></i>Log'),
href: 'api/applications/{id}/logfile', state: 'applications.logfile',
target: '_blank',
show: function (application) { show: function (application) {
if (!application.managementUrl || !application.statusInfo.status || application.statusInfo.status === 'OFFLINE') { if (!application.managementUrl || !application.statusInfo.status || application.statusInfo.status === 'OFFLINE') {
return false; return false;
...@@ -38,5 +50,5 @@ module.run(function ($sce, $http, ApplicationViews) { ...@@ -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"> <div class="container">
<p>To make the logging section work you need to make the /jolokia-endpoint accessible. <div ng-if="errorWhileListing">
<br/> Include the jolokia-core.jar in your spring-boot-application: <p>To make the logging section work you need to make the /jolokia-endpoint accessible.
<pre>&lt;dependency&gt; <br/> Include the jolokia-core.jar in your spring-boot-application:
&lt;groupId>org.jolokia&lt;/groupId&gt; <pre>&lt;dependency&gt; &lt;groupId>org.jolokia&lt;/groupId&gt; &lt;artifactId>jolokia-core&lt;/artifactId&gt; &lt;/dependency&gt;</pre>
&lt;artifactId>jolokia-core&lt;/artifactId&gt; </p>
&lt;/dependency&gt;</pre></p> <p>Please note that the logging section currently only works with Logback.
<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>:
<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>
<pre>&lt;configuration&gt; </p>
&lt;include resource="org/springframework/boot/logging/logback/base.xml"/&gt; </div>
&lt;jmxConfigurator/&gt; <pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre>
&lt;/configuration&gt;</pre></p> <div ng-show="loggers">
</div> <form>
<pre class="alert alert-error" ng-if="error"><b>Error:</b><br/>{{ error | json }}</pre> <div class="input-prepend input-append">
<div ng-show="loggers"> <button class="btn" title="Show package-level loggers" ng-class="{'btn-inverse': showPackageLoggers}" ng-click="togglePackageLoggers()"><i class="fa fa-folder-o"></i></button>
<form> <input placeholder="Filter by name ..." class="input-xxlarge" type="search" ng-model="filterLogger.name" />
<div class="input-prepend input-append"> <button class="btn" title="reload list" ng-click="refreshLoggers()"><i class="fa fa-repeat"></i></button>
<button class="btn" title="Show package-level loggers" ng-class="{'btn-inverse': showPackageLoggers}" ng-click="togglePackageLoggers()"><i class="fa fa-folder-o"></i></button> <span title="filtered / total" class="add-on">{{ filteredLoggers.length }}/{{ loggers.length }}</span>
<input placeholder="Filter by name ..." class="input-xxlarge" type="search" ng-model="filterLogger.name" /> </div>
<button class="btn" title="reload list" ng-click="refreshLoggers()"><i class="fa fa-repeat"></i></button> </form>
<span title="filtered / total" class="add-on">{{ filteredLoggers.length }}/{{ loggers.length }}</span> <table class="table table-hover">
</div> <tbody>
</form> <tr ng-repeat="logger in (filteredLoggers = (loggers | filter:packageFilter | filter:filterLogger) ) | limitTo: limit track by logger.name">
<table class="table table-hover"> <td>
<tbody> <sba-logger value="logger" on-level-changed="refreshLoggers"></sba-logger>
<tr ng-repeat="logger in (filteredLoggers = (loggers | filter:packageFilter | filter:filterLogger) ) | limitTo: limit track by logger.name"> </td>
<td> </tr>
<sba-logger value="logger" on-level-changed="refreshLoggers"></sba-logger> <tr ng-show="limit < loggers.length">
</td> <td>
</tr> <button class="btn btn-link btn-block" ng-click="limit = limit + 10">show more</button>
<tr ng-show="limit < loggers.length"> </td>
<td> </tr>
<button class="btn btn-link btn-block" ng-click="limit = limit + 10">show more</button> <tr ng-show="limit < loggers.length">
</td> <td>
</tr> <button class="btn btn-link btn-block" ng-click="limit = loggers.length">show all</button>
<tr ng-show="limit < loggers.length"> </td>
<td> </tr>
<button class="btn btn-link btn-block" ng-click="limit = loggers.length">show all</button> </tbody>
</td> </table>
</tr> </div>
</tbody>
</table>
</div> </div>
<div class="alert alert-error" ng-if="error"> <div class="container">
<b>Error:</b> {{ error }} <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> </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"> <div class="container">
<b>Error:</b> {{ error }} <div class="alert alert-error" ng-if="error">
</div> <b>Error:</b> {{ error }}
<div class="row"> </div>
<div class="text-center"> <div class="row">
<form> <div class="text-center">
<button class="btn" ng-click="dumpThreads()"><i class="fa fa-repeat"></i> refresh</button> <form>
</form> <button class="btn" ng-click="dumpThreads()"><i class="fa fa-repeat"></i> refresh</button>
</form>
</div>
</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> </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"> <div class="container">
<b>Error:</b> {{ error }} <div class="alert alert-error" ng-if="error">
</div> <b>Error:</b> {{ error }}
<div class="form-inline"> </div>
<button title="refresh" class="btn" ng-click="refresh()"><i class="fa fa-repeat"></i></button> <div class="form-inline">
<div class="input-prepend input-append"> <button title="refresh" class="btn" ng-click="refresh()"><i class="fa fa-repeat"></i></button>
<button title="auto refresh" class="btn" ng-click="toggleAutoRefresh()" ng-class="{'active':refresher != null}"> <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> <i class="fa fa-refresh" ng-class="{'fa-spin':refresher != null}"></i>
</button> </button>
<input class="input-mini" type="number" min="1" ng-model="refreshInterval" ng-disabled="refresher != null" /> <input class="input-mini" type="number" min="1" ng-model="refreshInterval" ng-disabled="refresher != null" />
<span class="add-on">sec</span> <span class="add-on">sec</span>
</div>
<input placeholder="Filter" class="input-xxlarge" type="search" ng-model="searchFilter" />
</div> </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> </div>
<ul class="timeline">
<li ng-repeat="trace in traces | filter:searchFilter">
<sba-trace value="trace"></sba-trace>
</li>
</ul>
.header--application { .header--application {
margin-bottom: 40px; margin-bottom: 20px;
font-size: 15px; font-size: 15px;
} }
.header--application .navbar-inner { .header--application .navbar-inner {
......
...@@ -18,4 +18,4 @@ ...@@ -18,4 +18,4 @@
</div> </div>
</div> </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