需求:
前段时间根据工作人员反馈无法看到当前功能执行过程中的日志,希望实现一个日志导出的功能,可以查看当前功能执行过程中有哪些地方发生了错误,方便数据的调试。
实现:
使用过滤器,过滤到当前功能(方法)执行过程中产生的日志,将日志进行记录,当前功能(方法)执行完毕后,将过滤得到的日志写入到一个文件中进行下载即可。
废话不多说了,直接看代码,相信有一点经验的程序员都能看懂。
1. 实现一个过滤器,拦截日志
@Component
@Scope()
public class CiRouteLogFilter extends Filter<ILoggingEvent> {
// 先定义一个静态的标识变量为false,标识该过滤器的状态(true:生效,false:不生效)
private static boolean status = false;
// 覆盖的方法,在项目启动时,该方法默认执行,该拦截器默认是启动生效的,在默认启动时,将自定义的状态修改为false
@Override
public void start() {
status = false;
}
// 自定义的一个启动方法,调用时将自定义的状态修改为true
public void begin(){
status = true;
}
// 覆盖的方法,过滤器关闭的方法,在关闭过滤器时,将自定义的状态修改为false
@Override
public void stop() {
status = false;
}
// 获取自定义的状态标识
public boolean getStatus() {
return status;
}
// 以上操作的主要目的是通过定义一个静态标识变量,来实现手动控制过滤器的启动与关闭,默认是自启动的
// 覆盖的一个方法,具体的过滤流程就在这里了
@Override
public FilterReply decide(ILoggingEvent iLoggingEvent) {
// 如果自定义的状态为true,则开始过滤,否则不进行任何操作
if (getStatus()) {
String exception = "";
IThrowableProxy throwableProxy = iLoggingEvent.getThrowableProxy();
if(throwableProxy != null){
exception = "<span class='excehtext'>"+throwableProxy.getClassName()+" "+throwableProxy.getMessage()+"</span></br>";
for(int i=0; i<throwableProxy.getStackTraceElementProxyArray().length;i++){
exception += "<span class='excetext'>"+throwableProxy.getStackTraceElementProxyArray()[i].toString()+"</span></br>";
}
}
// 自我封装的一个日志消息对象,将当前过滤到的日志消息封装到该对象中
LoggerMessage loggerMessage = new CiLoggerMessage(
iLoggingEvent.getFormattedMessage(),
DateFormat.getDateTimeInstance().format(new Date(iLoggingEvent.getTimeStamp())),
iLoggingEvent.getThreadName(),
iLoggingEvent.getLoggerName(),
iLoggingEvent.getLevel().levelStr,
exception,
""
);
// 将该日志消息对象放入到自我定义的一个队列中,保证日志的顺序性
CiLoggerQueue.getInstance().add(loggerMessage);
}
// 放行该过滤器,使该过滤器继续向下执行其他的过滤器
// FilterReply的值:ACCEPT 、NEUTRAL、DENY。
return FilterReply.NEUTRAL;
}
}
日志消息对象
@Data
@AllArgsConstructor
public class LoggerMessage {
private String body;
private String timestamp;
private String threadName;
private String className;
private String level;
private String exception;
private String cause;
}
//CiLoggerMessage是该类的子类,在此没有做任何的扩展
存放日志的队列
public class CiLoggerQueue {
// 这是一个单例的队列,用来存放日志消息,该队列默认大小为3000,根据自己的业务进行调整
private static LinkedBlockingQueue<LoggerMessage> ciMessageQueue = null;
public synchronized static LinkedBlockingQueue<LoggerMessage> getInstance(){
if (ciMessageQueue == null){
ciMessageQueue = new LinkedBlockingQueue<LoggerMessage>(3000);
}
return ciMessageQueue;
}
}
2. 配置该过滤器
在项目的logback.xml日志配置文件中对于自定义的过滤器进行配置
3. 在对应的功能上添加过滤
@Autowired
CiRouteLogFilter ciRouteFilter;
@RequestMapping(value = "/downloadLog")
public void uploadExcelCiRouteDownloadLog(@RequestParam(value = "containEndSignal",required = false)Boolean containEndSignal, HttpServletRequest req, HttpServletResponse response) {
// 手动启动自定义的日志过滤器,启动后自定义的状态为true
ciRouteFilter.begin();
// 执行目标方法
try{
...
}catch (Exception ex){
Logger.error(ex.getMessage());
}
// 处理日志
LinkedBlockingQueue<LoggerMessage> queue = CiLoggerQueue.getInstance();
StringBuffer sb = new StringBuffer();
for (LoggerMessage message : queue) {
sb.append(message.getTimestamp()+" ").append(message.getBody()).append("\n");
}
queue.clear();
String log = sb.toString();
ciRouteFilter.stop();
// 下载日志
try {
InputStream inputStream = IOUtils.toInputStream(log);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("log.txt", "UTF-8"));
IOUtils.copy(inputStream,response.getOutputStream());
response.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
如有疑问,欢迎留言
到此结束,祝大家有所收获!