log-web.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { isDevBuild } from "@/next/env";
  2. import { addLogLine } from "@ente/shared/logging";
  3. /**
  4. * Log a standard startup banner.
  5. *
  6. * This helps us identify app starts and other environment details in the logs.
  7. *
  8. * @param appId An identifier of the app that is starting.
  9. * @param userId The uid for the currently logged in user, if any.
  10. */
  11. export const logStartupBanner = (appId: string, userId?: number) => {
  12. // TODO (MR): Remove the need to lowercase it, change the enum itself.
  13. const appIdL = appId.toLowerCase();
  14. const sha = process.env.GIT_SHA;
  15. const buildId = isDevBuild ? "dev " : sha ? `git ${sha} ` : "";
  16. addLogLine(`Starting ente-${appIdL}-web ${buildId}uid ${userId ?? 0}`);
  17. };
  18. interface LogEntry {
  19. timestamp: number;
  20. logLine: string;
  21. }
  22. const lsKey = "logs";
  23. /**
  24. * Record {@link message} in a persistent log storage.
  25. *
  26. * These strings, alongwith associated timestamps, get added to a small ring
  27. * buffer, whose contents can be later be retrieved by using {@link savedLogs}.
  28. *
  29. * This ring buffer is persisted in the browser's local storage.
  30. */
  31. export const logToDisk = (message: string) => {
  32. const maxCount = 1000;
  33. const log: LogEntry = { logLine: message, timestamp: Date.now() };
  34. try {
  35. const logs = logEntries();
  36. if (logs.length > maxCount) {
  37. logs.slice(logs.length - maxCount);
  38. }
  39. logs.push(log);
  40. localStorage.setItem(lsKey, JSON.stringify({ logs }));
  41. } catch (e) {
  42. console.error("Failed to persist log", e);
  43. if (e instanceof Error && e.name === "QuotaExceededError") {
  44. localStorage.removeItem(lsKey);
  45. }
  46. }
  47. };
  48. const logEntries = (): unknown[] => {
  49. const s = localStorage.getItem("logs");
  50. if (!s) return [];
  51. const o: unknown = JSON.parse(s);
  52. if (!(o && typeof o == "object" && "logs" in o && Array.isArray(o.logs))) {
  53. console.error("Unexpected log entries obtained from local storage", o);
  54. return [];
  55. }
  56. return o.logs;
  57. };
  58. /**
  59. * Return a string containing all recently saved log messages.
  60. *
  61. * @see {@link persistLog}.
  62. */
  63. export const savedLogs = () => logEntries().map(formatEntry).join("\n");
  64. const formatEntry = (e: unknown) => {
  65. if (e && typeof e == "object" && "timestamp" in e && "logLine" in e) {
  66. const timestamp = e.timestamp;
  67. const logLine = e.logLine;
  68. if (typeof timestamp == "number" && typeof logLine == "string") {
  69. return `[${new Date(timestamp).toISOString()}] ${logLine}`;
  70. }
  71. }
  72. return String(e);
  73. };