Archivo de la etiqueta: error

Ionic 4+ ErrorHandler, Cómo enviar los errores a un servicio externo

Cuando tu app da error y se ejecuta en dispositivos en los que no tienes acceso a los logs, esta solución te puede solucionar parte del camino para resolver el error.

Primero creamos el servicio que enviara el error a un servidor o servicio externo, en este ejemplo yo hago una petición HTTP a una URL externa, pero puede ser todo lo complejo que quieras:

error-log-service.ts

import { Injectable } from '@angular/core';

import { HttpClient, HttpHeaders} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ErrorLogService {

  constructor(
    private http: HttpClient
  ){ }

  sendError(error) {

    const headers: HttpHeaders = new HttpHeaders({
      'Content-Type': 'application/json'
    });

    const requestOptions = { headers };

    const url = 'https://www.javray.com/log.php';

    const body: any = {};

    body.message = error.message || 'N/A';
    body.stack = error.stack || 'N/A';

    this.http.post(url, body, requestOptions).subscribe();
  }
}

Después definimos la clase que extenderá ErrorHandler y que hará uso de nuestro servicio:

my-error-handler.ts

import { Injectable, ErrorHandler } from '@angular/core';

import { ErrorLogService } from './error-log.service';

@Injectable({
  providedIn: 'root'
})
export class MyErrorHandler extends ErrorHandler {

  constructor(private errorLogService: ErrorLogService) {
    super();
  }

  handleError(error) {
    super.handleError(error);
    this.errorLogService.sendError(error);
  }
}

Y por ultimo añadimos MyErrorHandler como provider en la app:

app.module.ts


import { ErrorHandler, NgModule, Injectable } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';

import { MyErrorHandler } from './my-error-handler';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: ErrorHandler, useClass: MyErrorHandler },
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Una implementación sencilla del servicio que recibe los logs sería la siguiente:

log.php


header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: *');

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
        exit(0);
}

$params = json_decode(file_get_contents('php://input'), true);

$fecha = date('Y-m-d H:m:s');

$log = '[' . $fecha . '] ';
$log .= '"' . $params['message'] . '" ';
$log .= $params['stack'];

$fichero = '/var/www/html/logs/' . date('Ymd') . '.log';

file_put_contents($fichero, $log . "\n", FILE_APPEND);

echo json_encode(array('estado' => 'OK'));