log.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { inWorker } from "@/next/env";
  2. import { isDevBuild } from "./env";
  3. import { logToDisk as webLogToDisk } from "./log-web";
  4. import { workerBridge } from "./worker/worker-bridge";
  5. /**
  6. * Write a {@link message} to the on-disk log.
  7. *
  8. * This is used by the renderer process (via the contextBridge) to add entries
  9. * in the log that is saved on disk.
  10. */
  11. export const logToDisk = (message: string) => {
  12. const electron = globalThis.electron;
  13. if (electron) electron.logToDisk(message);
  14. else if (inWorker()) workerLogToDisk(message);
  15. else webLogToDisk(message);
  16. };
  17. const workerLogToDisk = (message: string) => {
  18. workerBridge.logToDisk(message).catch((e) => {
  19. console.error(
  20. "Failed to log a message from worker",
  21. e,
  22. "\nThe message was",
  23. message,
  24. );
  25. });
  26. };
  27. const messageWithError = (message: string, e?: unknown) => {
  28. if (!e) return message;
  29. let es: string;
  30. if (e instanceof Error) {
  31. // In practice, we expect ourselves to be called with Error objects, so
  32. // this is the happy path so to say.
  33. return `${e.name}: ${e.message}\n${e.stack}`;
  34. } else {
  35. // For the rest rare cases, use the default string serialization of e.
  36. es = String(e);
  37. }
  38. return `${message}: ${es}`;
  39. };
  40. const logError = (message: string, e?: unknown) => {
  41. const m = `[error] ${messageWithError(message, e)}`;
  42. if (isDevBuild) console.error(m);
  43. logToDisk(m);
  44. };
  45. const logWarn = (message: string, e?: unknown) => {
  46. const m = `[warn] ${messageWithError(message, e)}`;
  47. if (isDevBuild) console.error(m);
  48. logToDisk(m);
  49. };
  50. const logInfo = (...params: unknown[]) => {
  51. const message = params
  52. .map((p) => (typeof p == "string" ? p : JSON.stringify(p)))
  53. .join(" ");
  54. const m = `[info] ${message}`;
  55. if (isDevBuild) console.log(m);
  56. logToDisk(m);
  57. };
  58. const logDebug = (param: () => unknown) => {
  59. if (isDevBuild) console.log("[debug]", param());
  60. };
  61. /**
  62. * Ente's logger.
  63. *
  64. * This is an object that provides three functions to log at the corresponding
  65. * levels - error, info or debug.
  66. *
  67. * Whenever we need to save a log message to disk,
  68. *
  69. * - When running under electron these messages are saved to the log maintained
  70. * by the electron app we're running under.
  71. *
  72. * - Otherwise such messages are written to a ring buffer in local storage.
  73. */
  74. export default {
  75. /**
  76. * Log an error message with an optional associated error object.
  77. *
  78. * {@link e} is generally expected to be an `instanceof Error` but it can be
  79. * any arbitrary object that we obtain, say, when in a try-catch handler (in
  80. * JavaScript any arbitrary value can be thrown).
  81. *
  82. * The log is written to disk. In development builds, the log is also
  83. * printed to the browser console.
  84. */
  85. error: logError,
  86. /**
  87. * Sibling of {@link error}, with the same parameters and behaviour, except
  88. * it gets prefixed with a warning instead of an error tag.
  89. */
  90. warn: logWarn,
  91. /**
  92. * Log a message.
  93. *
  94. * This is meant as a replacement of {@link console.log}, and takes an
  95. * arbitrary number of arbitrary parameters that it then serializes.
  96. *
  97. * The log is written to disk. In development builds, the log is also
  98. * printed to the browser console.
  99. */
  100. info: logInfo,
  101. /**
  102. * Log a debug message.
  103. *
  104. * To avoid running unnecessary code in release builds, this takes a
  105. * function to call to get the log message instead of directly taking the
  106. * message. The provided function will only be called in development builds.
  107. *
  108. * The function can return an arbitrary value which is serialized before
  109. * being logged.
  110. *
  111. * This log is NOT written to disk. And it is printed to the browser
  112. * console, but only in development builds.
  113. */
  114. debug: logDebug,
  115. };