You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java util Logging生成.lck文件及日志类名行号异常问题咨询

问题原因分析

1. .lck文件生成原因

java.util.logging(JUL)的FileHandler为了避免多进程同时写入同一个日志文件引发冲突,会自动为打开的日志文件生成对应的.lck锁文件。正常情况下JVM优雅退出时FileHandler会自动释放锁并删除该文件,若JVM异常崩溃、未主动关闭FileHandler就会导致锁文件残留。

2. 日志行号/类名显示错误原因

JUL的日志记录器默认会抓取直接调用Logger输出方法的代码位置作为日志来源,你的业务代码调用的是LogUtility.info(),最终是在LogUtility类内部调用的logger.info(),所以JUL只会识别到LogUtility的位置,无法向上追溯到实际的业务调用方。

优化解决方案

核心优化点

  • 调整初始化顺序,保证日志路径提示信息可以正常写入文件
  • 增加JVM关闭钩子,主动关闭FileHandler避免锁文件残留
  • 优化日志输出逻辑,自动抓取实际业务调用方的类名、方法名、行号
  • 兼容跨平台路径,避免仅支持Windows系统
  • 新增日志目录自动创建逻辑,避免目录不存在时写入报错

优化后代码

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class LogUtility {
    private static final LogUtility INSTANCE = new LogUtility();
    private final Logger logger = Logger.getLogger("LogUtility");
    private FileHandler fileHandler;

    private LogUtility() {
        // 优先用跨平台通用的用户目录获取方式
        String userHomePath = System.getProperty("user.home");
        String logDirPath;
        // Windows系统单独适配原有日志路径
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            logDirPath = userHomePath + File.separator + "AppData" + File.separator + "LocalLow" + File.separator + "Sun" + File.separator + "Java" + File.separator + "Deployment" + File.separator + "log";
        } else {
            // 非Windows系统默认在用户目录下创建.applogs文件夹存日志
            logDirPath = userHomePath + File.separator + ".applogs";
        }
        // 自动创建不存在的日志目录
        File logDir = new File(logDirPath);
        if (!logDir.exists()) {
            logDir.mkdirs();
        }
        try {
            String dateTimeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
            String logFilePath = logDirPath + File.separator + "app_" + dateTimeStamp + ".log";
            // 第二个参数控制是否追加写入,false表示每次启动生成新日志文件,可按需调整
            fileHandler = new FileHandler(logFilePath, false);
            fileHandler.setFormatter(new SimpleFormatter());
            logger.addHandler(fileHandler);
            // 初始化完成后再打印路径,确保日志写入文件
            info("Log Path -->" + logFilePath);
        } catch (SecurityException | IOException e) {
            e.printStackTrace();
        }
        // 注册JVM关闭钩子,退出时主动释放文件锁
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            if (fileHandler != null) {
                fileHandler.close();
            }
        }));
    }

    public static LogUtility getInstance() {
        return INSTANCE;
    }

    public void info(String msg) {
        // 遍历堆栈获取实际业务调用方信息
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        // stackTrace[0]是getStackTrace方法本身,stackTrace[1]是当前info方法,stackTrace[2]就是业务调用方
        StackTraceElement caller = stackTrace[2];
        // 用logp方法手动指定调用方信息,覆盖默认的类名识别逻辑
        logger.logp(Level.INFO, caller.getClassName(), caller.getMethodName(), "Line " + caller.getLineNumber() + ": " + msg);
    }
}

额外优化建议

  • 如果项目允许引入第三方依赖,建议替换为SLF4J+Logback的日志框架,比原生JUL功能更丰富、配置更灵活,不需要自行处理行号识别、锁文件这类底层问题
  • 可以自定义Formatter修改日志输出格式,调整为更易读的结构化格式,比如固定顺序输出时间、日志级别、类名、方法名、行号、消息内容

内容的提问来源于stack exchange,提问作者Karthikeyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 13:45:07