java.util.logging简介

最近做Veracode Scan,为了解决Improper Output Neutralization for Logs (CWE ID 117)问题,涉及到jdk自带log的一些内容做部分记录。


一、Logger

核心的java.util.logging.Logger类,用于输出log。

// 常规的得到logger的方法
Logger logger = Logger.getLogger(LoggerDemo.class.getName());
System.out.println(logger);

Logger rootLogger = Logger.getLogger("");
System.out.println(rootLogger);
输出:
java.util.logging.Logger@5ca881b5
java.util.logging.LogManager$RootLogger@24d46ca6

可以看到通过Logger的静态方法getLogger(String name)得到Logger

  • 带参数的返回的是java.util.logging.Logger
  • 参数为空字符串的时候返回的是java.util.logging.LogManager$RootLogger

实际上所有的通过此法得到的Logger都是由LogManager统一管理的,也就是通过name参数保证每个Logger的唯一性。所有创建的Logger都带有parent成员变量指向同一个RootLogger。当新建的Logger绑定handlers的时候可以通过调用父级Loggerhandler进行输出log
Logger成员变量
Handler遍历方式是从自己到父级所有Handler

public void log(LogRecord record) {
        if (!isLoggable(record.getLevel())) {
            return;
        }
        Filter theFilter = filter;
        if (theFilter != null && !theFilter.isLoggable(record)) {
            return;
        }

        // Post the LogRecord to all our Handlers, and then to
        // our parents' handlers, all the way up the tree.

        Logger logger = this;
        while (logger != null) {
        //获得自己绑定的Handler
            final Handler[] loggerHandlers = isSystemLogger
                ? logger.accessCheckedHandlers()
                : logger.getHandlers();
		//遍历调用自己绑定的Handler
            for (Handler handler : loggerHandlers) {
                handler.publish(record);
            }
		//是否使用parent的Handler
            final boolean useParentHdls = isSystemLogger
                ? logger.useParentHandlers
                : logger.getUseParentHandlers();

            if (!useParentHdls) {
                break;
            }
		//判断是否有parent是否有,如有则得到parent并循环获取Handler
            logger = isSystemLogger ? logger.parent : logger.getParent();
        }
    }

二、Handler

每一次log输出都是由Handler完成

Logger logger = Logger.getLogger(LoggerDemo.class.getName());
logger.info("This is a test message!!!");
输出:
May 05, 2020 9:49:08 PM com.LoggerDemo main
INFO: This is a test message!!!

这里在创建getLogger的时候没有绑定任何handler,所以没有自己的handler,输出时调用parent绑定的handlerRootLogger默认会绑定一个ConsoleHandler。而ConsoleHandler的默认输出是System.err

    public ConsoleHandler() {
        sealed = false;
        configure();
        setOutputStream(System.err);
        sealed = true;
    }

JDK自带Handler如下:
Handler

三、Formatter

Handler中可以绑定一个Formatter来处理输出内容的格式。
依然ConsoleHandler为例,默认绑定的FormatterSimpleFormatter

	private void configure() {
        LogManager manager = LogManager.getLogManager();
        String cname = getClass().getName();

        setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
        setFilter(manager.getFilterProperty(cname +".filter", null));
        //这里设置默认的Formatter,由此可见可从配置中指定自己的formatter,通过properties文件配置的方式后续介绍。
        setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
        try {
            setEncoding(manager.getStringProperty(cname +".encoding", null));
        } catch (Exception ex) {
            try {
                setEncoding(null);
            } catch (Exception ex2) {
                // doing a setEncoding with null should always work.
                // assert false;
            }
        }
    }

先看下SimpleFormatter的内容:

	public synchronized String format(LogRecord record) {
        dat.setTime(record.getMillis());
        String source;
        if (record.getSourceClassName() != null) {
            source = record.getSourceClassName();
            if (record.getSourceMethodName() != null) {
               source += " " + record.getSourceMethodName();
            }
        } else {
            source = record.getLoggerName();
        }
        String message = formatMessage(record);
        String throwable = "";
        if (record.getThrown() != null) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            pw.println();
            record.getThrown().printStackTrace(pw);
            pw.close();
            throwable = sw.toString();
        }
        //这里是核心的format,将所有要输出的参数通过String.format的方式格式化。
        return String.format(format,
                             dat,
                             source,
                             record.getLoggerName(),
                             record.getLevel().getLocalizedLevelName(),
                             message,
                             throwable);
    }

看到这个我们就可以自己定义一个Formatter尝试。

		//得到Logger
        Logger logger = Logger.getLogger(LoggerDemo.class.getName());
        //禁用父级handler
        logger.setUseParentHandlers(false);
        //声明自己的handler
        Handler handler = new ConsoleHandler();
        //设置Formatter
        handler.setFormatter(new Formatter() {
            @Override
            public String format(LogRecord record) {
	            //指定格式具体内容请参考String的API,简单的说 %+第n个参数,$+想要的格式。
                return String.format("%1$tl:%1$tM:%1$tS %2$s %3$s %4$s: %n%5$s%n",
                        new Date(),
                        record.getLoggerName(),
                        record.getSourceMethodName(),
                        record.getLevel(),
                        record.getMessage()
                        //没有详细写抛出异常的情况,参考源码很容易理解
                        );
            }
        });
        logger.addHandler(handler);
        logger.info("This is a test message!!!");
输出:
10:17:53 com.LoggerDemo main INFO: 
This is a test message!!!

可以比较前面的输出作为对照,log的格式已经变了。

四、LogManager

前面我们已经通过代码自定义了自己想要的Logger处理器。通过LogManager可以读取.properties的方式设置。

如下配置即可为ConsoleHandler添加Formatter

handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

LogManager装载配置

LogManager.getLogManager().readConfiguration(ClassLoader.getSystemResourceAsStream("logging_default.properties"));

五、后记

还有一些Filter类也都大同小异的,不再赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值