如何为java.util.logging每条日志语句添加自定义上下文字段
给java.util.logging日志添加上下文信息(以俄罗斯方块得分为例)
嘿,针对你想用标准java.util.logging给俄罗斯方块日志加上当前得分这类上下文的需求,我整理了几个实用的方案,全都是基于JDK标准库实现的,不用额外引入依赖:
方案1:ThreadLocal存上下文 + 自定义Formatter(推荐)
这是最灵活的方式,适合需要在多个日志点共享上下文的场景:
第一步:做个上下文存储工具类
先搞个简单的类,用ThreadLocal来存当前游戏的得分,保证线程安全(哪怕以后做多人联机多线程游戏也能兼容):
public class GameContext { // 用ThreadLocal存储当前线程的得分 private static final ThreadLocal<Integer> currentScore = new ThreadLocal<>(); // 设置当前得分 public static void setCurrentScore(int score) { currentScore.set(score); } // 获取当前得分 public static Integer getCurrentScore() { return currentScore.get(); } // 线程结束时清理,避免内存泄漏 public static void clear() { currentScore.remove(); } }
第二步:自定义日志格式器
继承SimpleFormatter,重写format方法,把得分上下文拼到日志里:
import java.util.logging.LogRecord; import java.util.logging.SimpleFormatter; public class GameLogFormatter extends SimpleFormatter { @Override public String format(LogRecord record) { // 从上下文拿得分,没拿到就显示0 Integer score = GameContext.getCurrentScore(); String scoreTag = (score != null) ? "[Score: " + score + "]" : "[Score: 0]"; // 先拿到父类生成的原始日志,再把得分标签加在前面 String originalLog = super.format(record); return scoreTag + " " + originalLog; } }
第三步:给Logger配置自定义Formatter
在你的Tetris类里,把默认的Handler换成咱们自定义的,这样所有日志都会带上得分:
package org.tester; import java.util.logging.ConsoleHandler; import java.util.logging.Logger; public class Tetris implements HighScoreGame { static Logger logger = Logger.getLogger(Tetris.class.getName()); static { // 先关掉父Logger的Handler,避免重复输出日志 logger.setUseParentHandlers(false); // 创建控制台Handler,设置咱们的自定义Formatter ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setFormatter(new GameLogFormatter()); // 把Handler加到Logger上 logger.addHandler(consoleHandler); } private int score = 0; @Override public void addScore(int points) { score += points; // 更新上下文里的得分 GameContext.setCurrentScore(score); logger.info("Added " + points + " points to score"); } // 比如移动方块的日志 public void moveBlockDown() { // 每次打日志前确保上下文是最新的得分 GameContext.setCurrentScore(score); logger.fine("Moved game block down one row"); } // 游戏结束记得清理上下文 public void gameOver() { logger.info("Game over! Final score recorded"); GameContext.clear(); } }
方案2:直接给LogRecord传上下文参数
如果不想用ThreadLocal,也可以每次打日志的时候把得分作为参数传进去,然后在Formatter里读取:
自定义Formatter读取参数
public class GameLogFormatter extends SimpleFormatter { @Override public String format(LogRecord record) { // 从LogRecord的参数里拿得分 Integer score = (Integer) record.getParameters()[0]; String scoreTag = "[Score: " + score + "]"; String originalLog = super.format(record); return scoreTag + " " + originalLog; } }
打日志时传入得分
public void addScore(int points) { score += points; // 把得分作为第二个参数传给log方法 logger.log(Level.INFO, "Added " + points + " points", score); }
这个方式的缺点是每次打日志都要手动传得分,适合上下文不频繁变化的场景。
小提示
- 如果是多线程场景,一定要记得在任务结束时调用
GameContext.clear(),避免ThreadLocal内存泄漏。 - 也可以通过修改
logging.properties配置文件来全局设置自定义Formatter,这样不用在代码里手动配置Handler:handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=org.tester.GameLogFormatter
这样你的每条日志就都会带上当前的得分上下文啦!
内容的提问来源于stack exchange,提问作者Caitlin Rainone
相关产品推荐
相关产品推荐

