Commit /logs function dirs. not commited before, because of .gitignore.

This commit is contained in:
Orlando M Guerreiro 2025-05-23 09:59:37 +01:00
parent c155508d25
commit 7d1d5a213c
10 changed files with 477 additions and 0 deletions

View file

@ -0,0 +1,18 @@
export type Level = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'OFF';
export interface Logger {
configuredLevel: Level | null;
effectiveLevel: Level;
}
export interface LoggersResponse {
levels: Level[];
loggers: { [key: string]: Logger };
}
export class Log {
constructor(
public name: string,
public level: Level,
) {}
}

View file

@ -0,0 +1,82 @@
@defer (when loggers() && !isLoading()) {
<div class="table-responsive">
<h2 id="logs-page-heading" data-cy="logsPageHeading" jhiTranslate="logs.title">Logs</h2>
<p jhiTranslate="logs.nbloggers" [translateValues]="{ total: loggers()?.length }">Existem {{ loggers()?.length }} loggers.</p>
<span jhiTranslate="logs.filter">Filtro</span>
<input type="text" [ngModel]="filter()" (ngModelChange)="filter.set($event)" class="form-control" />
<table class="table table-sm table-striped table-bordered" aria-describedby="logs-page-heading">
<thead>
<tr jhiSort [sortState]="sortState" (sortChange)="sortState.set($event)">
<th jhiSortBy="name" scope="col"><span jhiTranslate="logs.table.name">Nome</span> <fa-icon icon="sort"></fa-icon></th>
<th jhiSortBy="level" scope="col"><span jhiTranslate="logs.table.level">Nível</span> <fa-icon icon="sort"></fa-icon></th>
</tr>
</thead>
<tbody>
@for (logger of filteredAndOrderedLoggers(); track $index) {
<tr>
<td>
<small>{{ logger.name | slice: 0 : 140 }}</small>
</td>
<td>
<button
(click)="changeLevel(logger.name, 'TRACE')"
[ngClass]="logger.level === 'TRACE' ? 'btn-primary' : 'btn-light'"
class="btn btn-sm"
>
TRACE
</button>
<button
(click)="changeLevel(logger.name, 'DEBUG')"
[ngClass]="logger.level === 'DEBUG' ? 'btn-success' : 'btn-light'"
class="btn btn-sm"
>
DEBUG
</button>
<button
(click)="changeLevel(logger.name, 'INFO')"
[ngClass]="logger.level === 'INFO' ? 'btn-info' : 'btn-light'"
class="btn btn-sm"
>
INFO
</button>
<button
(click)="changeLevel(logger.name, 'WARN')"
[ngClass]="logger.level === 'WARN' ? 'btn-warning' : 'btn-light'"
class="btn btn-sm"
>
WARN
</button>
<button
(click)="changeLevel(logger.name, 'ERROR')"
[ngClass]="logger.level === 'ERROR' ? 'btn-danger' : 'btn-light'"
class="btn btn-sm"
>
ERROR
</button>
<button
(click)="changeLevel(logger.name, 'OFF')"
[ngClass]="logger.level === 'OFF' ? 'btn-secondary' : 'btn-light'"
class="btn btn-sm"
>
OFF
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
} @loading {
<div class="d-flex justify-content-center me-3">
<div class="spinner-border"></div>
</div>
}

View file

@ -0,0 +1,82 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { of } from 'rxjs';
import LogsComponent from './logs.component';
import { LogsService } from './logs.service';
import { Log, LoggersResponse } from './log.model';
describe('LogsComponent', () => {
let comp: LogsComponent;
let fixture: ComponentFixture<LogsComponent>;
let service: LogsService;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, LogsComponent],
providers: [LogsService],
})
.overrideTemplate(LogsComponent, '')
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LogsComponent);
comp = fixture.componentInstance;
service = TestBed.inject(LogsService);
});
describe('OnInit', () => {
it('should set all default values correctly', () => {
expect(comp.filter()).toBe('');
expect(comp.sortState().predicate).toBe('name');
expect(comp.sortState().order).toBe('asc');
});
it('Should call load all on init', () => {
// GIVEN
const log = new Log('main', 'WARN');
jest.spyOn(service, 'findAll').mockReturnValue(
of({
loggers: {
main: {
effectiveLevel: 'WARN',
},
},
} as unknown as LoggersResponse),
);
// WHEN
comp.ngOnInit();
// THEN
expect(service.findAll).toHaveBeenCalled();
expect(comp.loggers()?.[0]).toEqual(expect.objectContaining(log));
});
});
describe('change log level', () => {
it('should change log level correctly', () => {
// GIVEN
const log = new Log('main', 'ERROR');
jest.spyOn(service, 'changeLevel').mockReturnValue(of({}));
jest.spyOn(service, 'findAll').mockReturnValue(
of({
loggers: {
main: {
effectiveLevel: 'ERROR',
},
},
} as unknown as LoggersResponse),
);
// WHEN
comp.changeLevel('main', 'ERROR');
// THEN
expect(service.changeLevel).toHaveBeenCalled();
expect(service.findAll).toHaveBeenCalled();
expect(comp.loggers()?.[0]).toEqual(expect.objectContaining(log));
});
});
});

