javascript - Hur loggar man alla möjliga konsolfel som uppstår i en Angular-applikation på en klients sida?
För närvarande består min Angular-applikation av ett betydande antal undermoduler och komponenter. Angular-applikationer är ganska känsliga för HTML-renderingsfel. Till exempel, om vi får ett NULL-objekt från ett API och försöker få åtkomst till dess egenskap, bryts renderingen av applikationen delvis.
Det är ganska svårt att hantera och testa sådana fall med tanke på att applikationen ständigt växer. Vet du om det är möjligt att skapa ett skript som kan logga alla fel som visas i en konsol?
Här är vad jag har fått:
1) En hantering av http-förfrågningsfel för att logga brutna förfrågningar från ett API:
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
2) Klient-sida logger men av någon anledning loggar den bara fel från crawlers :)
<script>
window.onerror = function(m,u,l,c) {
if (window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
var data = "msg="+encodeURIComponent(m)
+"&url="+encodeURIComponent(u)
+"&line="+l
+"&col="+c
+"&href="+encodeURIComponent(window.location.href);
xhr.open("GET", "logger.php?"+data, true);
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
xhr.send();
}
};
</script>
Det skulle vara mycket användbart att få ett skript som enkelt kan logga alla fel från konsolen och
Svar #1
Om du vill fånga och logga alla fel (inte bara de av HTTP-natur) i din Angular-applikation skulle jag föreslå det sätt som Sentry och andra buggspårare använder.
Angular har en ErrorHandler-klass som tillhandahåller en krok för centraliserad undantagshantering.
Så din uppgift skulle vara att skapa din egen felhanterare. Jag brukar göra det så här:
import {ErrorHandler, Injectable} from '@angular/core';
import {LoggerService} from './some/logger.service';
@Injectable()
export class CustomErrorHandlerService extends ErrorHandler {
constructor(private logger: LoggerService) {
super();
}
handleError(error) {
// Here you can provide whatever logging you want
this.logger.logError(error);
super.handleError(error);
}
}
Nästa steg är att använda din felhanterare genom beroendeinjektion. Lägg bara till den i din AppModule på detta sätt:
import {CustomErrorHandlerService} from '~/app/_services/custom-error-handler.service';
@NgModule({
imports: [...],
declarations: [...],
providers: [
...
{provide: ErrorHandler, useClass: CustomErrorHandlerService},
],
bootstrap: [AppComponent]
})
Det är allt. Den viktigaste egenskapen hos en sådan implementering är att denna tjänst hanterar alla möjliga fel som uppstår i din applikation (precis som du frågar i din fråga), inklusive HTTP-fel och andra.
Vänligen lämna dina kommentarer om du har några ytterligare frågor.
Svar #2
När jag började arbeta med Angular tyckte jag att den här artikeln var hjälpsam: Angular: Lägga till loggning i Angular-applikationer.
Om du följer vägledningen får du en LoggingService som du kan konfigurera för att logga fel till en av tre (eller alla) platser:
- Konsolen
- Lokal lagring
- En databas (via POST-förfrågan)
Vad artikeln utelämnar är hur man fångar felen automatiskt. Du kan göra detta med en HttpInterceptor. Här är ett exempel från en applikation jag arbetar med:
http-förfrågan-avlyssnare.ts
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
import { LogService } from '../services/log.service';
@Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {
constructor (private logService: LogService, private router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const newReq = req.clone({
headers: req.headers.set('Content-Type', 'application/json'),
withCredentials: true
});
return next.handle(newReq)
.pipe(
retry(1),
catchError((error: HttpErrorResponse) => {
let displayError = '';
let serverError = '';
let clientError = '';
if (error.error instanceof ErrorEvent) {
// client-side error
clientError = `Error: ${error.error.message}`;
this.logService.error(clientError);
} else {
// server-side error
displayError = error.error;
serverError = `Error Code: ${error.status}\n${error.message}\n${error.error}`;
if (error.status === 401) {
this.logService.error(serverError);
this.router.navigate(['/unauthorized', { message: error.error}]);
}
if (error.status >= 500) {
this.logService.error(serverError);
this.router.navigate(['/error']);
}
}
return throwError(displayError);
})
);
}
}
HttpInterceptors är mycket kraftfulla. Den här gör följande:
- Lägger till en Content-Type i headern på varje begäran och sätter den till application/json
- Skickar varje förfrågan med referenser
- Försöker varje begäran en gång till innan felhantering
Hantera fel
- När användaren är obehörig, omdirigeras det till en anpassad 401-felsida
- När servern kastar ett fel (500+), omdirigeras det till en anpassad serversidesfel-sida
- I alla fall loggar det felet med hjälp av den ovan nämnda loggtjänsten
- Det kastar också felet tillbaka till anroparen, så om ditt API returnerar användarvänliga felmeddelanden kan du fånga dem i din controller och visa dem för dina användare
För att registrera en HttpInterceptor i din applikation behöver du en modul:
http-interceptor.module.ts
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpRequestInterceptor } from './http-request-interceptor';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS,
useClass: HttpRequestInterceptor,
multi: true
}
]
})
export class HttpInterceptorModule { }
Därefter registrerar du denna modul i din app.module.ts-fil och inkluderar den i imports-arrayen.
