Should I send JS exceptions manually to Google Analytics? - javascript

I am new to Google Analytics. I have an Angular 4 application. I would like to know when an uncaught JavaScript exception had been thrown. I've set up a Google Analytics via Google Tag Manager. It works perfectly for history change event (navigation between Angular routes). But it doesn't fire JS errors.
And here is what I see in debug pane:
You can see an exception in console but JS Error tag is not fired. What am I doing wrong?

Ok, here is what I found. You shouldn't expect that exceptions will be fired automatically. You should implement ErrorHandler and manually throw event from there.
import {ErrorHandler, Injector} from '#angular/core';
import {Angulartics2GoogleTagManager} from 'angulartics2/gtm';
export class MyErrorHandler implements ErrorHandler {
private analytics: Angulartics2GoogleTagManager;
constructor(private injector: Injector) {
}
handleError(error: Error) {
console.error(error);
if (!this.analytics) {
this.analytics = this.injector.get(Angulartics2GoogleTagManager);
}
this.analytics.eventTrack(JSON.stringify(error.message), {
event: 'jsError',
label: JSON.stringify(error.stack).replace('\n', ' ')
});
}
}
app.module.ts:
{provide: ErrorHandler, useClass: MyErrorHandler, deps: [Injector]},

Related

nestjs exception filter is not invoked

I am trying to use an exception filter in my NestJS app. I followed the instructions found here to setup my global ExceptionFilter, which looks like this:
#Catch()
export class DemoExceptionFilter implements ExceptionFilter
{
private readonly logger: Logger;
constructor()
{
this.logger = new Logger(DemoExceptionFilter .name);
}
public catch(exception: unknown, host: ArgumentsHost): void
{
this.logger.log(exception);
}
}
In my AppModule I have registered the DemoExceptionFilter this way:
#Module({
...
providers: [
...
{
provide: APP_FILTER,
useClass: DemoExceptionFilter
}
...
]
})
When I throw an exception somewhere in my code that exception gets logged by NestJS in the console but my DemoExceptionFilter is not invoked.
I also tried
app.useGlobalFilters(new DemoExceptionFilter());
in main.ts, which also does not work.
What am I missing?
In the documentation, it says where global exception filters will be applied:
Global-scoped filters are used across the whole application, for every controller and every route handler.
They are not used for the application startup. So if you want to test your exception filter, throw an error in the route handler method of a controller and call the route.

Angular - How to implement Exception Handling on component level

