@watchupltd/angular
v1.0.3
Published
Angular integration for incident tracking with dependency injection and error handler.
Readme
@watchupltd/angular
Angular integration for incident tracking with dependency injection and error handler.
Installation
npm install @watchupltd/angularQuick Start
Standalone Components (Angular 14+)
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideIncident } from '@watchupltd/angular';
export const appConfig: ApplicationConfig = {
providers: [
provideIncident({
apiKey: 'your-api-key',
projectId: 'your-project-id',
environment: 'production'
})
]
};// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig);NgModule (Legacy)
// app.module.ts
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IncidentService, IncidentErrorHandler, INCIDENT_CONFIG } from '@watchupltd/angular';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
{
provide: INCIDENT_CONFIG,
useValue: {
apiKey: 'your-api-key',
projectId: 'your-project-id',
environment: 'production'
}
},
IncidentService,
{
provide: ErrorHandler,
useClass: IncidentErrorHandler
}
],
bootstrap: [AppComponent]
})
export class AppModule {}Usage in Components
Inject the Service
import { Component } from '@angular/core';
import { IncidentService } from '@watchupltd/angular';
@Component({
selector: 'app-root',
template: '<button (click)="handleClick()">Click me</button>'
})
export class AppComponent {
constructor(private incident: IncidentService) {}
async handleClick() {
try {
await this.fetchData();
} catch (error) {
this.incident.captureError(error, {
tags: { component: 'AppComponent' }
});
}
}
async fetchData() {
// Your logic
}
}What It Tracks
Automatic Tracking
- Angular Errors: Via custom
ErrorHandler - Component Errors: Errors in components, services, pipes
- Global JavaScript Errors: Via
window.onerror - Unhandled Promise Rejections: Via
window.onunhandledrejection
Manual Tracking
- Custom errors via
captureError() - Log messages via
captureMessage() - User context via
setUser() - Tags via
setTag()/setTags() - Breadcrumbs via
addBreadcrumb()
API Reference
IncidentService
Injectable service for incident tracking.
captureError(error: Error, context?: any): Promise<string | null>
try {
await riskyOperation();
} catch (error) {
this.incident.captureError(error, {
tags: { operation: 'data-fetch' },
extra: { userId: '123' }
});
}captureMessage(message: string, level?: SeverityLevel, context?: any): Promise<string | null>
this.incident.captureMessage('User completed checkout', 'info', {
tags: { flow: 'checkout' }
});addBreadcrumb(breadcrumb: any): void
this.incident.addBreadcrumb({
message: 'User clicked submit button',
category: 'user-action',
level: 'info'
});setUser(user: any): void
this.incident.setUser({
id: 'user-123',
email: '[email protected]',
username: 'john_doe'
});setTag(key: string, value: string): void
this.incident.setTag('version', '1.2.3');setTags(tags: Record<string, string>): void
this.incident.setTags({
browser: 'chrome',
os: 'windows'
});Examples
Track HTTP Errors
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { IncidentService } from '@watchupltd/angular';
import { catchError } from 'rxjs/operators';
@Injectable()
export class UserService {
constructor(
private http: HttpClient,
private incident: IncidentService
) {}
getUsers() {
return this.http.get('/api/users').pipe(
catchError(error => {
this.incident.captureError(error, {
tags: { api: 'users', endpoint: '/api/users' }
});
throw error;
})
);
}
}Track User Login
import { Component } from '@angular/core';
import { IncidentService } from '@watchupltd/angular';
@Component({
selector: 'app-login',
template: '<form (ngSubmit)="onLogin()">...</form>'
})
export class LoginComponent {
constructor(private incident: IncidentService) {}
async onLogin() {
this.incident.addBreadcrumb({
message: 'User attempting login',
category: 'auth'
});
try {
const user = await this.authService.login(this.credentials);
this.incident.setUser({
id: user.id,
email: user.email,
username: user.username
});
this.incident.captureMessage('User logged in successfully', 'info');
} catch (error) {
this.incident.captureError(error, {
tags: { flow: 'authentication' }
});
}
}
}Track Form Validation
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { IncidentService } from '@watchupltd/angular';
@Component({
selector: 'app-contact-form',
template: '<form [formGroup]="form" (ngSubmit)="onSubmit()">...</form>'
})
export class ContactFormComponent {
form: FormGroup;
constructor(
private fb: FormBuilder,
private incident: IncidentService
) {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
message: ['', Validators.required]
});
}
onSubmit() {
if (this.form.invalid) {
const errors = Object.keys(this.form.controls)
.filter(key => this.form.controls[key].invalid)
.map(key => `${key}: ${Object.keys(this.form.controls[key].errors || {}).join(', ')}`);
this.incident.captureMessage(
`Form validation failed: ${errors.join('; ')}`,
'warning',
{
tags: { form: 'contact' },
extra: { errors }
}
);
return;
}
// Submit form
}
}Track Route Changes
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { IncidentService } from '@watchupltd/angular';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent implements OnInit {
constructor(
private router: Router,
private incident: IncidentService
) {}
ngOnInit() {
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
this.incident.addBreadcrumb({
message: `Navigated to ${event.url}`,
category: 'navigation',
data: { url: event.url }
});
});
}
}Custom Error Handler
The package includes a custom error handler that's automatically registered:
@Injectable()
export class IncidentErrorHandler implements ErrorHandler {
constructor(private incidentService: IncidentService) {}
handleError(error: Error): void {
this.incidentService.captureError(error, {
tags: { angular: 'true' }
});
console.error(error);
}
}Configuration
interface IncidentConfig {
apiKey: string; // Required: Your API key
projectId: string; // Required: Your project ID
endpoint?: string; // Optional: Custom API endpoint
environment?: string; // Optional: Environment (default: 'production')
batchInterval?: number; // Optional: Batch interval in ms (default: 5000)
maxBatchSize?: number; // Optional: Max events per batch (default: 10)
maxRetries?: number; // Optional: Max retry attempts (default: 3)
enabled?: boolean; // Optional: Enable/disable SDK (default: true)
}Dependency Injection
The SDK uses Angular's dependency injection system:
INCIDENT_CONFIG: InjectionToken for configurationIncidentService: Injectable service for trackingIncidentErrorHandler: Custom ErrorHandler implementation
TypeScript Support
Full TypeScript support with type definitions included.
import type { IncidentConfig, SeverityLevel, User, Breadcrumb } from '@watchupltd/angular';Angular Version Support
- Angular 14+ (standalone components)
- Angular 12-13 (NgModule)
- Earlier versions may work but are not officially supported
Data Models
Event Model
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"project_id": "proj_abc123",
"fingerprint": "a1b2c3d4e5f6...",
"title": "Error",
"message": "Something went wrong",
"stack_trace": "Error: Something went wrong\n at Component (app.component.ts:10:5)",
"service": "frontend",
"environment": "production",
"severity": "error",
"timestamp": 1678901234567,
"platform": "javascript",
"release": "1.2.3",
"metadata": {
"angular": {
"component": "AppComponent",
"errorHandler": true
}
},
"user": {
"id": "user-123",
"email": "[email protected]",
"username": "john_doe"
},
"tags": {
"component": "AppComponent",
"version": "1.2.3"
},
"breadcrumbs": [
{
"timestamp": 1678901230000,
"message": "Component initialized",
"category": "angular",
"level": "info",
"data": {"component": "AppComponent"}
}
],
"context": {
"angular": {
"version": "16.0.0"
}
}
}User Model
{
"id": "user-123",
"email": "[email protected]",
"username": "john_doe",
"ip_address": "192.168.1.1"
}Breadcrumb Model
{
"timestamp": 1678901234567,
"message": "User action description",
"category": "user-action | navigation | http | database | console",
"level": "fatal | error | warning | info | debug",
"data": {
"key": "value"
}
}License
MIT
