From e5bd4713ac045c07eade1e1890e1f7a252a0b682 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:34:53 -0700 Subject: [PATCH] Performance: use virtual scroll container and log level parsing for logs view (#11233) --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- .../components/admin/logs/logs.component.html | 15 ++++++--- .../components/admin/logs/logs.component.scss | 2 +- .../admin/logs/logs.component.spec.ts | 13 +++++++- .../components/admin/logs/logs.component.ts | 32 +++++++++++++------ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src-ui/src/app/components/admin/logs/logs.component.html b/src-ui/src/app/components/admin/logs/logs.component.html index d6685d857a..21df9f33bd 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.html +++ b/src-ui/src/app/components/admin/logs/logs.component.html @@ -29,14 +29,19 @@
-{{log}}
- } -+ {{log.message}} +
+ diff --git a/src-ui/src/app/components/admin/logs/logs.component.scss b/src-ui/src/app/components/admin/logs/logs.component.scss index 834c8c1cb9..56fd2e8f36 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.scss +++ b/src-ui/src/app/components/admin/logs/logs.component.scss @@ -18,7 +18,7 @@ .log-container { overflow-y: scroll; height: calc(100vh - 200px); - top: 70px; + top: 0; p { white-space: pre-wrap; diff --git a/src-ui/src/app/components/admin/logs/logs.component.spec.ts b/src-ui/src/app/components/admin/logs/logs.component.spec.ts index 6e4adacfe0..841fec44d5 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.spec.ts +++ b/src-ui/src/app/components/admin/logs/logs.component.spec.ts @@ -1,3 +1,8 @@ +import { + CdkVirtualScrollViewport, + ScrollingModule, +} from '@angular/cdk/scrolling' +import { CommonModule } from '@angular/common' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' import { ComponentFixture, TestBed } from '@angular/core/testing' @@ -38,6 +43,9 @@ describe('LogsComponent', () => { NgxBootstrapIconsModule.pick(allIcons), LogsComponent, PageHeaderComponent, + CommonModule, + CdkVirtualScrollViewport, + ScrollingModule, ], providers: [ provideHttpClient(withInterceptorsFromDi()), @@ -54,7 +62,6 @@ describe('LogsComponent', () => { fixture = TestBed.createComponent(LogsComponent) component = fixture.componentInstance reloadSpy = jest.spyOn(component, 'reloadLogs') - window.HTMLElement.prototype.scroll = function () {} // mock scroll jest.useFakeTimers() fixture.detectChanges() }) @@ -83,6 +90,10 @@ describe('LogsComponent', () => { }) it('should auto refresh, allow toggle', () => { + jest + .spyOn(CdkVirtualScrollViewport.prototype, 'scrollToIndex') + .mockImplementation(() => undefined) + jest.advanceTimersByTime(6000) expect(reloadSpy).toHaveBeenCalledTimes(2) diff --git a/src-ui/src/app/components/admin/logs/logs.component.ts b/src-ui/src/app/components/admin/logs/logs.component.ts index 4799b61252..488f9db264 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.ts +++ b/src-ui/src/app/components/admin/logs/logs.component.ts @@ -1,7 +1,11 @@ +import { + CdkVirtualScrollViewport, + ScrollingModule, +} from '@angular/cdk/scrolling' +import { CommonModule } from '@angular/common' import { ChangeDetectorRef, Component, - ElementRef, OnDestroy, OnInit, ViewChild, @@ -21,8 +25,11 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading imports: [ PageHeaderComponent, NgbNavModule, + CommonModule, FormsModule, ReactiveFormsModule, + CdkVirtualScrollViewport, + ScrollingModule, ], }) export class LogsComponent @@ -32,7 +39,7 @@ export class LogsComponent private logService = inject(LogService) private changedetectorRef = inject(ChangeDetectorRef) - public logs: string[] = [] + public logs: Array<{ message: string; level: number }> = [] public logFiles: string[] = [] @@ -40,7 +47,7 @@ export class LogsComponent public autoRefreshEnabled: boolean = true - @ViewChild('logContainer') logContainer: ElementRef + @ViewChild('logContainer') logContainer: CdkVirtualScrollViewport ngOnInit(): void { this.logService @@ -75,7 +82,7 @@ export class LogsComponent .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (result) => { - this.logs = result + this.logs = this.parseLogsWithLevel(result) this.loading = false this.scrollToBottom() }, @@ -100,12 +107,19 @@ export class LogsComponent } } + private parseLogsWithLevel( + logs: string[] + ): Array<{ message: string; level: number }> { + return logs.map((log) => ({ + message: log, + level: this.getLogLevel(log), + })) + } + scrollToBottom(): void { this.changedetectorRef.detectChanges() - this.logContainer?.nativeElement.scroll({ - top: this.logContainer.nativeElement.scrollHeight, - left: 0, - behavior: 'auto', - }) + if (this.logContainer) { + this.logContainer.scrollToIndex(this.logs.length - 1) + } } } -- 2.47.3