前言
实际项目开发过程中,为了加快程序计算处理速度,我们常常会用到多线程技术,但是由于多个线程都是异步执行,所以每个线程打印的日志会相互干扰、散乱混杂,变得难以阅读和追踪,这篇文件我们就来解决一下这个问题。
我想到的解放办法有两种,一种是给线程命名,打印日志时加入线程名称,查找时候再通过线程名来进行筛选过滤。这种方式的优点是比较简单直接,代码改动量小,而且能够一次将所有有报错日志的线程都查找出来,缺点是日志文件很大时查找会比较慢。
第二种方法是给每个线程新建一个单独的日志文件,这样查看哪个线程日志就去找对应文件。优点是文件相互隔离,内容清晰,缺点是不能统一查找报错日志了。具体需要使用哪种方案还是需要根据使用场景和实际需求来选择。
本文主要介绍第二种方法的实现。
log4j
我们以常用的日志实现类log4j为例,maven坐标如下
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
创建线程日志类
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
public class ThreadLogger {
public static Logger getLogger(String logName) {
PatternLayout layout = new PatternLayout("[%d{MM-dd HH:mm:ss}] %-5p %-8t %m%n");
// 日志文件按照每天分文件夹存放
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String logPath = "/log/" + sdf.format(new Date()) + "/" + logName + ".log";
// 文件输出
DailyRollingFileAppender fileAppender = null;
try {
fileAppender = new DailyRollingFileAppender(layout, logPath, "yyyy-MM-dd");
fileAppender.setAppend(false);
fileAppender.setImmediateFlush(true);
fileAppender.setThreshold(Level.DEBUG);
fileAppender.activateOptions();
} catch (IOException e) {
e.printStackTrace();
}
// 绑定到logger
Logger logger = Logger.getLogger(logName);
logger.setLevel(Level.DEBUG);
logger.addAppender(fileAppender);
return logger;
}
}
自定义线程类进行测试
import org.apache.log4j.Logger;
public class MyThread implements Runnable {
Logger logger;
public MyThread(String logName) {
logger = ThreadLogger.getLogger(logName);
}
public void run() {
logger.debug("debug start");
logger.info("thread started!");
logger.error("error");
logger.info("thread finished!");
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
MyThread myThread = new MyThread(i+"");
myThread.run();
}
}
}
结果
总结
给线程设置单独日志的核心就是要让每个线程持有自己的log对象,并且在log对象中通过appender设置线程自己日志格式和路径。