Observability

AWS CloudWatch

src/app/vendors/cloud-watch/ — a thin wrapper that PUTs metric payloads to a Payability-hosted endpoint, which forwards them to CloudWatch.

@Injectable({ providedIn: 'root' })
export class CloudWatchService {
    constructor(@Inject(CLOUD_WATCH_API_URL) private readonly apiUrl: string, private readonly http: HttpClient) {}

    putMetricData(metric: CloudWatchMetric): Observable<PutMetricDataResponse> {
        return this.http.put<PutMetricDataResponse>(this.apiUrl, metric);
    }
}

Configured with CloudWatchModule.forRoot(environment.cloudWatchApiUrl).

Payload shape

export interface CloudWatchMetric {
    Namespace: MonitoringNamespace;
    MetricData: MetricData[];
}

export interface MetricData {
    MetricName: MetricName;
    Value: number | string;
    Unit: MetricUnit;
    Dimensions: Dimension[];
    Timestamp: string;
}

This mirrors the AWS PutMetricData API shape, so the forwarding service is a pass-through.

Namespace and metrics

export enum MonitoringNamespace {
    CriticalServiceMonitoring = 'critical_service_monitoring',
}

export enum MetricName {
    DailySummaryLoadTime  = 'load_time',
    DailySummaryLoadError = 'load_error',
}

load_time

How long the tracked page took to become useful.

load_error

A Daily Summary data-load failure.

MetricUnit covers the full AWS unit vocabulary — seconds through terabits per second, plus Percent, Count and None.

Only one namespace and two metric names exist. The infrastructure is general — adding a metric means adding a MetricName member and a call site, not new plumbing.

Page-load timing

PageLoadTimeTrackerModule.forRoot(['/dashboard']) in AppModule.

PageLoadTimeTrackerService

Measures navigation-to-render duration for allow-listed routes.

PageLoadTimeNotifierService

Emits completed measurements.

page-load-time-initializer.factory.ts

initializePageLodTimeTracking bridges the notifier to CloudWatchService.

page-load-time-tracker.token.ts

DI token for the route allow-list.

Only /dashboard is measured today. Registered as an APP_INITIALIZER so tracking is live before the first navigation — see Bootstrap & Initialization.

Daily Summary error tracking

initializeDailySummaryLoadDataErrorTrackerProvider registers a listener that emits the load_error metric when the Daily Summary report fails to load. That report is used by suppliers to reconcile daily payouts, so a data-pipeline break raises an operational alarm rather than only a UI error.

Smartlook

Session replay, configured with environment.smartLookKey via smartlook-client. Records interactions for UX analysis and support reproduction.

Session replay captures the DOM of a financial application. Any new field showing sensitive data — card numbers, tax ids, bank details — must be excluded from recording.

Segment

Product analytics, covered in Event Logger and Event Catalog.

Datadog

@datadog/browser-core, @datadog/browser-logs and @datadog/browser-rum are present in devDependencies but are not imported by the application. They are a leftover, not an active integration.

Error surfacing

HttpErrorHandlerInterceptor

Logs every HTTP failure to the console with { url, status, response } and shows a toast for 400/403/500. See HTTP Interceptors.

NotImplementedError

core/pyb/utils/errors.ts. Thrown by ReportBase for unimplemented optional operations.

PybToastService

User-facing transient messages.

BannerService

Persistent, condition-driven messages.

There is no global ErrorHandler override. Uncaught runtime errors surface only in the browser console — HTTP failures are the only category with structured handling.

Build provenance

environment.env, environment.version and environment.buildTime are stamped at build time by config/pre-build.js and surfaced in the UI footer, so a screenshot in a bug report identifies the exact build.

ServiceWorkerService monitors for new versions and prompts a reload through update-app-banner.