有些时候webapp有需求将日志输出到前台页面,便于开发者查看日志,本篇将介绍如何将日志输出到前台显示;

  WebSocket技术、log4j、miniui(本项目所用前端,其他也一样);

  受到此博主的启发,特感谢此博主:https://blog.csdn.net/xiao__gui/article/details/50041673

  

  1. log4j.rootLogger=debug,stdout,WA
    # 选用WriterAppender作为Appender,表示以流的形式输出,这个Appender一般很少用,是常用Appender的父类
  2. log4j.appender.WA=org.apache.log4j.WriterAppender
  3. log4j.appender.WA.Threshold=debug
  4. log4j.appender.WA.layout=org.apache.log4j.PatternLayout
    # %X{ip} 输出本服务器的ip,通过MDC输入(见后面描述)
  5. log4j.appender.WA.layout.ConversionPattern=%d{ISO8601} - [%X{ip}] -%5p %c{1}:%L - %m%n

 

  1. import java.io.PipedReader;
  2. import java.io.PipedWriter;
  3. import java.io.Writer;
  4. import java.net.InetAddress;
  5. import javax.websocket.OnClose;
  6. import javax.websocket.OnError;
  7. import javax.websocket.OnOpen;
  8. import javax.websocket.Session;
  9. import javax.websocket.server.ServerEndpoint;
  10. import org.apache.log4j.Appender;
  11. import org.apache.log4j.Logger;
  12. import org.apache.log4j.MDC;
  13. import org.apache.log4j.WriterAppender;
  14. /**
  15. * @Author : ycj
  16. * @Description : 日志文件处理websocket服务
  17. * @CreateDate : 2018/6/12 9:42
  18. */
  19. @ServerEndpoint("/log")
  20. public class LogWebsocketHandle {
  21. private PipedReader reader;
  22. private Writer writer;
  23. /**
  24. * WebSocket请求开启
  25. */
  26. @OnOpen
  27. public void onOpen(Session session) {
  28. Logger root = Logger.getRootLogger();
  29. try {
  30. // 获取本服务器id
  31. String hostAddress = InetAddress.getLocalHost().getHostAddress();
       // MDC是key-value结构,有兴趣的可以去了解下,在log4j的配置中设置 %X{ip},在日志中输出
  32. MDC.put("ip",hostAddress);
  33. Appender appender = root.getAppender("WA");
       // 通过管道流进行线程间的通讯
  34. reader = new PipedReader();
  35. writer = new PipedWriter( reader) ;
  36. ((WriterAppender) appender).setWriter(writer);
  37. // 启动新的线程
  38. LogThread thread = new LogThread(reader, session);
  39. thread.start();
  40. } catch (Exception ex) {
  41. ex.printStackTrace();
  42. }
  43. }
  44. /**
  45. * WebSocket请求关闭,关闭请求时调用此方法,关闭流
  46. */
  47. @OnClose
  48. public void onClose() {
  49. try {
  50. if(reader != null) {
  51. reader.close();
  52. }
  53. } catch (Exception ex) {
  54. ex.printStackTrace();
  55. }
  56. try {
  57. if(writer != null) {
  58. writer.close();
  59. }
  60. } catch (Exception ex) {
  61. ex.printStackTrace();
  62. }
  63. }
  64. @OnError
  65. public void onError(Throwable thr) {
  66. thr.printStackTrace();
  67. }
  68. }

 

  LogThread 线程

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.PipedReader;
  4. import javax.websocket.Session;
  5. /**
  6. * 新线程
  7. */
  8. public class LogThread extends Thread {
  9. private BufferedReader reader;
  10. private Session session;
  11. public LogThread(PipedReader pipedReader, Session session) {
  12. this.reader = new BufferedReader(pipedReader);
  13. this.session = session;
  14. }
  15. @Override
  16. public void run() {
  17. String line;
  18. try {
  19. while((line = reader.readLine()) != null) {
  20. // 将实时日志通过WebSocket发送给客户端
  21. session.getBasicRemote().sendText(line + "<br>");
  22. }
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }

  

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>tail log</title>
     <!-- 此boot.js不需要-->
  6. <script src="../../boot.js"></script>
  7. <script src="../../js/jquery-1.6.2.min.js"></script>
  8. </head>
  9. <body>
  10. <div id="log-container" style="height: 480px; overflow-y: scroll; background: #333;padding: 10px;">
  11. <div>
  12. </div>
  13. </div>
  14. <div>
      <!-- 下面div用的是miniui的样式,其他的稍微改变一下即可-->
  15. <div id="ck1" name="product" class="mini-checkbox" checked = "true" readOnly="false" text="是否滚动"
  16. onvaluechanged="onValueChanged" style="left: 20px"></div>
  17. <a style="left: 30px;top: 1px" class="mini-button mini-button-info"
  18. onclick="clearContext()">清屏</a>
  19. </div>
  20. </body>
  21. <script>
  22. // 控制是否滚动显示日志
  23. var checked = true;
  24. function onValueChanged(e) {
  25. checked = this.getChecked();
  26. }
  27. $(document).ready(function() {
  28. // 指定websocket路径,此地址建议根据用js动态获取
  29. var websocket = new WebSocket('ws://localhost:8080/log');
  30. websocket.onmessage = function(event) {
  31. // 接收服务端的实时日志并添加到HTML页面中(error显示红色)
  32. if (event.data.search("ERROR") != -1) {
  33. $("#log-container div").append(event.data).css("color", "#AA0000");
  34. } else {
  35. $("#log-container div").append(event.data).css("color", "#aaa");
  36. }
  37. // 是否滚动
  38. if (checked) {
  39. // 滚动条滚动到最低部
  40. $("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height());
  41. }
  42. };
  43. });
  44. // 清屏日志
  45. function clearContext() {
  46. $("#log-container div").empty();
  47. }
  48. </script>
  49. </body>
  50. </html>
  1. // 此方式获取的bootPath项目根路径放入WebSocket路径需要进行截取
    JsPath = function (js) {
  2. var scripts = document.getElementsByTagName("script");
  3. var path = "";
  4. for (var i = 0, l = scripts.length; i < l; i++) {
  5. var src = scripts[i].src;
  6. if (src.indexOf(js) != -1) {
  7. var ss = src.split(js);
  8. path = ss[0];
  9. break;
  10. }
  11. }
  12. var href = location.href;
  13. href = href.split("#")[0];
  14. href = href.split("?")[0];
  15. var ss = href.split("/");
  16. ss.length = ss.length - 1;
  17. href = ss.join("/");
  18. if (path.indexOf("https:") == -1 && path.indexOf("http:") == -1 && path.indexOf("file:") == -1 && path.indexOf("\/") != 0) {
  19. path = href + "/" + path;
  20. }
  21. return path;
  22. }
  23. var bootPATH = JsPath("boot.js");

 

 

版权声明:本文为ycjcham原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ycjcham/p/9243455.html