JUL 使用手册【原创·个人私用】
首先一问:我们为什么需要日志系统?
我们之前一直都在使用System.out.println
来打印信息,但是,如果项目中存在大量的控制台输出语句,会显得很凌乱,而且日志的粒度是不够细的,假如我们现在希望,项目只在debug的情况下打印某些日志,而在实际运行时不打印日志,采用直接输出的方式就很难实现了,因此我们需要使用日志框架来规范化日志输出。
Logger 是 JDK 自带的 日志框架,我觉得是挺方便的,能把这个学会,其实就已经很牛逼了 ~ 切记:它又被称为 JUL(Java Util Logging)
public class Main {
public static void main(String[] args) {
// 首先获取日志打印器
Logger logger = Logger.getLogger(Main.class.getName());
// 调用info来输出一个普通的信息,直接填写字符串即可
logger.info("我是普通的日志");
}
}
JDK 自带的日志
日志分为7个级别,详细信息我们可以在Level类中查看:
SEVERE
(最高级别):用于表示严重错误,表示程序遇到了无法继续执行的致命错误。WARNING
:用于表示警告信息,表示程序遇到了一些可能会导致问题的情况,但不会影响程序的正常执行。INFO
(默认级别):用于表示常规消息,表示程序的正常运行状态或重要的操作。CONFIG
:用于表示配置信息,表示程序的配置项或设置。FINE
:用于表示详细信息,比INFO
级别更详细,用于调试和追踪程序的执行过程。FINER
:用于表示更详细的信息,比FINE
级别更详细,通常用于更详细的调试和追踪。FINEST
(最低级别):用于表示最详细的信息,提供程序执行过程中的所有细节,通常用于非常详细的调试和追踪。
我们之前通过info
方法直接输出的结果就是使用的默认级别的日志,我们可以通过log
方法来设定该条日志的输出级别:
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
logger.log(Level.SEVERE, "严重的错误", new IOException("我就是错误"));
logger.log(Level.WARNING, "警告的内容");
logger.log(Level.INFO, "普通的信息");
logger.log(Level.CONFIG, "级别低于普通信息");
}
我们发现,级别低于默认级别的日志信息,无法输出到控制台,我们可以通过设置来修改日志的打印级别:
修改日志的打印级别
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
// 修改日志级别
// 意思是 可以打印的信息级别 最低是 CONFIG
logger.setLevel(Level.CONFIG);
// 还可以设置 不使用父日志处理器
// 父日志处理器的 日志级别是 INFO
logger.setUseParentHandlers(false);
// 以使用自定义的日志处理器,来决定能否对某条信息 进行输出
// 这里是新建了 控制台处理器,是直接把处理后的信息直接输出到 控制台上的
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.INFO);
// 当你不适用 父日志处理器之后,自定义处理器是可以添加多个的
// 然后 我们要输出的每条信息,它会按照 添加处理器的顺序 进行判断
// 如果 第一个处理器可以处理,那么就轮不到 第二个处理器去处理!就是这样的规则!
logger.addHandler(handler);
// 添加输出到本地文件,它就会将输出到 test.log 中去
FileHandler fileHandler = new FileHandler("test.log");
fileHandler.setLevel(Level.CONFIG);
logger.addHandler(fileHandler);
// 由此,我们会发现 只要是 CONFIG 级别的信息,就都会被输出到 test.log 中去
// 用 logger.log 进行不同级别的 信息输出!
logger.log(Level.SEVERE, "严重的错误", new IOException("我就是错误"));
logger.log(Level.WARNING, "警告的内容");
logger.log(Level.INFO, "普通的信息");
logger.log(Level.CONFIG, "级别低于普通信息");
}
修改打印(输出)格式
public static void main(String[] args) throws IOException {
Logger logger = Logger.getLogger(Main.class.getName());
logger.setUseParentHandlers(false);
// 为了让颜色变回普通的颜色,通过代码块在初始化时将输出流设定为 System.out
ConsoleHandler handler = new ConsoleHandler(){{
setOutputStream(System.out);
}};
// 以创建匿名内部类的方式去实现自定义的格式
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String time = format.format(new Date(record.getMillis())); //格式化日志时间
String level = record.getLevel().getName(); // 获取日志级别名称
// String level = record.getLevel().getLocalizedName(); // 获取本地化名称(语言跟随系统)
String thread = String.format("%10s", Thread.currentThread().getName()); //线程名称(做了格式化处理,留出10格空间)
long threadID = record.getThreadID(); //线程ID
String className = String.format("%-20s", record.getSourceClassName()); //发送日志的类名
String msg = record.getMessage(); //日志消息
// \033[33m 作为颜色代码,30~37都有对应的颜色,38是没有颜色,IDEA能显示,但是某些地方可能不支持
return "\033[38m" + time + " \033[33m" + level + " \033[35m" + threadID
+ "\033[38m --- [" + thread + "] \033[36m" + className + "\033[38m : " + msg + "\n";
}
});
logger.addHandler(handler);
logger.info("我是测试消息1...");
logger.log(Level.INFO, "我是测试消息2...");
logger.log(Level.WARNING, "我是测试消息3...");
}
设置信息过滤器
// 由于 SetFilter() 的参数 是接收只有 一个方法的
// 内容只要包含 "普通" 我们就肯定会过滤掉!
logger.setFilter(record -> !record.getMessage().contains("普通"));
读取 Properties 配置文件设置日志
读取配置文件来指定 输出格式
public class TestFormatter extends Formatter {
@Override
public String format(LogRecord record) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String time = format.format(new Date(record.getMillis())); //格式化日志时间
return time + " : " + record.getMessage() + "\n";
}
}
java.util.logging.ConsoleHandler.formatter = com.test.TestFormatter
logging.properties
# RootLogger 的默认处理器为
handlers= java.util.logging.ConsoleHandler
# RootLogger 的默认的日志级别
.level= CONFIG
# 指定默认日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定默认日志消息格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定默认的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
public static void main(String[] args) throws IOException {
//获取日志管理器
LogManager manager = LogManager.getLogManager();
//读取我们自己的配置文件
manager.readConfiguration(new FileInputStream("logging.properties"));
//再获取日志打印器
Logger logger = Logger.getLogger(Main.class.getName());
logger.log(Level.CONFIG, "我是一条日志信息"); //通过自定义配置文件,我们发现默认级别不再是INFO了
}
Lombok 快速开启日志(重点)
@Log
public class Main {
public static void main(String[] args) {
System.out.println("自动生成的Logger名称:"+log.getName());
log.info("我是日志信息");
}
}
只需要添加一个@Log
注解即可,添加后,我们可以直接使用一个静态变量log,而它就是自动生成的Logger。我们也可以手动指定名称:
@Log(topic = "打工是不可能打工的")
public class Main {
public static void main(String[] args) {
System.out.println("自动生成的Logger名称:"+log.getName());
log.info("我是日志信息");
}
}
MyBatis 使用自定义 Logger 日志
- 创建一个实现了
org.apache.ibatis.logging.Log
接口的类,用于将MyBatis的日志输出到JDK自带的Logger中。例如,可以创建一个名为JdkLogger
的类。
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdkLogger implements org.apache.ibatis.logging.Log {
private Logger logger;
public JdkLogger(String clazz) {
logger = Logger.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return logger.isLoggable(Level.FINE);
}
@Override
public void error(String s, Throwable e) {
logger.log(Level.SEVERE, s, e);
}
@Override
public void error(String s) {
logger.log(Level.SEVERE, s);
}
@Override
public void debug(String s) {
logger.log(Level.FINE, s);
}
@Override
public void trace(String s) {
logger.log(Level.FINER, s);
}
@Override
public void warn(String s) {
logger.log(Level.WARNING, s);
}
}
- 在MyBatis的配置文件中,配置使用
JdkLogger
作为日志实现。
<configuration>
<!-- 其他配置项 -->
<settings>
<setting name="logImpl" value="top.muquanyu.mybatis.JdkLogger"/>
</settings>
</configuration>
在上述配置中,logImpl
设置为top.muquanyu.mybatis.JdkLogger
,即使用我们自定义的JdkLogger
作为日志实现。
这样配置后,MyBatis的日志将会通过JDK自带的Logger进行输出。你可以根据需要对JdkLogger
进行扩展,以满足你的日志记录需求。
MyBatis 使用 STDOUT_LOGGING
STDOUT_LOGGING表示直接使用标准输出将日志信息打印到控制台
<configuration>
<!-- 其他配置项 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
public class TestMain {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before(){
try {
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(new FileInputStream("mybatis-config.xml"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void test(){
try(SqlSession sqlSession = sqlSessionFactory.openSession(true)){
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
System.out.println(mapper.getStudentBySidAndSex(1, "男"));
System.out.println(mapper.getStudentBySidAndSex(1, "男"));
}
}
}
MyBatis 使用 JUL
<setting name="logImpl" value="JDK_LOGGING" />
将其配置为JDK_LOGGING表示使用JUL进行日志打印,因为Mybatis的日志级别都比较低,因此我们需要设置一下logging.properties
默认的日志级别:
handlers= java.util.logging.ConsoleHandler
.level= ALL
java.util.logging.ConsoleHandler.level = ALL
@Log
public class TestMain {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before(){
try {
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(new FileInputStream("mybatis-config.xml"));
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(new FileInputStream("logging.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test(){
try(SqlSession sqlSession = sqlSessionFactory.openSession(true)){
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
log.info(mapper.getStudentBySidAndSex(1, "男").toString());
log.info(mapper.getStudentBySidAndSex(1, "男").toString());
}
}
}