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、成果展示


浙公网安备 33010602011771号