如何格式化我的日志消息

20

我想在控制台打印出日志信息之前格式化它们Logger.fine

例如:如何格式化 "{0} 有 {1} 个苹果" 这个信息,输入为John和10。

我希望使用一个日志框架来解决这个问题,而不是单独对每个信息进行格式化。JDK6特定的日志类似乎没有这种粒度。


6
如果你使用 SLF4J,你可以这样写:log.debug("{} 带了 {} 个苹果", "John", 10); SLF4J 有一个绑定,会将日志输出到 java.util.logging - Tomasz Nurkiewicz
1
请重新表达问题。Tokenization 是将字符串分解成组成部分并生成输出而不是输入的过程。您的示例表明您正在询问如何格式化,而不是进行分词。 - Ray Toal
4个回答

26

使用MessageFormat

String s = MessageFormat.format("{0} has {1} apples with him", "John", 10);

或使用String.format方法:

String s = String.format("%1$s has %2$d apples with him", "John", 10);

2
对于像这样简单的情况,您可以省略 1$2$,并使用 String.format() 中的 "%s has %d apples with him" - Joachim Sauer
+1 for MessageFormat - 我能够很轻松地将所有的日志语句转换为使用MessageFormat。 - Julie
对我来说,这立即引发了这个相关的问题:https://dev59.com/WnE85IYBdhLWcg3wXCW1 - Jaap
不必要的,看下面David Groomes的回答就知道了。 - John Mikic

5
即使只使用JDK日志记录,您也可以使用自定义{{link1:Formatter}}来处理带参数的日志消息的格式化。
例如(简单示例):
public class MyFormatter extends Formatter {

    /**
     * @see java.util.logging.Formatter#format(java.util.logging.LogRecord)
     */
    @Override
    public String format(final LogRecord record) {
        return MessageFormat.format(record.getMessage(), record.getParameters());
    }
}

然后,要使用它,您可以配置一个包含以下内容的logging.properties文件:

java.util.logging.ConsoleHandler.formatter = com.example.MyFormatter

这不是必要的,请参见下面David Groomes的答案。 - John Mikic

4

只需使用String.format

String.format
   ("%s has %s apples with him", "John", "10");

直接使用String.format进行日志记录会有性能损失。 - Suryavanshi

2
JUL(java.util.logging)原生支持花括号样式的参数。您不需要第三方日志框架。以下是一个单类的工作示例(提示,我仍然建议使用SLF4J而不是JUL)。
import java.time.LocalDateTime;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

class JavaUtilLoggingExample {

    private static final Logger log = Logger.getLogger(JavaUtilLoggingExample.class.getName());

    /**
     * A java.util.logging (JUL) example adapted from the JavaDoc for java.text.MessageFormat.
     * See https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/text/MessageFormat.html
     *
     * Written for the StackOverflow question: https://dev59.com/Omw05IYBdhLWcg3wxkqr
     */
    public static void main(String[] args) {
        int planet = 7;
        String event = "a disturbance in the Force";

        log.log(Level.INFO, "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", new Object[]{planet, new Date(), event});

        // The following is printed (pretty good!):
        //
        //        Jan 02, 2022 8:06:59 PM JavaUtilLoggingExample main
        //        INFO: At 8:06:59 PM on Jan 2, 2022, there was a disturbance in the Force on planet 7

        // However, try to modernize your code and use java.time.LocalDateTime instead of java.util.Date and you will be
        // met with a surprise. Given this log statement:
        log.log(Level.INFO, "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", new Object[]{planet, LocalDateTime.now(), event});

        // The following is printed (not good!):
        //
        //        Jan 02, 2022 8:06:59 PM JavaUtilLoggingExample main
        //        INFO: At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.

        // Why? See this discussion. https://dev59.com/O8Dqa4cB1Zd3GeqPWBpp#66870717

        // By contrast, a similar (but less feature-ful) version of the above log statement written using the
        // SLF4J 'org.slf4j.Logger' class is below. It is much more succinct but it doesn't offer as many formatting
        // features.
        //
        // log.info("At {}, there was {} on planet {}.", new Date(), event, planet);

        // In conclusion, I continually wind back up with SLF4J in even my trivial Java programs despite my
        // once-yearly well-intentioned "Hey, why don't I reduce external dependencies as much as feasible. Can I just use JUL?"
        // efforts. The SLF4J API fits well in practice.
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接