提交 b1272213 编写于 作者: M Marcin Maciaszczyk 提交者: GitHub

Introduce logs autorefresh/follow (#2496)

* Add refresh button for logs view

* Add auto refresh for logs

* Refresh after clicking icon

* Minor improvements

* Multiple improvements

* Fix CI

* Fix CI
上级 f7b7419c
......@@ -504,6 +504,7 @@
<translation id="1223986507449568472" key="MSG_LOGS_LOGS_3" desc="Footer part for logs card.">to</translation>
<translation id="8510779305487405945" key="MSG_LOGS_LOGS_4" desc="Label atop a list of Containers">Containers</translation>
<translation id="7432843701863632053" key="MSG_LOGS_LOGS_5" desc="Label atop a list of Init Containers">Init Containers</translation>
<translation id="2297835557948059978" key="MSG_LOGS_LOGS_6" desc="(no description provided)">Toggle auto-refresh (every {{ctrl.refreshInterval / 1000}} seconds)</translation>
<translation id="3402202863027894322" key="MSG_LOGS_TRUNCATED_WARNING" desc="Error dialog indicating that parts of the log file is missing due to memory constraints.">The middle part of the log file cannot be loaded, because it is too big.</translation>
<translation id="7594890250973129963" key="MSG_LOGS_ZEROSTATE_TEXT" desc="Text for logs card zerostate in logs page.">The selected container has not logged any messages yet.</translation>
<translation id="2533795620042066843" key="MSG_MORE_WARNINGS_LABEL" desc="Show more warnings button label.">Show <ph name="COUNT"> more</ph></translation>
......
......@@ -510,6 +510,7 @@
<translation id="1223986507449568472" key="MSG_LOGS_LOGS_3" desc="Footer part for logs card.">to</translation>
<translation id="8510779305487405945" key="MSG_LOGS_LOGS_4" desc="Label atop a list of Containers">Containers</translation>
<translation id="7432843701863632053" key="MSG_LOGS_LOGS_5" desc="Label atop a list of Init Containers">Init Containers</translation>
<translation id="2297835557948059978" key="MSG_LOGS_LOGS_6" desc="(no description provided)">Toggle auto-refresh (every {{ctrl.refreshInterval / 1000}} seconds)</translation>
<translation id="3402202863027894322" key="MSG_LOGS_TRUNCATED_WARNING" desc="Error dialog indicating that parts of the log file is missing due to memory constraints.">大きすぎるため、ログファイルの中間部分は読み込めません。</translation>
<translation id="7594890250973129963" key="MSG_LOGS_ZEROSTATE_TEXT" desc="Text for logs card zerostate in logs page.">選択されたコンテナにはログにメッセージが記録されていません。</translation>
<translation id="2533795620042066843" key="MSG_MORE_WARNINGS_LABEL" desc="Show more warnings button label.">Show <ph name="COUNT"> more</ph></translation>
......
......@@ -502,6 +502,7 @@
<translation id="1223986507449568472" key="MSG_LOGS_LOGS_3" desc="Footer part for logs card."></translation>
<translation id="8510779305487405945" key="MSG_LOGS_LOGS_4" desc="Label atop a list of Containers">Containers</translation>
<translation id="7432843701863632053" key="MSG_LOGS_LOGS_5" desc="Label atop a list of Init Containers">Init Containers</translation>
<translation id="2297835557948059978" key="MSG_LOGS_LOGS_6" desc="(no description provided)">Toggle auto-refresh (every {{ctrl.refreshInterval / 1000}} seconds)</translation>
<translation id="3402202863027894322" key="MSG_LOGS_TRUNCATED_WARNING" desc="Error dialog indicating that parts of the log file is missing due to memory constraints.">日誌檔案過大,中間部分無法載入</translation>
<translation id="7594890250973129963" key="MSG_LOGS_ZEROSTATE_TEXT" desc="Text for logs card zerostate in logs page.">選擇的容器沒有日誌訊息</translation>
<translation id="2533795620042066843" key="MSG_MORE_WARNINGS_LABEL" desc="Show more warnings button label.">顯示更多 <ph name="COUNT"></ph></translation>
......
......@@ -504,6 +504,7 @@
<translation id="1223986507449568472" key="MSG_LOGS_LOGS_3" desc="Footer part for logs card."></translation>
<translation id="8510779305487405945" key="MSG_LOGS_LOGS_4" desc="Label atop a list of Containers">Containers</translation>
<translation id="7432843701863632053" key="MSG_LOGS_LOGS_5" desc="Label atop a list of Init Containers">Init Containers</translation>
<translation id="2297835557948059978" key="MSG_LOGS_LOGS_6" desc="(no description provided)">Toggle auto-refresh (every {{ctrl.refreshInterval / 1000}} seconds)</translation>
<translation id="3402202863027894322" key="MSG_LOGS_TRUNCATED_WARNING" desc="Error dialog indicating that parts of the log file is missing due to memory constraints.">日志文件过大,中间部分无法加载</translation>
<translation id="7594890250973129963" key="MSG_LOGS_ZEROSTATE_TEXT" desc="Text for logs card zerostate in logs page.">所选容器没有日志信息</translation>
<translation id="2533795620042066843" key="MSG_MORE_WARNINGS_LABEL" desc="Show more warnings button label.">显示更多 <ph name="COUNT"></ph></translation>
......
......@@ -34,10 +34,12 @@ export class LogsController {
* @param {!angular.$sce} $sce
* @param {!angular.$document} $document
* @param {!angular.$resource} $resource
* @param {!angular.$interval} $interval
* @param {!angular.$log} $log
* @param {!../common/errorhandling/dialog.ErrorDialog} errorDialog
* @ngInject
*/
constructor(logsService, $sce, $document, $resource, errorDialog) {
constructor(logsService, $sce, $document, $resource, $interval, $log, errorDialog) {
/** @private {!angular.$sce} */
this.sce_ = $sce;
......@@ -47,6 +49,12 @@ export class LogsController {
/** @private {!angular.$resource} */
this.resource_ = $resource;
/** @private {!angular.$interval} */
this.interval_ = $interval;
/** @private {!angular.$log} */
this.log_ = $log;
/** @export {!./service.LogsService} */
this.logsService = logsService;
......@@ -92,8 +100,11 @@ export class LogsController {
/** @private {!ui.router.$stateParams} */
this.stateParams_;
/** @export {!kdUiRouter.$transition$} - initialized from resolve */
/** @export {!kdUiRouter.$transition$} */
this.$transition$;
/** @export {number} Refresh interval in miliseconds. */
this.refreshInterval = 5000;
}
......@@ -103,6 +114,21 @@ export class LogsController {
this.stateParams_ = this.$transition$.params();
this.updateUiModel(this.podLogs);
this.topIndex = this.podLogs.logs.length;
this.registerIntervalFunction_();
}
/**
* Registers interval function used to automatically refresh logs.
*
* @private
*/
registerIntervalFunction_() {
this.interval_(() => {
if (this.logsService.getFollowing()) {
this.loadNewest();
this.log_.info('Automatically refreshed logs');
}
}, this.refreshInterval);
}
......@@ -144,6 +170,18 @@ export class LogsController {
this.currentSelection.offsetTo + logsPerView);
}
/**
* Toggles log follow mechanism.
*
* @export
*/
toggleLogFollow() {
this.logsService.setFollowing();
if (this.logsService.getFollowing()) {
this.loadNewest();
}
}
/**
* Downloads and loads slice of logs as specified by offsetFrom and offsetTo.
* It works just like normal slicing, but indices are referenced relatively to certain reference
......@@ -239,83 +277,6 @@ export class LogsController {
return div.innerHTML;
}
/**
* Indicates log area font size.
* @export
* @return {string}
*/
getLogsClass() {
const logsTextSize = 'kd-logs-element';
if (this.logsService.getCompact()) {
return `${logsTextSize}-compact`;
}
return logsTextSize;
}
/**
* Return proper style class for logs content.
* @export
* @return {string}
*/
getStyleClass() {
const logsTextColor = 'kd-logs-text-color';
if (this.logsService.getInverted()) {
return `${logsTextColor}-invert`;
}
return logsTextColor;
}
/**
* Return proper style class for text color icon.
* @export
* @return {string}
*/
getColorIconClass() {
const logsTextColor = 'kd-logs-color-icon';
if (this.logsService.getInverted()) {
return `${logsTextColor}-invert`;
}
return logsTextColor;
}
/**
* Return proper style class for font size icon.
* @export
* @return {string}
*/
getSizeIconClass() {
const logsTextColor = 'kd-logs-size-icon';
if (this.logsService.getCompact()) {
return `${logsTextColor}-compact`;
}
return logsTextColor;
}
/**
* Return the proper icon depending on the timestamp selection state
* @export
* @return {string}
*/
getTimestampIcon() {
if (this.logsService.getShowTimestamp()) {
return 'timer';
}
return 'timer_off';
}
/**
* Return the proper icon depending on the previous-container-logs selection state
* @export
* @return {string}
*/
getPreviousIcon() {
if (this.logsService.getPrevious()) {
return 'exposure_neg_1';
}
return 'exposure_zero';
}
/**
* Return the link to download the log file
* @export
......@@ -327,24 +288,6 @@ export class LogsController {
this.logsService.getPrevious()}`;
}
/**
* Execute when a user changes the container from which logs should be loaded.
* @export
*/
onContainerChange() {
this.loadNewest();
}
/**
* Execute when a user changes the pod from which logs should be loaded.
*
* @export
*/
onPodChange() {
this.loadNewest();
}
/**
* Execute when a user changes the selected option for console font size.
* @export
......
......@@ -20,7 +20,7 @@ limitations under the License.
<md-select class="kd-logs-toolbar-select"
aria-label="container"
ng-model="ctrl.container"
md-on-close="ctrl.onContainerChange(ctrl.container)"
md-on-close="ctrl.loadNewest()"
required>
<md-optgroup label="[[Containers|Label atop a list of Containers]]"
class="kd-namespace-select-namespace-list">
......@@ -41,7 +41,7 @@ limitations under the License.
<md-select class="kd-logs-toolbar-select"
aria-label="resource"
ng-model="ctrl.pod"
md-on-close="ctrl.onContainerChange(ctrl.pod)"
md-on-close="ctrl.loadNewest()"
required>
<md-option ng-repeat="item in ctrl.logSources.podNames"
ng-value="item">
......@@ -51,47 +51,58 @@ limitations under the License.
<div class="kd-logs-style-buttons">
<md-button class="kd-logs-toolbar-button"
ng-click="ctrl.onTextColorChange()">
<md-icon md-font-library="material-icons"
ng-class="ctrl.getColorIconClass()">
<md-icon ng-class="{'kd-logs-color-icon-invert': ctrl.logsService.getInverted(),
'kd-logs-color-icon': !ctrl.logsService.getInverted()}">
format_color_text
</md-icon>
</md-button>
<md-button class="kd-logs-toolbar-button"
ng-click="ctrl.onFontSizeChange()">
<md-icon md-font-library="material-icons"
ng-class="ctrl.getSizeIconClass()">
<md-icon ng-class="{'kd-logs-size-icon-compact': ctrl.logsService.getCompact(),
'kd-logs-size-icon': !ctrl.logsService.getCompact()}">
text_fields
</md-icon>
</md-button>
<md-button class="kd-logs-toolbar-button"
ng-click="ctrl.onShowTimestamp()">
<md-icon md-font-library="material-icons"
class="kd-logs-icon">
{{ctrl.getTimestampIcon()}}
<md-icon class="kd-logs-icon">{{ctrl.logsService.getShowTimestamp() ? 'timer' : 'timer_off'}}</md-icon>
</md-button>
<md-button class="kd-logs-toolbar-button"
ng-click="ctrl.toggleLogFollow()">
<md-icon class="kd-logs-icon">
refresh
<svg class="kd-cross-style"
ng-if="!ctrl.logsService.getFollowing()"
width="32px"
height="32px"
viewBox="0 0 32 32">
<line class="kd-cross-line-white" x1="9" x2="27" y1="9" y2="27"/>
<line class="kd-cross-line-black" x1="9" x2="27" y1="10" y2="28"/>
</svg>
</md-icon>
<md-tooltip md-delay="500"
md-autohide>
[[Toggle auto-refresh (every {{ctrl.refreshInterval / 1000}} seconds)|]]
</md-tooltip>
</md-button>
<md-button class="kd-logs-toolbar-button"
ng-click="ctrl.onPreviousChange()">
<md-icon md-font-library="material-icons"
class="kd-logs-icon">
{{ctrl.getPreviousIcon()}}
</md-icon>
<md-icon class="kd-logs-icon">{{ctrl.logsService.getPrevious() ? 'exposure_neg_1' : 'exposure_zero'}}</md-icon>
</md-button>
<md-button class="kd-logs-toolbar-button"
ng-href="{{ctrl.getDownloadLink()}}">
<md-icon md-font-library="material-icons"
class="kd-logs-icon">
file_download
</md-icon>
<md-icon class="kd-logs-icon">file_download</md-icon>
</md-button>
</div>
</kd-title>
<kd-content>
<md-virtual-repeat-container class="kd-log-view"
ng-class="ctrl.getStyleClass()"
ng-class="{'kd-logs-text-color-invert': ctrl.logsService.getInverted(),
'kd-logs-text-color': !ctrl.logsService.getInverted()}"
md-top-index="ctrl.topIndex">
<div md-virtual-repeat="item in ctrl.logsSet"
ng-class="ctrl.getLogsClass()">
ng-class="{'kd-logs-element-compact': ctrl.logsService.getCompact(),
'kd-logs-element': !ctrl.logsService.getCompact()}">
<span ng-bind-html="item"
class="kd-log-line">&nbsp;</span>
</div>
......
......@@ -141,3 +141,8 @@ kd-content-card {
.kd-logs-style-buttons {
float: right;
}
.kd-cross-line-black {
stroke: $emphasis;
stroke-width: 2;
}
......@@ -32,63 +32,66 @@ export class LogsService {
/** @private {boolean} */
this.previous_ = false;
/** @private {boolean} */
this.following_ = false;
}
setFollowing() {
this.following_ = !this.following_;
}
/**
* Getter for inverted flag.
* @return {boolean}
* @export
*/
getInverted() {
return this.inverted_;
getFollowing() {
return this.following_;
}
/**
* Switches the inverted flag.
*/
setInverted() {
this.inverted_ = !this.inverted_;
}
/**
* Switches the compact flag.
* @return {boolean}
* @export
*/
getInverted() {
return this.inverted_;
}
setCompact() {
this.compact_ = !this.compact_;
}
/**
* Getter for compact flag.
* @return {boolean}
* @export
*/
getCompact() {
return this.compact_;
}
/**
* Switches the show timestamp flag
*/
setShowTimestamp() {
this.showTimestamp_ = !this.showTimestamp_;
}
/**
* Getter for the show timestamp flag
* @returns {boolean}
* @return {boolean}
* @export
*/
getShowTimestamp() {
return this.showTimestamp_;
}
/**
* Switches the show previous flag
*/
setPrevious() {
this.previous_ = !this.previous_;
}
/**
* Getter for the show previous flag
* @returns {boolean}
* @return {boolean}
* @export
*/
getPrevious() {
return this.previous_;
......
......@@ -26,17 +26,14 @@ limitations under the License.
style="position: relative">
<md-button ng-click="isActive = !isActive"
class="md-icon-button">
<!-- SVG drawing to simulate crossed-eye icon.
This should be removed if possible. -->
<svg ng-if="isActive"
width="40px"
height="40px"
viewBox="0 0 40 40"
style="left: 0; top:0; position:absolute; bottom:0; right: 0;">
<line x1="10" y1="10" x2="30" y2="30"
style="stroke-width: 2; stroke: rgba(0,0,0,0.54);"/>
</svg>
<md-icon md-font-library="material-icons">remove_red_eye</md-icon>
<md-icon md-font-library="material-icons"
ng-if="isActive">
visibility_off
</md-icon>
<md-icon md-font-library="material-icons"
ng-if="!isActive">
visibility
</md-icon>
<md-tooltip md-delay="500"
md-autohide
ng-if="!isActive">
......
......@@ -18,13 +18,6 @@ import LogsModule from 'logs/module';
import {StateParams} from 'logs/state';
describe('Logs controller', () => {
/** @type {string} */
const logsTextColorClassName = 'kd-logs-text-color-invert';
/** @type {string} */
const logsTextSizeClassName = 'kd-logs-element';
/** @type {!LogsController} */
let ctrl;
......@@ -96,12 +89,6 @@ describe('Logs controller', () => {
expect(ctrl).not.toBeUndefined();
});
it('should return style classes for logs content', () => {
// expect
expect(ctrl.getStyleClass()).toEqual(`${logsTextColorClassName}`);
expect(ctrl.getLogsClass()).toEqual(`${logsTextSizeClassName}`);
});
it('should display zero state log line if server returned no logs', () => {
ctrl.podLogs.logs = [];
ctrl.$onInit();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册