【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;
    }
}
相关推荐
Hello-Mr.Wang9 小时前
【保姆级教程】MasterGo MCP + Cursor 一键实现 UI 设计稿还原
前端·javascript·vue.js·ai编程
过期动态10 小时前
MySQL中的约束
android·java·数据库·spring boot·mysql
wxin_VXbishe10 小时前
springboot新能源车充电站管理系统小程序-计算机毕业设计源码29213
java·c++·spring boot·python·spring·django·php
宁雨桥10 小时前
前端修行日记之JS 原型与 AI基础常识
前端·javascript·原型模式
程序员陆通10 小时前
月烧 400 刀到不到 20 刀:我是怎么把 OpenClaw 的 Token 账单砍掉 95% 的
java·前端·数据库
水云桐程序员10 小时前
前端教程官方文档|HTML、CSS、JavaScript教程官方文档
前端·javascript·css·html·学习方法
本末倒置18310 小时前
Vue 3 开发者转型 React 指南:保姆级教程
前端·javascript·vue.js
江南十四行10 小时前
Python生成器与协程:从迭代器到异步编程的进阶之路
开发语言·python
代码漫谈11 小时前
一文学习 SpringBoot 的 application.yml 配置,基于 Spring Boot 3.2.x
java·spring boot·spring·配置文件
SamDeepThinking11 小时前
程序员如何接受工作内容毫无意义?
java·后端·程序员