【JAVA】日志打印java.util.logging.*函数自定义格式,并且显示调用时行号

1、JAVA自带的这样:

代码如下:

java 复制代码
import java.util.logging.*;
Logger logger = Logger.getLogger(MyLogger.class.toString());
logger.info("123");

显示效果:

这样的格式,看起来不太好看,比如:会默认添加一个换行,另外,想查看行号,会比较麻烦;随着开发代码量不断增加,因为一个函数代码量也比较大,如果知道行号,定位问题具体问题代码比较快,有点类似C语言的__LINE__宏,使用起来比较方便。

2、基于这个问题,我们需要自已实现可以显示行号的日志打印格式,通过网上查资料,知道要用到自定义日志格式设置:

java 复制代码
        logger = Logger.getLogger(MyLogger.class.getName());
        logger.setUseParentHandlers(false);
        //如果需要将日志文件写到文件系统中,需要创建一个FileHandler对象
        Handler consoleHandler = new ConsoleHandler();

        //创建日志格式文件:本次采用自定义的Formatter
        consoleHandler.setFormatter(new MyFormatter());

        //将FileHandler对象添加到Logger对象中
        logger.addHandler(consoleHandler);

在MyFormatter中实现对格式的自定义:

java 复制代码
 @Override
    public String format(LogRecord arg0)
    {
        //创建StringBuilder对象来存放后续需要打印的日志内容
        StringBuilder builder = new StringBuilder();

        //获取时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        Date now = new Date();
        String dateStr = simpleDateFormat.format(now);

        builder.append("[");
        builder.append(dateStr);
        builder.append(" ");

        //拼接日志级别
        builder.append(arg0.getLevel()).append(" ");

        builder.append(arg0.getSourceClassName()).append(" ");

        //拼接方法名
        builder.append(arg0.getSourceMethodName()).append(" ");

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String line = stackTrace[8].toString();
        String lineNumber = line.substring(line.indexOf(":") + 1, line.length() - 1);
        //System.out.println("stackTrace[0].toString(): " + stackTrace[0].toString());

        //拼接方法名
        builder.append(lineNumber).append("] ");

        //拼接日志内容
        builder.append(arg0.getMessage());

        //日志换行
        builder.append("\r\n");

        return builder.toString();
    }

这里边额外使用了获取当前线程的堆栈跟踪元素:Thread.currentThread().getStackTrace(),在程序执行过程中,可以把当前位置的调用栈打印出来;

如果在当前的文件,由于调用栈深度不深,代码如下:

java 复制代码
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        System.out.println(stackTrace.length);
        for (StackTraceElement s: stackTrace) {
            System.out.println(s.toString());
        }

这样打印出来是这样的:

3、在实际运行的代码涉及跨类的调用,代码:

java 复制代码
/**
 * 可以自已定义日志打印格式,这样看起来比较方便些
 *
 */
class MyFormatter extends Formatter
{
    @Override
    public String format(LogRecord arg0)
    {
        //创建StringBuilder对象来存放后续需要打印的日志内容
        StringBuilder builder = new StringBuilder();

        //获取时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        Date now = new Date();
        String dateStr = simpleDateFormat.format(now);

        builder.append("[");
        builder.append(dateStr);
        builder.append(" ");

        //拼接日志级别
        builder.append(arg0.getLevel()).append(" ");

        builder.append(arg0.getSourceClassName()).append(" ");

        //拼接方法名
        builder.append(arg0.getSourceMethodName()).append(" ");

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String line = stackTrace[8].toString();
        String lineNumber = line.substring(line.indexOf(":") + 1, line.length() - 1);
        //System.out.println("stackTrace[0].toString(): " + stackTrace[0].toString());

        StackTraceElement[] stackTrace2 = Thread.currentThread().getStackTrace();
        System.out.println(stackTrace2.length);
        for (StackTraceElement s: stackTrace2) {
            System.out.println(s.toString());
        }
        //拼接方法名
        builder.append(lineNumber).append("] ");

        //拼接日志内容
        builder.append(arg0.getMessage());

        //日志换行
        builder.append("\r\n");

        return builder.toString();
    }
}

这样看到的调用栈较深些:

这样在取我们自已代码的入口的时候,就需要从这上边往下边数,因为这里边代码是一个数组,从上图中打印也可看出来,前几个是日志打印的栈

java 复制代码
StackTraceElement[] stackTrace2 = Thread.currentThread().getStackTrace();
        System.out.println(stackTrace2.length);
        for (StackTraceElement s: stackTrace2) {
            System.out.println(s.toString());
        }

从上图可以看出,我们代码在调用日志打印的时候,实际上使用的是stackTrace2数组里边第9个成员,由于我们需要取出行号,那么使用的是JAVA对字符串操作的搜索和截取操作,这样就可以达到目的:

java 复制代码
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String line = stackTrace[8].toString();
        String lineNumber = line.substring(line.indexOf(":") + 1, line.length() - 1);

由于我们可以自定义打印日志的格式,那么我们整体按照自已的要求自定义后,代码是这样子的:

java 复制代码
class MyFormatter extends Formatter
{
    @Override
    public String format(LogRecord arg0)
    {
        //创建StringBuilder对象来存放后续需要打印的日志内容
        StringBuilder builder = new StringBuilder();

        //获取时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        Date now = new Date();
        String dateStr = simpleDateFormat.format(now);

        builder.append("[");
        builder.append(dateStr);
        builder.append(" ");

        //拼接日志级别
        builder.append(arg0.getLevel()).append(" ");

        builder.append(arg0.getSourceClassName()).append(" ");

        //拼接方法名
        builder.append(arg0.getSourceMethodName()).append(" ");

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String line = stackTrace[8].toString();
        String lineNumber = line.substring(line.indexOf(":") + 1, line.length() - 1);

        //拼接方法名
        builder.append(lineNumber).append("] ");

        //拼接日志内容
        builder.append(arg0.getMessage());

        //日志换行
        builder.append("\r\n");

        return builder.toString();
    }
}

最终的打印效果如下图所示:

自定义日志打印完成类代码如下:

java 复制代码
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.*;

/**
 * 可以自已定义日志打印格式,这样看起来比较方便些
 *
 */
class MyFormatter extends Formatter
{
    @Override
    public String format(LogRecord arg0)
    {
        //创建StringBuilder对象来存放后续需要打印的日志内容
        StringBuilder builder = new StringBuilder();

        //获取时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        Date now = new Date();
        String dateStr = simpleDateFormat.format(now);

        builder.append("[");
        builder.append(dateStr);
        builder.append(" ");

        //拼接日志级别
        builder.append(arg0.getLevel()).append(" ");

        builder.append(arg0.getSourceClassName()).append(" ");

        //拼接方法名
        builder.append(arg0.getSourceMethodName()).append(" ");

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        String line = stackTrace[8].toString();
        String lineNumber = line.substring(line.indexOf(":") + 1, line.length() - 1);

        //拼接方法名
        builder.append(lineNumber).append("] ");

        //拼接日志内容
        builder.append(arg0.getMessage());

        //日志换行
        builder.append("\r\n");

        return builder.toString();
    }
}

public class MyLogger {
    static Logger logger;

    static  {
        logger = Logger.getLogger(MyLogger.class.getName());
        logger.setUseParentHandlers(false);
        //如果需要将日志文件写到文件系统中,需要创建一个FileHandler对象
        Handler consoleHandler = new ConsoleHandler();

        //创建日志格式文件:本次采用自定义的Formatter
        consoleHandler.setFormatter(new MyFormatter());

        //将FileHandler对象添加到Logger对象中
        logger.addHandler(consoleHandler);
    }

    public static Logger getLogger() {
        return logger;
    }
}
相关推荐
3GPP仿真实验室9 分钟前
【Matlab源码】6G候选波形:OFDM-IM 增强仿真平台 DM、CI
开发语言·matlab·ci/cd
devmoon13 分钟前
在 Polkadot 上部署独立区块链Paseo 测试网实战部署指南
开发语言·安全·区块链·polkadot·erc-20·测试网·独立链
lili-felicity13 分钟前
CANN流水线并行推理与资源调度优化
开发语言·人工智能
爬山算法14 分钟前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
沐知全栈开发14 分钟前
CSS3 边框:全面解析与实战技巧
开发语言
island131424 分钟前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构 Stream 调度机制
c语言·开发语言·神经网络
云姜.26 分钟前
线程和进程的关系
java·linux·jvm
是码龙不是码农28 分钟前
支付防重复下单|5 种幂等性设计方案(从初级到架构级)
java·架构·幂等性
曹牧28 分钟前
Spring Boot:如何在Java Controller中处理POST请求?
java·开发语言
heartbeat..28 分钟前
JVM 性能调优流程实战:从开发规范到生产应急排查
java·运维·jvm·性能优化·设计规范