Added polling in disk logs #89

Merged
maranda merged 2 commits from develop into main 2026-05-06 23:43:07 +02:00
5 changed files with 95 additions and 6 deletions

View File

@ -95,6 +95,34 @@
word-break: break-all;
}
.live-badge {
display: inline-flex;
align-items: center;
gap: 5px;
margin-left: 10px;
padding: 2px 8px;
background-color: #d32f2f;
color: #fff;
border-radius: 4px;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.05em;
vertical-align: middle;
}
.live-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background-color: #fff;
animation: blink 1s step-start infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.modal-actions {
padding: 16px 24px;
border-top: 1px solid #e0e0e0;

View File

@ -3,9 +3,18 @@
<h2 mat-dialog-title>
<mat-icon>storage</mat-icon>
{{ 'diskLogs' | translate }} - {{ data.client.name }}
<span *ngIf="isLive" class="live-badge">
<span class="live-dot"></span>LIVE
</span>
</h2>
<div class="header-actions">
<button mat-icon-button (click)="loadLogs()" [matTooltip]="'buttonRefresh' | translate" [disabled]="loading">
<button mat-icon-button
(click)="toggleLive()"
[matTooltip]="(isLive ? 'diskLogsStopLive' : 'diskLogsStartLive') | translate"
[color]="isLive ? 'warn' : ''">
<mat-icon>{{ isLive ? 'pause_circle' : 'play_circle' }}</mat-icon>
</button>
<button mat-icon-button (click)="loadLogs()" [matTooltip]="'buttonRefresh' | translate" [disabled]="loading || isLive">
<mat-icon>refresh</mat-icon>
</button>
</div>
@ -27,7 +36,7 @@
<span>{{ error }}</span>
</div>
<pre *ngIf="!loading && !error" class="log-content">{{ content || ('noLogsAvailable' | translate) }}</pre>
<pre *ngIf="!loading && !error" class="log-content" #logContent>{{ content || ('noLogsAvailable' | translate) }}</pre>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { Component, Inject, OnInit, OnDestroy } from '@angular/core';
import { Component, Inject, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { HttpClient } from '@angular/common/http';
import { ConfigService } from '@services/config.service';
@ -12,7 +12,14 @@ export class ClientDiskLogsModalComponent implements OnInit, OnDestroy {
content: string = '';
loading: boolean = false;
error: string | null = null;
isLive: boolean = false;
private apiUrl: string;
private pollInterval: ReturnType<typeof setInterval> | null = null;
private readonly POLL_INTERVAL_MS = 3000;
private readonly LIVE_LINES = 200;
@ViewChild('logContent') logContent!: ElementRef<HTMLPreElement>;
constructor(
public dialogRef: MatDialogRef<ClientDiskLogsModalComponent>,
@ -27,26 +34,67 @@ export class ClientDiskLogsModalComponent implements OnInit, OnDestroy {
this.loadLogs();
}
ngOnDestroy(): void {}
ngOnDestroy(): void {
this.stopLive();
}
loadLogs(): void {
loadLogs(lines: number = 0): void {
this.loading = true;
this.error = null;
this.http.get<{ content: string }>(`${this.apiUrl}/clients/${this.data.client.uuid}/disk-logs`).subscribe({
const url = lines > 0
? `${this.apiUrl}/clients/${this.data.client.uuid}/disk-logs?lines=${lines}`
: `${this.apiUrl}/clients/${this.data.client.uuid}/disk-logs`;
this.http.get<{ content: string }>(url).subscribe({
next: (response) => {
this.content = response.content || '';
this.loading = false;
if (this.isLive) {
this.scrollToBottom();
}
},
error: (err) => {
this.error = err.status === 404
? 'No se encontró el archivo de logs para este cliente.'
: 'Error al obtener los logs de disco.';
this.loading = false;
this.stopLive();
}
});
}
toggleLive(): void {
if (this.isLive) {
this.stopLive();
} else {
this.startLive();
}
}
private startLive(): void {
this.isLive = true;
this.loadLogs(this.LIVE_LINES);
this.pollInterval = setInterval(() => this.loadLogs(this.LIVE_LINES), this.POLL_INTERVAL_MS);
}
private stopLive(): void {
this.isLive = false;
if (this.pollInterval !== null) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
}
private scrollToBottom(): void {
setTimeout(() => {
const el = this.logContent?.nativeElement;
if (el) {
el.scrollTop = el.scrollHeight;
}
}, 50);
}
closeModal(): void {
this.dialogRef.close();
}

View File

@ -232,6 +232,8 @@
"clientListPartition": "List of clients where partitioning will be performed",
"clientLogs": "Client Logs",
"diskLogs": "Disk Logs",
"diskLogsStartLive": "Enable real-time mode",
"diskLogsStopLive": "Stop real-time mode",
"clientMacOS": "MacOS client",
"clientOff": "Client off",
"clientOgLive": "OGLive client",

View File

@ -232,6 +232,8 @@
"clientListPartition": "Listado de clientes donde se realizará el particionado",
"clientLogs": "Logs de cliente",
"diskLogs": "Logs de disco",
"diskLogsStartLive": "Activar modo en tiempo real",
"diskLogsStopLive": "Detener modo en tiempo real",
"clientMacOS": "Cliente MacOS",
"clientOff": "Cliente apagado",
"clientOgLive": "Cliente OGLive",