Performance: use virtual scroll container and log level parsing for logs view (#11233)

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
CanbiZ
2025-10-30 16:34:53 -07:00
committed by GitHub
parent b9aced07fb
commit e5bd4713ac
4 changed files with 46 additions and 16 deletions

View File

@@ -29,14 +29,19 @@
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
<cdk-virtual-scroll-viewport
itemSize="20"
class="bg-dark p-3 text-light font-monospace log-container"
#logContainer>
@if (loading && logFiles.length) {
<div>
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
}
@for (log of logs; track $index) {
<p class="m-0 p-0 log-entry-{{getLogLevel(log)}}">{{log}}</p>
}
</div>
<p *cdkVirtualFor="let log of logs"
class="m-0 p-0"
[ngClass]="'log-entry-' + log.level">
{{log.message}}
</p>
</cdk-virtual-scroll-viewport>

View File

@@ -18,7 +18,7 @@
.log-container {
overflow-y: scroll;
height: calc(100vh - 200px);
top: 70px;
top: 0;
p {
white-space: pre-wrap;

View File

@@ -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)

View File

@@ -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)
}
}
}