log4j输出日志到前端页面
一、简介
有些时候webapp有需求将日志输出到前台页面,便于开发者查看日志,本篇将介绍如何将日志输出到前台显示;
二、准备
WebSocket技术、log4j、miniui(本项目所用前端,其他也一样);
三、参考文档
受到此博主的启发,特感谢此博主:https://blog.csdn.net/xiao__gui/article/details/50041673
四、过程
1、log4j配置
- log4j.rootLogger=debug,stdout,WA
# 选用WriterAppender作为Appender,表示以流的形式输出,这个Appender一般很少用,是常用Appender的父类- log4j.appender.WA=org.apache.log4j.WriterAppender
- log4j.appender.WA.Threshold=debug
- log4j.appender.WA.layout=org.apache.log4j.PatternLayout
# %X{ip} 输出本服务器的ip,通过MDC输入(见后面描述)- log4j.appender.WA.layout.ConversionPattern=%d{ISO8601} - [%X{ip}] -%5p %c{1}:%L - %m%n
2、WebSocket后台编写
- import java.io.PipedReader;
- import java.io.PipedWriter;
- import java.io.Writer;
- import java.net.InetAddress;
- import javax.websocket.OnClose;
- import javax.websocket.OnError;
- import javax.websocket.OnOpen;
- import javax.websocket.Session;
- import javax.websocket.server.ServerEndpoint;
- import org.apache.log4j.Appender;
- import org.apache.log4j.Logger;
- import org.apache.log4j.MDC;
- import org.apache.log4j.WriterAppender;
- /**
- * @Author : ycj
- * @Description : 日志文件处理websocket服务
- * @CreateDate : 2018/6/12 9:42
- */
- @ServerEndpoint("/log")
- public class LogWebsocketHandle {
- private PipedReader reader;
- private Writer writer;
- /**
- * WebSocket请求开启
- */
- @OnOpen
- public void onOpen(Session session) {
- Logger root = Logger.getRootLogger();
- try {
- // 获取本服务器id
- String hostAddress = InetAddress.getLocalHost().getHostAddress();
// MDC是key-value结构,有兴趣的可以去了解下,在log4j的配置中设置 %X{ip},在日志中输出- MDC.put("ip",hostAddress);
- Appender appender = root.getAppender("WA");
// 通过管道流进行线程间的通讯- reader = new PipedReader();
- writer = new PipedWriter( reader) ;
- ((WriterAppender) appender).setWriter(writer);
- // 启动新的线程
- LogThread thread = new LogThread(reader, session);
- thread.start();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- /**
- * WebSocket请求关闭,关闭请求时调用此方法,关闭流
- */
- @OnClose
- public void onClose() {
- try {
- if(reader != null) {
- reader.close();
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- try {
- if(writer != null) {
- writer.close();
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- @OnError
- public void onError(Throwable thr) {
- thr.printStackTrace();
- }
- }
LogThread 线程
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.PipedReader;
- import javax.websocket.Session;
- /**
- * 新线程
- */
- public class LogThread extends Thread {
- private BufferedReader reader;
- private Session session;
- public LogThread(PipedReader pipedReader, Session session) {
- this.reader = new BufferedReader(pipedReader);
- this.session = session;
- }
- @Override
- public void run() {
- String line;
- try {
- while((line = reader.readLine()) != null) {
- // 将实时日志通过WebSocket发送给客户端
- session.getBasicRemote().sendText(line + "<br>");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
2、前端编写
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>tail log</title>
<!-- 此boot.js不需要-->- <script src="../../boot.js"></script>
- <script src="../../js/jquery-1.6.2.min.js"></script>
- </head>
- <body>
- <div id="log-container" style="height: 480px; overflow-y: scroll; background: #333;padding: 10px;">
- <div>
- </div>
- </div>
- <div>
<!-- 下面div用的是miniui的样式,其他的稍微改变一下即可-->- <div id="ck1" name="product" class="mini-checkbox" checked = "true" readOnly="false" text="是否滚动"
- onvaluechanged="onValueChanged" style="left: 20px"></div>
- <a style="left: 30px;top: 1px" class="mini-button mini-button-info"
- onclick="clearContext()">清屏</a>
- </div>
- </body>
- <script>
- // 控制是否滚动显示日志
- var checked = true;
- function onValueChanged(e) {
- checked = this.getChecked();
- }
- $(document).ready(function() {
- // 指定websocket路径,此地址建议根据用js动态获取
- var websocket = new WebSocket('ws://localhost:8080/log');
- websocket.onmessage = function(event) {
- // 接收服务端的实时日志并添加到HTML页面中(error显示红色)
- if (event.data.search("ERROR") != -1) {
- $("#log-container div").append(event.data).css("color", "#AA0000");
- } else {
- $("#log-container div").append(event.data).css("color", "#aaa");
- }
- // 是否滚动
- if (checked) {
- // 滚动条滚动到最低部
- $("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height());
- }
- };
- });
- // 清屏日志
- function clearContext() {
- $("#log-container div").empty();
- }
- </script>
- </body>
- </html>
3、js获取项目路径代码(供参考)
- // 此方式获取的bootPath项目根路径放入WebSocket路径需要进行截取
JsPath = function (js) {- var scripts = document.getElementsByTagName("script");
- var path = "";
- for (var i = 0, l = scripts.length; i < l; i++) {
- var src = scripts[i].src;
- if (src.indexOf(js) != -1) {
- var ss = src.split(js);
- path = ss[0];
- break;
- }
- }
- var href = location.href;
- href = href.split("#")[0];
- href = href.split("?")[0];
- var ss = href.split("/");
- ss.length = ss.length - 1;
- href = ss.join("/");
- if (path.indexOf("https:") == -1 && path.indexOf("http:") == -1 && path.indexOf("file:") == -1 && path.indexOf("\/") != 0) {
- path = href + "/" + path;
- }
- return path;
- }
- var bootPATH = JsPath("boot.js");
4、成果展示