feat(notifications): add structured observability for push wake and refresh flows

Introduce NotificationDebugEvents and [Notifications] console/panel logging for push
handlers, token registration, refresh timing, schedule replacement, and WAKEUP_PING.
This commit is contained in:
Jose Olarte III
2026-05-18 18:46:16 +08:00
parent a4453c0b1b
commit 63f5c4ecc7
8 changed files with 316 additions and 104 deletions

View File

@@ -0,0 +1,111 @@
/**
* Shared observability helpers for notification flows (console + debug panel).
*/
import { logNotification } from "./NotificationDebugEvents";
export function truncateFcmTokenForLog(token: string): string {
const t = token.trim();
if (t.length <= 24) {
return t;
}
return `${t.slice(0, 12)}${t.slice(-8)}`;
}
export function logPushNotificationReceived(notification: {
title?: string;
data?: Record<string, unknown>;
}): void {
const type =
typeof notification.data?.type === "string"
? notification.data.type
: "(none)";
logNotification(`pushNotificationReceived type=${type}`, {
title: notification.title,
dataType: type,
});
if (type === "WAKEUP_PING") {
logNotification("WAKEUP_PING received — will trigger refresh");
}
}
export function logPushNotificationActionPerformed(action: {
actionId?: string;
notification?: { title?: string; data?: Record<string, unknown> };
}): void {
const type =
typeof action.notification?.data?.type === "string"
? action.notification.data.type
: "(none)";
logNotification(
`pushNotificationActionPerformed actionId=${action.actionId ?? "(none)"} type=${type}`,
{
actionId: action.actionId,
dataType: type,
},
);
}
export function logTokenRegistrationStarted(token: string): void {
logNotification("Token registration started", {
token: truncateFcmTokenForLog(token),
});
}
export function logTokenRegistrationSuccess(token: string): void {
logNotification("Token registration success", {
token: truncateFcmTokenForLog(token),
});
}
export function logTokenRegistrationSkippedDuplicate(token: string): void {
logNotification("Token registration skipped (duplicate token)", {
token: truncateFcmTokenForLog(token),
});
}
export function logTokenRegistrationFailure(
token: string,
error: unknown,
): void {
const message = error instanceof Error ? error.message : String(error);
logNotification(`Token registration failure: ${message}`, {
token: truncateFcmTokenForLog(token),
});
}
export function logRefreshStarted(source?: string): void {
logNotification(source ? `Refresh started (${source})` : "Refresh started");
}
function elapsedMsSince(startedAt: number): number {
return performance.now() - startedAt;
}
export function logRefreshSuccess(
startedAt: number,
scheduledCount: number,
): void {
logNotification(
`Refresh completed in ${Math.round(elapsedMsSince(startedAt))}ms (scheduled ${scheduledCount})`,
);
}
export function logRefreshFailure(
startedAt: number,
errorMessage: string,
status?: number,
): void {
const statusPart = status != null ? ` HTTP ${status}` : "";
logNotification(
`Refresh failed in ${Math.round(elapsedMsSince(startedAt))}ms: ${errorMessage}${statusPart}`,
);
}
export function logNotificationClearing(method: string): void {
logNotification(`Clearing notifications via ${method}`);
}
export function logScheduleReplacement(count: number): void {
logNotification(`Schedule replacement: ${count} notification(s)`);
}