View file

@ -0,0 +1,61 @@
import { Component, computed, inject, OnInit, signal } from '@angular/core';
import { finalize } from 'rxjs/operators';
import SharedModule from 'app/shared/shared.module';
import { FormsModule } from '@angular/forms';
import { SortDirective, SortByDirective, sortStateSignal, SortService } from 'app/shared/sort';
import { Log, LoggersResponse, Level } from './log.model';
import { LogsService } from './logs.service';
@Component({
standalone: true,
selector: 'jhi-logs',
templateUrl: './logs.component.html',
imports: [SharedModule, FormsModule, SortDirective, SortByDirective],
})
export default class LogsComponent implements OnInit {
loggers = signal<Log[] | undefined>(undefined);
isLoading = signal(false);
filter = signal('');
sortState = sortStateSignal({ predicate: 'name', order: 'asc' });
filteredAndOrderedLoggers = computed<Log[] | undefined>(() => {
let data = this.loggers() ?? [];
const filter = this.filter();
if (filter) {
data = data.filter(logger => logger.name.toLowerCase().includes(filter.toLowerCase()));
}
const { order, predicate } = this.sortState();
if (order && predicate) {
data = data.sort(this.sortService.startSort({ order, predicate }, { predicate: 'name', order: 'asc' }));
}
return data;
});
private logsService = inject(LogsService);
private sortService = inject(SortService);
ngOnInit(): void {
this.findAndExtractLoggers();
}
changeLevel(name: string, level: Level): void {
this.logsService.changeLevel(name, level).subscribe(() => this.findAndExtractLoggers());
}
private findAndExtractLoggers(): void {
this.isLoading.set(true);
this.logsService
.findAll()
.pipe(
finalize(() => {
this.isLoading.set(false);
}),
)
.subscribe({
next: (response: LoggersResponse) =>
this.loggers.set(Object.entries(response.loggers).map(([key, logger]) => new Log(key, logger.effectiveLevel))),
error: () => this.loggers.set([]),
});
}
}

View file

@ -0,0 +1,31 @@
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { LogsService } from './logs.service';
describe('Logs Service', () => {
let service: LogsService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
});
service = TestBed.inject(LogsService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
describe('Service methods', () => {
it('should change log level', () => {
service.changeLevel('main', 'ERROR').subscribe();
const req = httpMock.expectOne({ method: 'POST' });
expect(req.request.body).toEqual({ configuredLevel: 'ERROR' });
});
});
});

View file

@ -0,0 +1,20 @@
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApplicationConfigService } from 'app/core/config/application-config.service';
import { LoggersResponse, Level } from './log.model';
@Injectable({ providedIn: 'root' })
export class LogsService {
private http = inject(HttpClient);
private applicationConfigService = inject(ApplicationConfigService);
changeLevel(name: string, configuredLevel: Level): Observable<{}> {
return this.http.post(this.applicationConfigService.getEndpointFor(`management/loggers/${name}`), { configuredLevel });
}
findAll(): Observable<LoggersResponse> {
return this.http.get<LoggersResponse>(this.applicationConfigService.getEndpointFor('management/loggers'));
}
}