Logging Enhancements (#1521)
* Recreated Kavita Logging with Serilog instead of Default. This needs to be move out of the appsettings now, to allow auto updater to patch. * Refactored the code to be completely configured via Code rather than appsettings.json. This is a required step for Auto Updating. * Added in the ability to send logs directly to the UI only for users on the log route. Stopping implementation as Alerts page will handle the rest of the implementation. * Fixed up the backup service to not rely on Config from appsettings.json * Tweaked the Logging levels available * Moved everything over to File-scoped namespaces * Moved everything over to File-scoped namespaces * Code cleanup, removed an old migration and changed so debug logging doesn't print sensitive db data * Removed dead code
This commit is contained in:
parent
9f715cc35f
commit
d1a14f7e68
212 changed files with 16599 additions and 16834 deletions
|
@ -142,6 +142,12 @@ export class MessageHubService {
|
|||
this.onlineUsersSource.next(usernames);
|
||||
});
|
||||
|
||||
this.hubConnection.on("LogObject", resp => {
|
||||
console.log(resp);
|
||||
});
|
||||
this.hubConnection.on("LogString", resp => {
|
||||
console.log(resp);
|
||||
});
|
||||
|
||||
this.hubConnection.on(EVENTS.ScanSeries, resp => {
|
||||
this.messagesSource.next({
|
||||
|
|
|
@ -23,6 +23,8 @@ import { SidenavModule } from '../sidenav/sidenav.module';
|
|||
import { ManageMediaSettingsComponent } from './manage-media-settings/manage-media-settings.component';
|
||||
import { ManageEmailSettingsComponent } from './manage-email-settings/manage-email-settings.component';
|
||||
import { ManageTasksSettingsComponent } from './manage-tasks-settings/manage-tasks-settings.component';
|
||||
import { ManageLogsComponent } from './manage-logs/manage-logs.component';
|
||||
import { VirtualScrollerModule } from '@iharbeck/ngx-virtual-scroller';
|
||||
|
||||
|
||||
|
||||
|
@ -45,6 +47,7 @@ import { ManageTasksSettingsComponent } from './manage-tasks-settings/manage-tas
|
|||
ManageMediaSettingsComponent,
|
||||
ManageEmailSettingsComponent,
|
||||
ManageTasksSettingsComponent,
|
||||
ManageLogsComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -59,6 +62,7 @@ import { ManageTasksSettingsComponent } from './manage-tasks-settings/manage-tas
|
|||
PipeModule,
|
||||
SidenavModule,
|
||||
UserSettingsModule, // API-key componet
|
||||
VirtualScrollerModule
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
<ng-container *ngIf="tab.fragment === TabID.Libraries">
|
||||
<app-manage-library></app-manage-library>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === TabID.Logs">
|
||||
<app-manage-logs></app-manage-logs>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === TabID.System">
|
||||
<app-manage-system></app-manage-system>
|
||||
</ng-container>
|
||||
|
|
|
@ -13,7 +13,8 @@ enum TabID {
|
|||
Libraries = 'libraries',
|
||||
System = 'system',
|
||||
Plugins = 'plugins',
|
||||
Tasks = 'tasks'
|
||||
Tasks = 'tasks',
|
||||
Logs = 'logs'
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -27,6 +28,7 @@ export class DashboardComponent implements OnInit {
|
|||
{title: 'General', fragment: TabID.General},
|
||||
{title: 'Users', fragment: TabID.Users},
|
||||
{title: 'Libraries', fragment: TabID.Libraries},
|
||||
//{title: 'Logs', fragment: TabID.Logs},
|
||||
{title: 'Media', fragment: TabID.Media},
|
||||
{title: 'Email', fragment: TabID.Email},
|
||||
//{title: 'Plugins', fragment: TabID.Plugins},
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<ng-container *ngIf="logs$ | async as items">
|
||||
<virtual-scroller #scroll [items]="items" [bufferAmount]="1">
|
||||
<div class="grid row g-0" #container>
|
||||
<div class="card col-auto mt-2 mb-2" *ngFor="let item of scroll.viewPortItems; index as i">
|
||||
{{item.timestamp | date}} [{{item.level}}] {{item.message}}
|
||||
</div>
|
||||
</div>
|
||||
</virtual-scroller>
|
||||
</ng-container>
|
66
UI/Web/src/app/admin/manage-logs/manage-logs.component.ts
Normal file
66
UI/Web/src/app/admin/manage-logs/manage-logs.component.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
|
||||
import { BehaviorSubject, ReplaySubject, Subject, take } from 'rxjs';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
interface LogMessage {
|
||||
timestamp: string;
|
||||
level: 'Information' | 'Debug' | 'Warning' | 'Error';
|
||||
message: string;
|
||||
exception: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-logs',
|
||||
templateUrl: './manage-logs.component.html',
|
||||
styleUrls: ['./manage-logs.component.scss']
|
||||
})
|
||||
export class ManageLogsComponent implements OnInit, OnDestroy {
|
||||
|
||||
hubUrl = environment.hubUrl;
|
||||
private hubConnection!: HubConnection;
|
||||
|
||||
logsSource = new BehaviorSubject<LogMessage[]>([]);
|
||||
public logs$ = this.logsSource.asObservable();
|
||||
|
||||
constructor(private accountService: AccountService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.hubConnection = new HubConnectionBuilder()
|
||||
.withUrl(this.hubUrl + 'logs', {
|
||||
accessTokenFactory: () => user.token
|
||||
})
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
console.log('Starting log connection');
|
||||
|
||||
this.hubConnection
|
||||
.start()
|
||||
.catch(err => console.error(err));
|
||||
|
||||
this.hubConnection.on('SendLogAsObject', resp => {
|
||||
const payload = resp.arguments[0] as LogMessage;
|
||||
const logMessage = {timestamp: payload.timestamp, level: payload.level, message: payload.message, exception: payload.exception};
|
||||
// TODO: It might be better to just have a queue to show this
|
||||
const values = this.logsSource.getValue();
|
||||
values.push(logMessage);
|
||||
this.logsSource.next(values);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
// unsubscrbe from signalr connection
|
||||
if (this.hubConnection) {
|
||||
this.hubConnection.stop().catch(err => console.error(err));
|
||||
console.log('Stoping log connection');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<div class="container-fluid">
|
||||
<form [formGroup]="settingsForm" *ngIf="serverSettings !== undefined">
|
||||
<p class="text-warning pt-2">Port, Logging Level, and Swagger require a manual restart of Kavita to take effect.</p>
|
||||
<p class="text-warning pt-2">Port and Swagger require a manual restart of Kavita to take effect.</p>
|
||||
<div class="mb-3">
|
||||
<label for="settings-cachedir" class="form-label">Cache Directory</label> <i class="fa fa-info-circle" placement="right" [ngbTooltip]="cacheDirectoryTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #cacheDirectoryTooltip>Where the server place temporary files when reading. This will be cleaned up on a regular basis.</ng-template>
|
||||
|
@ -48,8 +48,8 @@
|
|||
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<label for="logging-level-port" class="form-label">Logging Level</label> <i class="fa fa-info-circle" placement="right" [ngbTooltip]="loggingLevelTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #loggingLevelTooltip>Use debug to help identify issues. Debug can eat up a lot of disk space. Requires restart to take effect.</ng-template>
|
||||
<span class="visually-hidden" id="logging-level-port-help">Port the server listens on. Requires restart to take effect.</span>
|
||||
<ng-template #loggingLevelTooltip>Use debug to help identify issues. Debug can eat up a lot of disk space.</ng-template>
|
||||
<span class="visually-hidden" id="logging-level-port-help">Port the server listens on.</span>
|
||||
<select id="logging-level-port" aria-describedby="logging-level-port-help" class="form-select" aria-describedby="settings-tasks-scan-help" formControlName="loggingLevel">
|
||||
<option *ngFor="let level of logLevels" [value]="level">{{level | titlecase}}</option>
|
||||
</select>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue