JAVA异常和日志

未捕获异常

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"}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值