Logs viewer will now color-code logs based on type (error, warning, info, etc.)

You can also clear logs from the logs viewer as well
This commit is contained in:
Isaac Grynsztein
2020-07-05 22:17:54 -04:00
parent 990b3d4037
commit e97e9ec717
15 changed files with 120 additions and 33 deletions

View File

@@ -73,6 +73,7 @@ import { ManageRoleComponent } from './components/manage-role/manage-role.compon
import { CookiesUploaderDialogComponent } from './dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component';
import { LogsViewerComponent } from './components/logs-viewer/logs-viewer.component';
import { ModifyPlaylistComponent } from './dialogs/modify-playlist/modify-playlist.component';
import { ConfirmDialogComponent } from './dialogs/confirm-dialog/confirm-dialog.component';
registerLocaleData(es, 'es');
@@ -113,7 +114,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
ManageRoleComponent,
CookiesUploaderDialogComponent,
LogsViewerComponent,
ModifyPlaylistComponent
ModifyPlaylistComponent,
ConfirmDialogComponent
],
imports: [
CommonModule,

View File

@@ -1,11 +1,24 @@
<div style="height: 100%">
<div *ngIf="logs_loading" style="position: absolute; top: 40%; left: 50%">
<div style="height: 275px;">
<div *ngIf="logs_loading" style="z-index: 999; position: absolute; top: 40%; left: 50%">
<mat-spinner [diameter]="32"></mat-spinner>
</div>
<textarea style="height: 275px" matInput readonly [(ngModel)]="logs" placeholder="Logs will appear here" i18n-placeholder="Logs placeholder"></textarea>
<!-- Virtual mode (fast, select text buggy) -->
<!--<cdk-virtual-scroll-viewport style="height: 274px;" itemSize="50" class="example-viewport">
<div *cdkVirtualFor="let log of logs; let i = index" class="example-item">
<span [ngStyle]="{'color':log.color}">{{log.text}}</span>
</div>
</cdk-virtual-scroll-viewport>-->
<!-- Non-virtual mode (slow, bug-free) -->
<div style="height: 274px; overflow-y: auto">
<div *ngFor="let log of logs; let i = index" class="example-item">
<span [ngStyle]="{'color':log.color}">{{log.text}}</span>
</div>
</div>
<div>
<button style="margin-top: 12px;" [cdkCopyToClipboard]="logs" (click)="copiedLogsToClipboard()" mat-flat-button color="primary"><ng-container i18n="Copy to clipboard button text">Copy to clipboard</ng-container></button>
<div style="float: right">
<button style="position: absolute; right: 0px; top: 12px;" [cdkCopyToClipboard]="logs_text" (click)="copiedLogsToClipboard()" mat-mini-fab color="primary"><mat-icon style="font-size: 22px !important;">content_copy</mat-icon></button>
<div style="display: inline-block;">
<ng-container i18n="Label for lines select in logger view">Lines:</ng-container>&nbsp;
<mat-form-field style="width: 75px;">
<mat-select (selectionChange)="getLogs()" [(ngModel)]="requested_lines">
@@ -17,5 +30,8 @@
</mat-select>
</mat-form-field>
</div>
<span class="spacer"></span>
<button style="float: right; margin-top: 12px;" (click)="clearLogs()" mat-stroked-button color="warn"><ng-container i18n="Clear logs button">Clear logs</ng-container></button>
</div>
</div>

View File

@@ -0,0 +1 @@
.spacer {flex: 1 1 auto;}

View File

@@ -1,5 +1,7 @@
import { Component, OnInit, AfterViewInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { PostsService } from '../../posts.services';
import { MatDialog } from '@angular/material/dialog';
import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialog.component';
@Component({
selector: 'app-logs-viewer',
@@ -8,10 +10,11 @@ import { PostsService } from '../../posts.services';
})
export class LogsViewerComponent implements OnInit {
logs: string = null;
logs: any = null;
logs_text: string = null;
requested_lines = 50;
logs_loading = false;
constructor(private postsService: PostsService) { }
constructor(private postsService: PostsService, private dialog: MatDialog) { }
ngOnInit(): void {
this.getLogs();
@@ -21,8 +24,24 @@ export class LogsViewerComponent implements OnInit {
if (!this.logs) { this.logs_loading = true; } // only show loading spinner at the first load
this.postsService.getLogs(this.requested_lines !== 0 ? this.requested_lines : null).subscribe(res => {
this.logs_loading = false;
if (res['logs']) {
this.logs = res['logs'];
if (res['logs'] !== null || res['logs'] !== undefined) {
this.logs_text = res['logs'];
this.logs = [];
const logs_arr = res['logs'].split('\n');
logs_arr.forEach(log_line => {
let color = 'inherit'
if (log_line.includes('ERROR')) {
color = 'red';
} else if (log_line.includes('WARN')) {
color = 'yellow';
} else if (log_line.includes('VERBOSE')) {
color = 'gray';
}
this.logs.push({
text: log_line,
color: color
})
});
} else {
this.postsService.openSnackBar('Failed to retrieve logs!');
}
@@ -37,4 +56,30 @@ export class LogsViewerComponent implements OnInit {
this.postsService.openSnackBar('Logs copied to clipboard!');
}
clearLogs() {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: {
dialogTitle: 'Clear logs',
dialogText: 'Would you like to clear your logs? This will delete all your current logs, permanently.',
submitText: 'Clear'
}
});
dialogRef.afterClosed().subscribe(confirmed => {
if (confirmed) {
this.postsService.clearAllLogs().subscribe(res => {
if (res['success']) {
this.logs = [];
this.logs_text = '';
this.getLogs();
this.postsService.openSnackBar('Logs successfully cleared!');
} else {
this.postsService.openSnackBar('Failed to clear logs!');
}
}, err => {
this.postsService.openSnackBar('Failed to clear logs!');
});
}
});
}
}

View File

@@ -238,6 +238,10 @@ export class PostsService implements CanActivate {
return this.http.post(this.path + 'logs', {lines: lines}, this.httpOptions);
}
clearAllLogs() {
return this.http.post(this.path + 'clearAllLogs', {}, this.httpOptions);
}
isPinSet() {
return this.http.post(this.path + 'isPinSet', {}, this.httpOptions);
}