提交 3f1009e0 编写于 作者: E Elijah Oyekunle 提交者: Kubernetes Prow Robot

Logs Auto Scroll (#3561)

* wip

* remove scroll method

* added isScrolledBottom

* updated isScrolledBottom

* removed unnecessary stuffs

* - adds option to follow logs
- scrolls to bottom on navigation to prev page
- scrolls to top on navigation to next page

* remove invert class

* remove invert class

* fix html bug
上级 630c8464
......@@ -22,7 +22,8 @@ export class LogService {
inverted_ = true;
compact_ = false;
showTimestamp_ = false;
following_ = false;
following_ = true;
autoRefresh_ = false;
constructor(private readonly http_: HttpClient) {}
......@@ -30,7 +31,11 @@ export class LogService {
return this.http_.get<T>(`api/v1/log/${uri}`, {params});
}
setFollowing(): void {
setFollowing(status: boolean): void {
this.following_ = status;
}
toggleFollowing(): void {
this.following_ = !this.following_;
}
......@@ -38,6 +43,14 @@ export class LogService {
return this.following_;
}
setAutoRefresh(): void {
this.autoRefresh_ = !this.autoRefresh_;
}
getAutoRefresh(): boolean {
return this.autoRefresh_;
}
setPrevious(): void {
this.previous_ = !this.previous_;
}
......
......@@ -13,7 +13,7 @@
// limitations under the License.
import {HttpParams} from '@angular/common/http';
import {Component, OnDestroy} from '@angular/core';
import {Component, ElementRef, OnDestroy, ViewChild} from '@angular/core';
import {MatDialog} from '@angular/material';
import {LogDetails, LogLine, LogSelection, LogSources} from '@api/backendapi';
import {StateService} from '@uirouter/core';
......@@ -40,8 +40,11 @@ const i18n = {
'The middle part of the log file cannot be loaded, because it is too big.'
};
type ScrollPosition = 'TOP'|'BOTTOM';
@Component({selector: 'kd-logs', templateUrl: './template.html', styleUrls: ['./style.scss']})
export class LogsComponent implements OnDestroy {
@ViewChild('logViewContainer') logViewContainer_: ElementRef;
podLogs: LogDetails;
logsSet: string[];
logSources: LogSources;
......@@ -112,6 +115,13 @@ export class LogsComponent implements OnDestroy {
if (podLogs.info.truncated) {
this.notifications_.push(i18n.MSG_LOGS_TRUNCATED_WARNING, NotificationSeverity.error);
}
if (this.logService.getFollowing()) {
// Pauses very slightly for the view to refresh.
setTimeout(() => {
this.scrollToBottom();
});
}
}
formatAllLogs(logs: LogLine[]): string[] {
......@@ -157,7 +167,8 @@ export class LogsComponent implements OnDestroy {
this.loadView(
this.currentSelection.logFilePosition, this.currentSelection.referencePoint.timestamp,
this.currentSelection.referencePoint.lineNum,
this.currentSelection.offsetFrom - logsPerView, this.currentSelection.offsetFrom);
this.currentSelection.offsetFrom - logsPerView, this.currentSelection.offsetFrom,
this.scrollToBottom.bind(this));
}
/**
......@@ -167,7 +178,7 @@ export class LogsComponent implements OnDestroy {
this.loadView(
this.currentSelection.logFilePosition, this.currentSelection.referencePoint.timestamp,
this.currentSelection.referencePoint.lineNum, this.currentSelection.offsetTo,
this.currentSelection.offsetTo + logsPerView);
this.currentSelection.offsetTo + logsPerView, this.scrollToTop.bind(this));
}
/**
......@@ -180,7 +191,7 @@ export class LogsComponent implements OnDestroy {
*/
loadView(
logFilePosition: string, referenceTimestamp: string, referenceLinenum: number,
offsetFrom: number, offsetTo: number): void {
offsetFrom: number, offsetTo: number, onLoad?: Function): void {
const namespace = this.state_.params.resourceNamespace;
const params = new HttpParams()
.set('logFilePosition', logFilePosition)
......@@ -193,6 +204,9 @@ export class LogsComponent implements OnDestroy {
this.logService.getResource(`${namespace}/${this.pod}/${this.container}`, params)
.subscribe((podLogs: LogDetails) => {
this.updateUiModel(podLogs);
if (onLoad) {
onLoad();
}
});
}
......@@ -218,12 +232,19 @@ export class LogsComponent implements OnDestroy {
this.loadNewest();
}
/**
* Toggles log auto-refresh mechanism.
*/
toggleLogAutoRefresh(): void {
this.logService.setAutoRefresh();
this.toggleIntervalFunction();
}
/**
* Toggles log follow mechanism.
*/
toggleLogFollow(): void {
this.logService.setFollowing();
this.toggleIntervalFunction();
this.logService.toggleFollowing();
}
/**
......@@ -242,4 +263,54 @@ export class LogsComponent implements OnDestroy {
const dialogData = {data: {pod: this.pod, container: this.container}};
this.dialog_.open(LogsDownloadDialog, dialogData);
}
/**
* Listens for scroll events to set log following state.
*/
onLogsScroll(): void {
this.logService.setFollowing(this.isScrolledBottom());
}
/**
* Checks if the current logs scroll position is at the bottom.
*/
isScrolledBottom(): boolean {
const {nativeElement} = this.logViewContainer_;
return nativeElement.scrollHeight <= nativeElement.scrollTop + nativeElement.clientHeight;
}
/**
* Scrolls log view to the bottom of the page.
*/
scrollToBottom(): void {
this.scrollTo('BOTTOM');
}
/**
* Scrolls log view to the top of the page.
*/
scrollToTop(): void {
this.scrollTo('TOP');
}
scrollTo(position: ScrollPosition): void {
const {nativeElement} = this.logViewContainer_;
if (!nativeElement) {
return;
}
let top;
switch (position) {
case 'TOP':
top = 0;
break;
case 'BOTTOM':
top = nativeElement.scrollHeight;
break;
default:
return;
}
nativeElement.scrollTo({top, left: 0, behavior: 'smooth'});
}
}
......@@ -70,11 +70,11 @@ limitations under the License.
</button>
<button mat-icon-button
matTooltip="Toggle auto-refresh (every {{refreshInterval / 1000}} seconds)"
(click)="toggleLogFollow()">
(click)="toggleLogAutoRefresh()">
<mat-icon>
refresh
<svg class="kd-cross-style"
*ngIf="!logService.getFollowing()"
*ngIf="!logService.getAutoRefresh()"
width="32px"
height="32px"
viewBox="0 0 32 32">
......@@ -91,6 +91,11 @@ limitations under the License.
</svg>
</mat-icon>
</button>
<button mat-icon-button
[matTooltip]="logService.getFollowing() ? 'Following logs' : 'Follow logs'"
(click)="toggleLogFollow()">
<mat-icon>{{logService.getFollowing() ? 'flash_on' : 'flash_off'}}</mat-icon>
</button>
<button mat-icon-button
matTooltip="{{logService?.getPrevious() ? 'Show current logs' : 'Show previous logs'}}"
(click)="onPreviousChange()">
......@@ -109,7 +114,9 @@ limitations under the License.
[ngClass]="{'kd-logs-text-color-invert': logService?.getInverted(), 'kd-logs-text-color': !logService?.getInverted()}">
<div kdLoadingSpinner
[isLoading]="isLoading"></div>
<div class="kd-log-view-container">
<div class="kd-log-view-container"
#logViewContainer
(scroll)="onLogsScroll()">
<div class="kd-virtual-repeat-sizer"></div>
<div *ngFor="let item of logsSet"
class="kd-logs-element"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册