While working inside Angular (Angular 4, 5), if a component raises Error (TypeError or null or undefined error or so), whole application breaks onward.
How can we deal with this, to catch errors on component level and possibly show a fallback UI, like React16 does using Error Boundaries.
I would approach it by handling the error at Component level and have a service that listens to any errors happening at Component or Service level.
Ex:
Throw the error from the service
catch the error in component
Handle the error, process it and send the Error event with details to ErrorService.
You can have a app level component "errorBannerComponent" which takes input from ErrorService and paint your UI.
As soon as the error is received in ErrorService, The errorBannerComponent should display the error on screen.
Hope it helps.
Also By default, Angular comes with its own ErrorHandler that
intercepts all the Errors that happen in our app and logs them to the
console, preventing the app from crashing. We can modify this default behavior by creating a new class that implements the ErrorHandler:
You can find more details and example here:
As the proposed solutions are rather dull. I tried to recreate it myself. The easiest solution would be to provide a module scoped custom ErrorHandler class.
Thanks to this, you could even create a multiple different ErrorBoundaries.
My proposed solution can be seen here: https://stackblitz.com/edit/angular-ivy-brb143?file=src/app/widget/widget.module.ts
What is really important for this solution to work (atleast it didn't work otherwise for me). Was to provide the custom error handler as a part of a module rather than a component directly.
The important bits from the solutions:
module:
/**
* This is really imporant as this allows us to provide a module scoped ErrorHandler
*/
#NgModule({
imports: [CommonModule],
declarations: [WidgetComponent],
providers: [{ provide: ErrorHandler, useClass: WidgetErrorHandler }],
exports: [WidgetComponent],
})
export class WidgetModule {}
component where we can throw, and catch error
#Component({
selector: 'app-widget',
templateUrl: './widget.component.html',
styleUrls: ['./widget.component.css'],
})
export class WidgetComponent implements OnInit {
constructor(#Inject(ErrorHandler) public widgetError: WidgetErrorHandler) {}
ngOnInit() {
this.widgetError.isError$.subscribe((error) =>
console.log('component can act on error: ', error)
);
}
public handleThrowErrorClick(): void {
throw Error('Button clicked');
}
}
and the handler iself
#Injectable()
export class WidgetErrorHandler implements ErrorHandler {
public isError$: Subject<Error | any> = new Subject();
handleError(error) {
console.log('Intercepted error', error);
this.isError$.next(error);
}
}

Ionic 3 app with cordova-plugin-chrome-apps-sockets-tcp. Livereload trouble

I am using cordova-plugin-chrome-apps-sockets-tcp in my android application. In this app I am trying to connect to TCP/IP server. Example of my code:
export class HomePage implements OnInit {
constructor(public navCtrl: NavController) {}
ngOnInit() {
(<any>window).chrome.sockets.tcp.create({}, createInfo => {
let socketTcpId = createInfo.socketId;
(<any>window).chrome.sockets.tcp.connect(socketTcpId, "127.0.0.1", 12345, result => {
console.log("Connected to server");
});
});
} ...
I uploaded this code to android emulator V 7.1.1 using command:
ionic cordova run android --prod --emulator --livereload --consolelogs
During the first run my application succesfully connects to TCP/IP server. But if I will press CTRL-S and livereload process will starts, after reloading I see error and warn messages:
console.warn: Ionic Native: deviceready did not fire within 5000ms. This can happen when plugins are in an inconsistent state. Try removing plugins from plugins/ and reinstalling them.
Runtime Error
Uncaught (in promise): TypeError: Cannot read property 'sockets' of undefined at HomePage.ngOnInit
I tried to fix it with reinstalling of plugin, removed and added android platform, I tried to use window.plugins or window.cordova.plugins, but them are undefined and solved my problem. Can you help me, please?
After declaration of chrome variable, code:
import {
Component,
OnInit
} from '#angular/core';
import {
NavController
} from 'ionic-angular';
declare var chrome;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage implements OnInit {
constructor(public navCtrl: NavController) {}
ngOnInit() {
chrome.sockets.tcp.create({}, createInfo => {
let socketTcpId = createInfo.socketId;
chrome.sockets.tcp.connect(socketTcpId, "127.0.0.1", 12345, result => {
console.log("Connected to server");
});
});
}...
First run - all working. After livereload I see message: ReferenceError: chrome is not defined. After that if I will replace declaration of chrome:
} from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
declare var chrome;
export class HomePage implements OnInit {
it will call the Runtime error "'Home Page' declared by the module...", and after that if I will put declaration back and press CTRL-S - all will work again and I will connect to server, where am I wrong?
You must call it with (<any>window)
Example:
(<any>window).chrome.sockets.tcpServer.create({}, function(createInfo) {
alert('Created')
this.listenAndAccept(createInfo.socketId);
});

Angular 2. Error: Loading chunk failed

Using angular 2 with lazy loaded modules, I can receive(for example) 401 HTTP code from server
bootstrap 0b40fee…:101 GET http://localhost:8082/2.chunk.js
Error: Loading chunk 2 failed.
at HTMLScriptElement.onScriptComplete (bootstrap 0b40fee…:91)
at HTMLScriptElement.wrapFn (zone.js:1032)
at ZoneDelegate.invokeTask (zone.js:414)
at Object.onInvokeTask (core.es5.js:4119)
at ZoneDelegate.invokeTask (zone.js:413)
at Zone.runTask (zone.js:181)
at HTMLScriptElement.ZoneTask.invoke (zone.js:476)
How to handle this error?
Check my answer for details
Workaround to bypass this chunk fails error => Programmatically force app to reload if chunks failed error occurs using global error handler.
import { ErrorHandler } from '#angular/core';
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error: any): void {
const chunkFailedMessage = /Loading chunk [\d]+ failed/;
if (chunkFailedMessage.test(err.message)) {
window.location.reload();
}
}
}
Provide it in our root module to change default behavior in our app, so instead of using default ErrorHandler class we are using our custom GlobalErrorHandler class.
#NgModule({
providers: [{provide: ErrorHandler, useClass: GlobalErrorHandler}]
})
I was having the same problem so I investigated. I found the solution. This happened to me when I redeployed to another server and the chunk had a [hash].
You can catch the error either in a catch all like this:
ngOnInit() {
if (!this.previousRouterErrorHandler) {
this.previousRouterErrorHandler = this.router.errorHandler;
let that = this;
this.router.errorHandler = function (err: any) {
// Handle here. err.message == "Loading chunk chunk-name failed."
return that.previousRouterErrorHandler.apply(that.previousRouterErrorHandler, arguments);
};
}
}
Or directly at the link which navigated
click() {
this.router.navigate(['/lazy-route'])
.catch(err => {
// Handle here
});
}
Here is my solution for this. I inject this service as a singleton in my app / core module.
It waits for instances of NavigationError from the router, checks if they are ChunkLoadError's and then does a redirect to the place the user wanted to go to anyway.
// Angular
import { Injectable, OnDestroy } from '#angular/core';
import { Router, NavigationError } from '#angular/router';
// Rxjs
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
#Injectable()
export class ChunkErrorHandler implements OnDestroy {
private subscription: Subscription;
constructor(router: Router) {
this.subscription = router.events
.pipe(filter(event => event instanceof NavigationError))
.subscribe(event => {
this.handleRouterErrors(event as NavigationError);
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
private handleRouterErrors(event: NavigationError) {
if (event.error.name === 'ChunkLoadError') {
window.location.href = `${window.location.origin}${event.url}`;
}
}
}
It happen when when deploy new code.The manifest.js which holds the files and hashes doesn't update without refreshing and when it loads a chunk it obviously uses the old hash from manifest.js.
So while catching error we can do force reload with given url :-
click() {
this.router.navigate(['/lazy-route'])
.catch(err => {
// Handle here
// reload with given route
// window.location.pathname('/lazy-route');
// OR
// reset existing route(containing query params) with given route and force reload
window.history.pushState({}, document.title, '/lazy-route' );
window.location.reload();
});
}
chunk related errors can be raised by any environment or routing related issues making them hard to debunk.
In my case, the amount of data moving in my PWA was too much to handle by the angular router. It was flooding the headers of the js chunks getters and therefore raising bad_request errors.
I suggest you to check out those network calls (getters of chunks.js like http://localhost:xxxx/158.js) for anything unusual in headers and refactor sketchy stuff in your current dev environment, since it's a real black hole time to investigate the source of the error by yourself.
Hope that'll help
check out Catch Storage, i guess service worker save some thing in catch storage
console.log(Object.entries(error));
this help me to understand what's inside the error is
rejection,
promise,
zone,
task
and below is my solution:
handleError(error) {
switch (error?.rejection?.name) {
case 'ChunkLoadError':
window.location.href = window.location.href;
break;
default:
break;
}
}
In my case, I was putting my files in an S3 bucket. I kept getting this error because it was calling the wrong filenames all together and returning an html error response.
At some point I let the IT team know what was happening. They were like, let's invalidate the cache on CloudFront... What?! Yeah! Let's do that...
Moral of the story, if you've been searching the web for answers to this error and can't find any, check with the IT team or any place that the index.html file might be getting cached.
this probably means unhandled exception. you have to handle error responses (4xx, 5xx status codes) from server in whatever way you want: show error message somewhere, redirect to some page, do anything but not leave it unhandled.
for example:
return this.http.get(requestDetails)
.map(res => res.json())
.catch(err => {
console.log('server error:', err)
Observable.throw(err);
});

Angular 2 Webworkers Http Uncaught (in promise): not implemented

I've been stuck on an error that I'm not completely sure how to solve.
My application is made in Angular2 and runs completely in a webworker largely based on this tutorial http://www.syntaxsuccess.com/viewarticle/web-workers-in-angular-2.0
My first feature was an implementation of socket.io which is working perfectly(also with observables etc..) but now I want to use the Http service of Angular2 and I get the following error:
My code of the service is like this and the error arrises when I call validateAccessToken (I have to add the .js on my imports otherwise I get a 404 on the files within the webworker):
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from "#angular/http";
import { environment } from "../../../environments/environment.js";
import { Observable } from "rxjs/Observable.js";
import 'rxjs/add/operator/toPromise.js';
import 'rxjs/add/operator/map.js';
#Injectable()
export class AuthService {
headers: Headers;
options: RequestOptions;
url: string;
constructor(private http:Http) {
this.url = environment.authServerUrl;
}
validateAccessToken(token) {
return this.http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
};
extractData(response: Response) {...}
handleError(error: any) {...}
}
I know the question is quite vague but with the information I get from the error it's not really clear what's going wrong for me.
The CookieXSRFStrategy is default enabled by Angular2 and used by http.
The webworker does not have DOM access to get the cookie to insert in the http headers. And thus throws the error Uncaught not implemented.
You should implement your own CookieXSRFStrategy strategy which at least does not throw this error ;)

Categories