RT,有朋友私我,说想看一下回复https://jfinal.com/feedback/8089的思路实现代码,最近确实非常忙。。。接了好多项目。。。话 不 多 说 了, 上 石马 ~
思路:
复制JF的 Log4jLog,在中间复写自己的业务日志,代码里面都是全局位置Log log = Log.getLog(XXX.class)。
全局变量共享在拦截器configHandler配置的,里面也是通过ThreadLocal存放的。比如登录用户,请求路径target,参数,返回结果等等信息
1建个Handler,业务里面可以随意存取公共数据,方便的很
package cn.yunjiaowu.msg.common.handler.data; import com.jfinal.handler.Handler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 公共数据共享处理器 */ public class DataHandler extends Handler { private static final ThreadLocal<DataPack> TL = new ThreadLocal(); @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.indexOf('.') != -1) { //如果要拦截处理计算静态资源,此处可放开 return ; } DataPack data = new DataPack(); TL.set(data); data.setTarget(target); data.setRequest(request); data.setResponse(response); try { next.handle(target, request, response, isHandled); }finally { TL.remove(); } } public static DataPack getDataPack() { return TL.get(); } }
1.1 Interceptor
package cn.yunjiaowu.msg.common.interceptor; import cn.yunjiaowu.msg.common.handler.data.DataHandler; import cn.yunjiaowu.msg.common.handler.data.DataPack; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; /** * 公共数据共享拦截器添加 */ public class DataInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { DataPack data = DataHandler.getDataPack(); data.setActionKey(inv.getActionKey()); //根据自己业务来自定义 inv.invoke(); } }
2数据包装类
package cn.yunjiaowu.msg.common.handler.data; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 共享包装类 */ public class DataPack { private String target; private HttpServletRequest request; private HttpServletResponse response; private String parameter; private String actionKey; //此处可以扩展自己的业务对象,如登录用户等 public String getTarget() { return target; } public void setTarget(String target) { this.target = target; } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } public HttpServletResponse getResponse() { return response; } public void setResponse(HttpServletResponse response) { this.response = response; } public String getParameter() { return parameter; } public void setParameter(String parameter) { this.parameter = parameter; } public void setActionKey(String actionKey) { this.actionKey = actionKey; } public String getActionKey() { return actionKey; } }
3Log对象
package cn.yunjiaowu.msg.common.log; import com.jfinal.log.Log; import com.jfinal.log.LogInfo; import org.apache.log4j.Level; import org.apache.log4j.Priority; public class MyLog extends Log { private org.apache.log4j.Logger log; private static final String callerFQCN = MyLog.class.getName(); MyLog(Class<?> clazz) { log = org.apache.log4j.Logger.getLogger(clazz); } MyLog(String name) { log = org.apache.log4j.Logger.getLogger(name); } public static MyLog getLog(Class<?> clazz) { return new MyLog(clazz); } public static MyLog getLog(String name) { return new MyLog(name); } public void handleLog(String callerFQCN, Priority level, String message, Throwable t){ message = MessageHandle.handle(callerFQCN, level, message, t); log.log(callerFQCN, level, message, t); } public void trace(String message) { handleLog(callerFQCN, Level.TRACE, message, null); } public void trace(String message, Throwable t) { handleLog(callerFQCN, Level.TRACE, message, t); } public void debug(String message) { handleLog(callerFQCN, Level.DEBUG, message, null); } public void debug(String message, Throwable t) { handleLog(callerFQCN, Level.DEBUG, message, t); } public void info(String message) { handleLog(callerFQCN, Level.INFO, message, null); } public void info(String message, Throwable t) { handleLog(callerFQCN, Level.INFO, message, t); } public void warn(String message) { handleLog(callerFQCN, Level.WARN, message, null); } public void warn(String message, Throwable t) { handleLog(callerFQCN, Level.WARN, message, t); } public void error(String message) { handleLog(callerFQCN, Level.ERROR, message, null); } public void error(String message, Throwable t) { handleLog(callerFQCN, Level.ERROR, message, t); } public void fatal(String message) { handleLog(callerFQCN, Level.FATAL, message, null); } public void fatal(String message, Throwable t) { handleLog(callerFQCN, Level.FATAL, message, t); } public boolean isTraceEnabled() { return log.isTraceEnabled(); } public boolean isDebugEnabled() { return log.isDebugEnabled(); } public boolean isInfoEnabled() { return log.isInfoEnabled(); } public boolean isWarnEnabled() { return log.isEnabledFor(Level.WARN); } public boolean isErrorEnabled() { return log.isEnabledFor(Level.ERROR); } public boolean isFatalEnabled() { return log.isEnabledFor(Level.FATAL); } // ------------------------------------------------------- /* * 以下方法与前面的两个 trace 方法必须覆盖父类中的实现,否则日志中的类名为 * com.jfinal.log.Log 而非所需要的日志发生地点的类名 */ public void trace(String format, Object... args) { if (isTraceEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); trace(li.message, li.throwable); } else { trace(String.format(format, args)); } } } public void debug(String format, Object... args) { if (isDebugEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); debug(li.message, li.throwable); } else { debug(String.format(format, args)); } } } public void info(String format, Object... args) { if (isInfoEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); info(li.message, li.throwable); } else { info(String.format(format, args)); } } } public void warn(String format, Object... args) { if (isWarnEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); warn(li.message, li.throwable); } else { warn(String.format(format, args)); } } } public void error(String format, Object... args) { if (isErrorEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); error(li.message, li.throwable); } else { error(String.format(format, args)); } } } public void fatal(String format, Object... args) { if (isFatalEnabled()) { if (endsWithThrowable(args)) { LogInfo li = parse(format, args); fatal(li.message, li.throwable); } else { fatal(String.format(format, args)); } } } }
4ILogFactory
package cn.yunjiaowu.msg.common.log; import com.jfinal.log.ILogFactory; import com.jfinal.log.Log; public class MyLogFactory implements ILogFactory { public Log getLog(Class<?> clazz) { return new MyLog(clazz); } public Log getLog(String name) { return new MyLog(name); } }
5MessageHandle
package cn.yunjiaowu.msg.common.log; import cn.yunjiaowu.msg.common.handler.data.DataHandler; import cn.yunjiaowu.msg.common.handler.data.DataPack; import com.jfinal.kit.Kv; import org.apache.log4j.Level; import org.apache.log4j.Priority; import javax.servlet.http.HttpServletRequest; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Enumeration; /** * 日志数据打包处理 */ public class MessageHandle { public static String handle(String callerFQCN, Priority level, String message, Throwable t){ DataPack data = DataHandler.getDataPack(); if(data == null){ return message; } Kv kv = Kv.by("target", data.getTarget()); //level可以自主判断需要记录什么样的日志 //可以在登录拦截器 放入用户信息 //如 data.get登录人信息 kv.set("actionKey", data.getActionKey()); //看自己业务是否需要加入请求参数这里只是一个例子 kv.set("parameter", getParameter(data)); if(Level.ERROR.equals(level)){ //异常日志,把部分代码行数放入日志,后续方便写入数据库,如果是只做日志文件,此处可注释掉 kv.set("code", getCode(t)); } kv.set("callerFQCN", callerFQCN); kv.set("message", message); kv.set("level", level.toString()); //我们业务日志是有入库的,这里就不写了, 根据自己业务来吧 //System.out.println(kv.toJson()); return kv.toJson(); } private static int maxOutputLengthOfParaValue = 512; private static String getParameter(DataPack data) { if (data.getParameter() != null){ return data.getParameter(); } HttpServletRequest request = data.getRequest(); Enumeration<String> e = request.getParameterNames(); if (e.hasMoreElements()) { StringBuilder sb = new StringBuilder(128); sb.append("Parameter : "); while (e.hasMoreElements()) { String name = e.nextElement(); String[] values = request.getParameterValues(name); if (values.length == 1) { sb.append(name).append("="); if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) { sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("..."); } else { sb.append(values[0]); } } else { sb.append(name).append("[]={"); for (int i=0; i<values.length; i++) { if (i > 0) sb.append(","); sb.append(values[i]); } sb.append("}"); } sb.append(" "); } String str = sb.toString(); data.setParameter(str); return str; } return null; } private static String getCode(Throwable e) { try { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); String ret = sw.toString(); ret = ret.substring(0, ret.indexOf("com.jfinal.aop.Invocation.invoke")); return ret; } catch (Exception e2) { return "ErrorInfoFromException"; } } }
6配置自己的JFinalConfig
/** * 配置常量 */ public void configConstant(Constants me) { //日志 me.setLogFactory(new MyLogFactory()); } /** * 配置全局拦截器 */ public void configInterceptor(Interceptors me) { me.add(new DataInterceptor()); } /** * 配置处理器 */ public void configHandler(Handlers me) { me.add(new DataHandler()); }
7使用日志
/** * IndexController */ @Path(value = "/", viewPath = "/") public class IndexController extends Controller { private static final Log log = Log.getLog(IndexController.class); public void index() { log.debug("来啦老弟~"); log.info("来啦老弟~"); log.warn("来啦老弟~"); log.error("来啦老弟~"); log.fatal("来啦老弟~"); render("/index.html"); } }
效果:
2021-04-22 09:37:37 [DEBUG]-[Thread: XNIO-2 task-1]-[cn.yunjiaowu.msg.index.IndexController.index()]: {"callerFQCN":"cn.yunjiaowu.msg.common.log.MyLog","level":"DEBUG","parameter":null,"message":"来啦老弟~","target":"/"} 2021-04-22 09:37:37 [INFO]-[Thread: XNIO-2 task-1]-[cn.yunjiaowu.msg.index.IndexController.index()]: {"callerFQCN":"cn.yunjiaowu.msg.common.log.MyLog","level":"INFO","parameter":null,"message":"来啦老弟~","target":"/"} 2021-04-22 09:37:37 [WARN]-[Thread: XNIO-2 task-1]-[cn.yunjiaowu.msg.index.IndexController.index()]: {"callerFQCN":"cn.yunjiaowu.msg.common.log.MyLog","level":"WARN","parameter":null,"message":"来啦老弟~","target":"/"} 2021-04-22 09:37:37 [ERROR]-[Thread: XNIO-2 task-1]-[cn.yunjiaowu.msg.index.IndexController.index()]: {"code":"ErrorInfoFromException","callerFQCN":"cn.yunjiaowu.msg.common.log.MyLog","level":"ERROR","parameter":null,"message":"来啦老弟~","target":"/"} 2021-04-22 09:37:37 [FATAL]-[Thread: XNIO-2 task-1]-[cn.yunjiaowu.msg.index.IndexController.index()]: {"callerFQCN":"cn.yunjiaowu.msg.common.log.MyLog","level":"FATAL","parameter":null,"message":"来啦老弟~","target":"/"}
大致就这样,我们有自己的深度业务,比如异常时候会自己发送钉钉运维群消息,就在处理的位置就可以了
福利图就不放了,睡觉。。。困了,有参考就点个赞呗