未捕获异常
Runnable 未捕获异常
@Slf4j
public class RunnableDemo implements Runnable{
boolean flag;
public RunnableDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
log.info("进入runnableDemo");
if(flag){
throw new NumberFormatException("RunnableDemo测试异常");
}
log.info("进入runnableDemo 结束");
}
}
以下日志打印在控制,不打印在日志文件中
10:09:17.110 [Thread-0] INFO c.z.d.exception.utils.RunnableDemo - 进入runnableDemo
Exception in thread "Thread-0" java.lang.NumberFormatException: RunnableDemo测试异常
at com.zhou.demo.exception.utils.RunnableDemo.run(RunnableDemo.java:29)
at java.lang.Thread.run(Thread.java:745)
因为其实执行的是 java.lang.ThreadGroup#uncaughtException;输出日志是System.err.print,所以不会输出到日志文件。
线程池未捕获异常
线程池会捕获任务抛出的异常和错误,处理策略会受到我们提交任务的方式而不同。
pool.execute
public static void testThreadPoolExec(Boolean flag){
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread thread = new Thread(new RunnableDemo(flag));
executorService.execute(thread);
}
只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler
实际也是执行 java.lang.ThreadGroup#uncaughtException
效果类似,打印在控制台不打印在日志文件
pool.submit
public static void testThreadPoolSumbit(Boolean flag){
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread thread = new Thread(new RunnableDemo(flag));
//TODO 没有以下,错误日志不会打印到控制台 e.printStackTrace()不会打印到logback日志 打印到日志使用log.error
Future<?> submit = executorService.submit(thread);
try {
submit.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
而通过submit提交的任务,submit()方式提交的任务会返给我们一个Future,无论是抛出的未检测异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中重新抛出。
UncaughtExceptionHandler
实现Thread的UncaughtExceptionHandler接口
@Slf4j
public class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Exception in thread {}", t.getName(), e);
}
}
###线程
Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler)
//设置全局的默认异常处理机制
Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)
除了线程池的sumbit方法,其他都按照MyExceptionHandler的log error打印到日志中了。
@Slf4j
public class RunnableHnadlerDemo implements Runnable{
boolean flag;
public RunnableHnadlerDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
log.info("进入runnableDemo");
if(flag){
throw new NumberFormatException("RunnableDemo测试异常");
}
log.info("进入runnableDemo 结束");
}
public static void testRunnable(Boolean flag){
Thread thread = new Thread(new RunnableDemo(flag));
thread.start();
}
public static void testThreadPoolExec(Boolean flag){
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread thread = new Thread(new RunnableDemo(flag));
executorService.execute(thread);
}
public static void lambadThread(){
Thread thread = new Thread(() -> {
log.info("进入 lambadThread");
if (true) {
throw new NumberFormatException("lambadThread测试异常");
}
log.info("进入lambadThread 结束");
});
thread.start();
}
public static void testThreadPoolSumbit(Boolean flag){
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread thread = new Thread(new RunnableDemo(flag));
Future<?> submit = executorService.submit(thread);
}
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());
//testRunnable(true);
//testThreadPoolExec(true);
//lambadThread();
testThreadPoolSumbit(true);
}
### 线程池
线程池的UncaughtExceptionHandler我们需要一个个设置,借助工具Apache Commons 和 Google Guava,可以实现,参考。
结论:
线程中未捕获异常不会出现的日志文件!!!
线程池中sumbit提交的任务,异常封装在ExecutionException,不判断结果类方法不会输出异常!!!
所以一定要注意对线程中异常的处理,不然任务可能失败的毫无声息。
使用CompletableFuture时候,使用handle或者exceptionally;以便以处理有可能的异常
## handle 异常返回handle的值,正常会返回正常值
### 当运行时出现了异常,可以通过exceptionally进行补偿
public static void testFutureR(Boolean flag){
CompletableFuture.runAsync(() -> {
log.info("进入CompletableFuture");
if (flag) {
throw new RuntimeException("测试异步异常");
}
log.info("进入CompletableFuture结束");
}).whenComplete((r, e) ->{
if(e != null){
log.error("执行未完成出现异常", e);
}
}).exceptionally(e -> {
log.error("exceptionally出现异常", e);
return null;
}).handle((t, e) -> {
log.error("hander 处理异常", e);
return null;
}).join();
}
日志记录和nohup
Linux的3种重定向 0:表示标准输入 1:标准输出,在一般使用时,默认的是标准输出 2:标准错误信息输出
可以使用nohup执行命令,将日志输出到文件,但是这样缺乏日志框架对日志的细致处理,控制单个日志的大小和控制总体的日志数量;
nohup产生的日志文件常常会太大占满磁盘或者不方便打开
nohup java -jar XX.jar> console.out 2>&1 &
方法1 安装cronolog切割日志
方法2 命令行切割日志,然后清空日志
cat /dev/null > nohup.out
方法3 不重定向,输出logBack日志文件,问题 未捕获异常
## 什么日志也不要
nohup java -jar XX.jar> /dev/null 2>&1 &
方法4 只输出错误日志到nohup日志文件
nohup java -jar XX.jar >/dev/null 2>log &
问题: 错误日志和正常日志对不上,不方便排查问题
综上,可以考虑强制对异常进行处理,一劳永逸 。
Add 日志级别控制
All < Trace < Debug < Info < Warn < Error < Fatal < OFF
设置日志级别
LoggingSystem loggingSystem = LoggingSystem.get(ClassLoader.getSystemClassLoader());
loggingSystem.setLogLevel(log.getName(), LogLevel.INFO);
SpringBoot2.x 引入包,设置如下
management:
endpoints:
web:
exposure:
include: loggers,health,info
访问,check 所有日志级别
http://localhost:10112/actuator/loggers
查询指定路径日志级别
http://localhost:10112/actuator/loggers/com.demo.package.class
post请求修改 日志级别
http://localhost:10112/actuator/loggers/com.demo.package.class
body {"configuredLevel":"trace"}