log4j实时输出日志到前端页面

日志实战 小海豚博客管理员 2020-04-26 17:42:29.0 281 0条

一、简介  

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

二、准备

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

三、WriterAppender

 org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方),是一个非常基础的Appender,他可以将LoggingEvent合成的结果直接交给一个Writer对象去处理。

四、过程

  1、log4j配置   

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

  2、WebSocket后台编写

  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();
  32. // MDC是key-value结构,有兴趣的可以去了解下,在log4j的配置中设置 %X{ip},在日志中输出
  33. MDC.put("ip",hostAddress);
  34. Appender appender = root.getAppender("WA");
  35. // 通过管道流进行线程间的通讯
  36. reader = new PipedReader();
  37. writer = new PipedWriter( reader) ;
  38. ((WriterAppender) appender).setWriter(writer);
  39. // 启动新的线程
  40. LogThread thread = new LogThread(reader, session);
  41. thread.start();
  42. } catch (Exception ex) {
  43. ex.printStackTrace();
  44. }
  45. }
  46. /**
  47. * WebSocket请求关闭,关闭请求时调用此方法,关闭流
  48. */
  49. @OnClose
  50. public void onClose() {
  51. try {
  52. if(reader != null) {
  53. reader.close();
  54. }
  55. } catch (Exception ex) {
  56. ex.printStackTrace();
  57. }
  58. try {
  59. if(writer != null) {
  60. writer.close();
  61. }
  62. } catch (Exception ex) {
  63. ex.printStackTrace();
  64. }
  65. }
  66. @OnError
  67. public void onError(Throwable thr) {
  68. thr.printStackTrace();
  69. }
  70. }

  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. }

  

  2、前端编写

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

  3、js获取项目路径代码(供参考)

  1. // 此方式获取的bootPath项目根路径放入WebSocket路径需要进行截取
  2. JsPath = function (js) {
  3. var scripts = document.getElementsByTagName("script");
  4. var path = "";
  5. for (var i = 0, l = scripts.length; i < l; i++) {
  6. var src = scripts[i].src;
  7. if (src.indexOf(js) != -1) {
  8. var ss = src.split(js);
  9. path = ss[0];
  10. break;
  11. }
  12. }
  13. var href = location.href;
  14. href = href.split("#")[0];
  15. href = href.split("?")[0];
  16. var ss = href.split("/");
  17. ss.length = ss.length - 1;
  18. href = ss.join("/");
  19. if (path.indexOf("https:") == -1 && path.indexOf("http:") == -1 && path.indexOf("file:") == -1 && path.indexOf("\/") != 0) {
  20. path = href + "/" + path;
  21. }
  22. return path;
  23. }
  24. var bootPATH = JsPath("boot.js");
暗锚,解决锚点偏移

文章评论

嘿,来试试登录吧!