您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

LogSection.java 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.debug.internal;
  17. import java.util.logging.Formatter;
  18. import java.util.logging.Handler;
  19. import java.util.logging.Level;
  20. import java.util.logging.LogRecord;
  21. import java.util.logging.Logger;
  22. import com.google.gwt.dom.client.Element;
  23. import com.google.gwt.logging.client.HtmlLogFormatter;
  24. import com.google.gwt.storage.client.Storage;
  25. import com.google.gwt.user.client.DOM;
  26. import com.google.gwt.user.client.Timer;
  27. import com.google.gwt.user.client.ui.Button;
  28. import com.google.gwt.user.client.ui.FlowPanel;
  29. import com.google.gwt.user.client.ui.HTML;
  30. import com.google.gwt.user.client.ui.Widget;
  31. import com.vaadin.client.ApplicationConnection;
  32. import com.vaadin.client.ValueMap;
  33. /**
  34. * Displays the log messages.
  35. * <p>
  36. * Scroll lock state is persisted.
  37. * </p>
  38. *
  39. * @since 7.1
  40. * @author Vaadin Ltd
  41. */
  42. public class LogSection implements Section {
  43. private final class LogSectionHandler extends Handler {
  44. private LogSectionHandler() {
  45. setLevel(Level.ALL);
  46. setFormatter(new HtmlLogFormatter(true) {
  47. @Override
  48. protected String getHtmlPrefix(LogRecord event) {
  49. return "";
  50. }
  51. @Override
  52. protected String getHtmlSuffix(LogRecord event) {
  53. return "";
  54. }
  55. @Override
  56. protected String getRecordInfo(LogRecord event,
  57. String newline) {
  58. return "";
  59. }
  60. });
  61. }
  62. @Override
  63. public void publish(LogRecord record) {
  64. if (!isLoggable(record)) {
  65. return;
  66. }
  67. // If no message is provided, record.getMessage will be null and so
  68. // the formatter.format will fail with NullPointerException (#12588)
  69. if (record.getMessage() == null) {
  70. record.setMessage("");
  71. }
  72. Formatter formatter = getFormatter();
  73. String msg = formatter.format(record);
  74. addRow(record.getLevel(), msg);
  75. }
  76. @Override
  77. public void close() {
  78. // Nothing to do
  79. }
  80. @Override
  81. public void flush() {
  82. // Nothing todo
  83. }
  84. }
  85. // If scroll is not locked, content will be scrolled after delay
  86. private static final int SCROLL_DELAY = 100;
  87. private Timer scrollTimer = null;
  88. // TODO should be persisted
  89. // log content limit
  90. private int limit = 500;
  91. private final DebugButton tabButton = new DebugButton(Icon.LOG,
  92. "Debug message log");
  93. private final HTML content = new HTML();
  94. private final Element contentElement;
  95. private final FlowPanel controls = new FlowPanel();
  96. private final Button clear = new DebugButton(Icon.CLEAR, "Clear log");
  97. private final Button reset = new DebugButton(Icon.RESET_TIMER,
  98. "Reset timer");
  99. private final Button scroll = new DebugButton(Icon.SCROLL_LOCK,
  100. "Scroll lock");
  101. public LogSection() {
  102. contentElement = content.getElement();
  103. content.setStylePrimaryName(VDebugWindow.STYLENAME + "-log");
  104. // clear log button
  105. controls.add(clear);
  106. clear.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
  107. clear.addClickHandler(event -> clear());
  108. // reset timer button
  109. controls.add(reset);
  110. reset.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
  111. reset.addClickHandler(event -> resetTimer());
  112. // scroll lock toggle
  113. controls.add(scroll);
  114. scroll.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON);
  115. scroll.addClickHandler(event -> toggleScrollLock());
  116. // select message if row is clicked
  117. content.addClickHandler(event -> {
  118. Element el = Element.as(event.getNativeEvent().getEventTarget());
  119. while (!el.getClassName()
  120. .contains(VDebugWindow.STYLENAME + "-message")) {
  121. if (el == contentElement) {
  122. // clicked something else
  123. return;
  124. }
  125. el = el.getParentElement();
  126. }
  127. selectText(el);
  128. });
  129. // Add handler to the root logger
  130. Logger.getLogger("").addHandler(new LogSectionHandler());
  131. }
  132. /**
  133. * Toggles scroll lock, writes state to persistent storage.
  134. */
  135. void toggleScrollLock() {
  136. setScrollLock(scrollTimer != null);
  137. Storage storage = Storage.getLocalStorageIfSupported();
  138. if (storage == null) {
  139. return;
  140. }
  141. VDebugWindow.writeState(storage, "log-scrollLock", scrollTimer == null);
  142. }
  143. /**
  144. * Activates or deactivates scroll lock
  145. *
  146. * @param locked
  147. */
  148. void setScrollLock(boolean locked) {
  149. if (locked && scrollTimer != null) {
  150. scrollTimer.cancel();
  151. scrollTimer = null;
  152. } else if (!locked && scrollTimer == null) {
  153. scrollTimer = new Timer() {
  154. @Override
  155. public void run() {
  156. Element el = (Element) contentElement.getLastChild();
  157. if (el != null) {
  158. el = el.getFirstChildElement();
  159. if (el != null) {
  160. el.scrollIntoView();
  161. }
  162. }
  163. }
  164. };
  165. }
  166. scroll.setStyleDependentName(VDebugWindow.STYLENAME_ACTIVE, locked);
  167. }
  168. private native void selectText(Element el)
  169. /*-{
  170. if ($doc.selection && $doc.selection.createRange) {
  171. var r = $doc.selection.createRange();
  172. r.moveToElementText(el);
  173. r.select();
  174. } else if ($doc.createRange && $wnd.getSelection) {
  175. var r = $doc.createRange();
  176. r.selectNode(el);
  177. var selection = $wnd.getSelection();
  178. selection.removeAllRanges();
  179. selection.addRange(r);
  180. }
  181. }-*/;
  182. private void clear() {
  183. contentElement.setInnerText("");
  184. }
  185. private void applyLimit() {
  186. while (contentElement.getChildCount() > limit) {
  187. contentElement.removeChild(contentElement.getFirstChild());
  188. }
  189. }
  190. /**
  191. * Sets the log row limit.
  192. *
  193. * @param limit The limit
  194. */
  195. public void setLimit(int limit) {
  196. this.limit = limit;
  197. applyLimit();
  198. // TODO should be persisted
  199. }
  200. /**
  201. * Gets the current log row limit.
  202. *
  203. * @return
  204. */
  205. public int getLimit() {
  206. // TODO should be read from persistent storage
  207. return limit;
  208. }
  209. @Override
  210. public DebugButton getTabButton() {
  211. return tabButton;
  212. }
  213. @Override
  214. public Widget getControls() {
  215. return controls;
  216. }
  217. @Override
  218. public Widget getContent() {
  219. return content;
  220. }
  221. @Override
  222. public void show() {
  223. Storage storage = Storage.getLocalStorageIfSupported();
  224. if (storage == null) {
  225. return;
  226. }
  227. setScrollLock(VDebugWindow.readState(storage, "log-scrollLock", false));
  228. }
  229. @Override
  230. public void hide() {
  231. // remove timer
  232. setScrollLock(true);
  233. }
  234. /**
  235. * Schedules a scoll if scroll lock is not active.
  236. */
  237. private void maybeScroll() {
  238. if (scrollTimer != null) {
  239. scrollTimer.cancel();
  240. scrollTimer.schedule(SCROLL_DELAY);
  241. }
  242. }
  243. /**
  244. * Resets the timer and inserts a log row indicating this.
  245. */
  246. private void resetTimer() {
  247. int sinceStart = VDebugWindow.getMillisSinceStart();
  248. int sinceReset = VDebugWindow.resetTimer();
  249. Element row = DOM.createDiv();
  250. row.addClassName(VDebugWindow.STYLENAME + "-reset");
  251. row.setInnerHTML(Icon.RESET_TIMER + " Timer reset");
  252. row.setTitle(VDebugWindow.getTimingTooltip(sinceStart, sinceReset));
  253. contentElement.appendChild(row);
  254. maybeScroll();
  255. }
  256. /**
  257. * Adds a row to the log, applies the log row limit by removing old rows if
  258. * needed, and scrolls new row into view if scroll lock is not active.
  259. *
  260. * @param level
  261. * @param msg
  262. * @return
  263. */
  264. private Element addRow(Level level, String msg) {
  265. int sinceReset = VDebugWindow.getMillisSinceReset();
  266. int sinceStart = VDebugWindow.getMillisSinceStart();
  267. Element row = DOM.createDiv();
  268. row.addClassName(VDebugWindow.STYLENAME + "-row");
  269. row.addClassName(level.getName());
  270. String inner = "<span class='" + VDebugWindow.STYLENAME + "-"
  271. + "'></span><span class='" + VDebugWindow.STYLENAME
  272. + "-time' title='"
  273. + VDebugWindow.getTimingTooltip(sinceStart, sinceReset) + "'>"
  274. + sinceReset + "ms</span><span class='" + VDebugWindow.STYLENAME
  275. + "-message'>" + msg + "</span>";
  276. row.setInnerHTML(inner);
  277. contentElement.appendChild(row);
  278. applyLimit();
  279. maybeScroll();
  280. return row;
  281. }
  282. @Override
  283. public void meta(ApplicationConnection ac, ValueMap meta) {
  284. addRow(Level.FINE, "Meta: " + meta.toSource());
  285. }
  286. @Override
  287. public void uidl(ApplicationConnection ac, ValueMap uidl) {
  288. addRow(Level.FINE, "UIDL: " + uidl.toSource());
  289. }
  290. }