Persistent Console Logger

记录所有 console.log 即使控制台被清除也能查看

  1. // ==UserScript==
  2. // @name Persistent Console Logger
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description 记录所有 console.log 即使控制台被清除也能查看
  6. // @author Your Name
  7. // @match *://*/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // 创建一个存储日志的数组
  16. const logStorage = [];
  17.  
  18. // 保存原始的 console.log 和 console.clear 方法
  19. const originalLog = console.log;
  20. const originalClear = console.clear;
  21.  
  22. // 重写 console.log 方法
  23. console.log = function(...args) {
  24. // 将日志存储到 logStorage 数组
  25. logStorage.push({
  26. type: 'log',
  27. args: args,
  28. timestamp: new Date()
  29. });
  30.  
  31. // 调用原始的 console.log 方法
  32. originalLog.apply(console, args);
  33. };
  34.  
  35. // 重写 console.warn 方法
  36. console.warn = function(...args) {
  37. logStorage.push({
  38. type: 'warn',
  39. args: args,
  40. timestamp: new Date()
  41. });
  42. originalLog.apply(console, args);
  43. };
  44.  
  45. // 重写 console.error 方法
  46. console.error = function(...args) {
  47. logStorage.push({
  48. type: 'error',
  49. args: args,
  50. timestamp: new Date()
  51. });
  52. originalLog.apply(console, args);
  53. };
  54.  
  55. // 重写 console.clear 方法
  56. console.clear = function() {
  57. // 可以选择不清除 logStorage,或根据需要清除
  58. // 这里选择不清除,确保日志仍然被记录
  59.  
  60. // 调用原始的 console.clear 方法
  61. originalClear.apply(console);
  62. };
  63.  
  64. // 创建一个界面按钮来查看存储的日志
  65. const createLogViewer = () => {
  66. // 创建按钮
  67. const button = document.createElement('button');
  68. button.innerText = '查看持久化日志';
  69. button.style.position = 'fixed';
  70. button.style.bottom = '20px';
  71. button.style.right = '20px';
  72. button.style.padding = '10px 20px';
  73. button.style.zIndex = 10000;
  74. button.style.backgroundColor = '#4CAF50';
  75. button.style.color = 'white';
  76. button.style.border = 'none';
  77. button.style.borderRadius = '5px';
  78. button.style.cursor = 'pointer';
  79. button.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
  80. button.title = '点击查看持久化的 console.log 日志';
  81.  
  82. // 创建日志查看窗口
  83. const logWindow = document.createElement('div');
  84. logWindow.style.position = 'fixed';
  85. logWindow.style.top = '50px';
  86. logWindow.style.right = '20px';
  87. logWindow.style.width = '400px';
  88. logWindow.style.height = '300px';
  89. logWindow.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  90. logWindow.style.color = 'white';
  91. logWindow.style.padding = '10px';
  92. logWindow.style.borderRadius = '5px';
  93. logWindow.style.overflowY = 'scroll';
  94. logWindow.style.display = 'none';
  95. logWindow.style.zIndex = 10000;
  96. logWindow.style.fontFamily = 'monospace';
  97. logWindow.style.fontSize = '12px';
  98.  
  99. // 创建关闭按钮
  100. const closeButton = document.createElement('button');
  101. closeButton.innerText = '关闭';
  102. closeButton.style.position = 'absolute';
  103. closeButton.style.top = '10px';
  104. closeButton.style.right = '10px';
  105. closeButton.style.padding = '5px 10px';
  106. closeButton.style.backgroundColor = '#f44336';
  107. closeButton.style.color = 'white';
  108. closeButton.style.border = 'none';
  109. closeButton.style.borderRadius = '3px';
  110. closeButton.style.cursor = 'pointer';
  111.  
  112. // 添加事件监听器
  113. button.addEventListener('click', () => {
  114. logWindow.style.display = 'block';
  115. renderLogs();
  116. });
  117.  
  118. closeButton.addEventListener('click', () => {
  119. logWindow.style.display = 'none';
  120. });
  121.  
  122. // 添加内容到 logWindow
  123. logWindow.appendChild(closeButton);
  124.  
  125. // 创建日志内容容器
  126. const logContent = document.createElement('div');
  127. logContent.style.marginTop = '40px';
  128. logWindow.appendChild(logContent);
  129.  
  130. // 渲染日志内容
  131. const renderLogs = () => {
  132. logContent.innerHTML = ''; // 清空之前的内容
  133. logStorage.forEach(entry => {
  134. const logEntry = document.createElement('div');
  135. logEntry.style.marginBottom = '5px';
  136. logEntry.style.padding = '5px';
  137. logEntry.style.borderBottom = '1px solid #555';
  138.  
  139. const time = entry.timestamp.toLocaleTimeString();
  140. const type = entry.type.toUpperCase();
  141. const args = entry.args.map(arg => {
  142. if (typeof arg === 'object') {
  143. try {
  144. return JSON.stringify(arg);
  145. } catch (e) {
  146. return '[Object]';
  147. }
  148. }
  149. return arg.toString();
  150. }).join(' ');
  151.  
  152. logEntry.innerHTML = `<strong>[${time}] ${type}:</strong> ${args}`;
  153. logContent.appendChild(logEntry);
  154. });
  155. };
  156.  
  157. // 自动刷新日志内容
  158. setInterval(renderLogs, 1000);
  159.  
  160. // 将按钮和日志窗口添加到页面
  161. document.body.appendChild(button);
  162. document.body.appendChild(logWindow);
  163. };
  164.  
  165. // 延迟创建界面,确保页面加载完成
  166. window.addEventListener('load', createLogViewer);
  167.  
  168. // 可选:将日志存储到本地存储,以便刷新页面后仍能保留
  169. const saveLogsToLocalStorage = () => {
  170. window.addEventListener('beforeunload', () => {
  171. localStorage.setItem('persistentLogs', JSON.stringify(logStorage));
  172. });
  173.  
  174. // 读取本地存储的日志
  175. const savedLogs = localStorage.getItem('persistentLogs');
  176. if (savedLogs) {
  177. try {
  178. const parsedLogs = JSON.parse(savedLogs);
  179. // 将字符串的 timestamp 转回 Date 对象
  180. parsedLogs.forEach(log => {
  181. if (log.timestamp) {
  182. log.timestamp = new Date(log.timestamp);
  183. }
  184. });
  185. logStorage.push(...parsedLogs);
  186. localStorage.removeItem('persistentLogs');
  187. } catch (e) {
  188. console.error('Failed to parse saved logs:', e);
  189. }
  190. }
  191. };
  192.  
  193. saveLogsToLocalStorage();
  194.  
  195. })();