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

Java自定义日志处理器无法输出至Stdout且日志配置不生效问题求助

Java自定义日志处理器无法输出至Stdout且日志配置不生效问题求助

Java Logging完全不像预期那样工作。它要么输出到stderr,要么什么都不打印,要么完全不读取配置。到底是怎么回事?

我在很多项目里用过它,但这是第一次遇到这种毫无缘由的异常情况。以下是相关代码:


自定义Handler(设置日志级别、格式化器并输出到stdout):

public class CustomHandler extends StreamHandler {

    public CustomHandler(Level level) {
        super(System.out, new CustomFormatter());
        setLevel(level);
    }

    @Override
    public void publish(LogRecord record) {
        if (LogService.getInstance().publishExternalLogs() || record.getSourceClassName().startsWith(
            "com.my.package")) {
            super.publish(record);
            super.flush();
        }
    }
}

自定义控制台日志格式化器:

public class CustomFormatter extends Formatter {

    private static final String FORMAT_WITH_MESSAGE =
        "%1$tF %1$tT - %2$-7s - %3$s%n";

    private static final String FORMAT_WITHOUT_MESSAGE =
        "%1$tF %1$tT - %2$-7s%n";

    private String replaceDefaultLevel(Level level) {
        switch (level.intValue()) {
            case 300: // Level.FINEST
                return "\u001B[34mTRACE\u001B[0m";
            case 400: // Level.FINER
                return "\u001B[34mDEBUG\u001B[0m";
            case 500: // Level.FINE
                return "\u001B[34mINFO\u001B[0m";
            case 800: // Level.INFO
                return "\u001B[34mINFO\u001B[0m";
            case 900: // Level.WARNING
                return "\u001B[33mWARNING\u001B[0m";
            case 1000: // Level.SEVERE
                return "\u001B[31mSEVERE\u001B[0m";
            default:
                return level.getLocalizedName();
        }
    }

    @Override
    public synchronized String format(LogRecord logRecord) {
        if (logRecord.getMessage() == null || logRecord
            .getMessage()
            .isEmpty()) {
            return String.format(
                FORMAT_WITHOUT_MESSAGE,
                new Date(logRecord.getMillis()),
                replaceDefaultLevel(logRecord.getLevel())
            );
        }
        else {
            return String.format(
                FORMAT_WITH_MESSAGE,
                new Date(logRecord.getMillis()),
                replaceDefaultLevel(logRecord.getLevel()),
                logRecord.getMessage()
            );
        }
    }

}

日志文件专用自定义格式化器:

public class CustomFileFormatter extends SimpleFormatter {

    private static final String FORMAT_WITH_MESSAGE =
        "%1$tF %1$tT - %2$-7s - %3$s%n";

    private static final String FORMAT_WITHOUT_MESSAGE =
        "%1$tF %1$tT - %2$-7s%n";

    @Override
    public synchronized String format(LogRecord logRecord) {
        if (logRecord.getMessage() == null || logRecord
            .getMessage()
            .isEmpty()) {
            return String.format(
                FORMAT_WITHOUT_MESSAGE,
                new Date(logRecord.getMillis()),
                logRecord.getLevel().toString()
            );
        }
        else {
            return String.format(
                FORMAT_WITH_MESSAGE,
                new Date(logRecord.getMillis()),
                logRecord.getLevel().toString(),
                logRecord.getMessage()
            );
        }
    }

}

日志管理服务类:

public class LogService {

    private static final Logger LOGGER_INFO =
        LogService.getInstance().getLogger(
            LogService.class.getCanonicalName(),
            Level.INFO
        );

    private static class LogServiceInstance {

        static final LogService INSTANCE = new LogService();
    }

    public static LogService getInstance() {
        return LogServiceInstance.INSTANCE;
    }
    /*
     * Show external libraries logs?
     */
    private boolean publishExternalLogs;

    private LogService() {
        this.publishExternalLogs = true;
    }

    public void init(String config) {
        try {
            LogManager
                .getLogManager()
                .readConfiguration(new java.io.ByteArrayInputStream(config.getBytes()));
        }
        catch (IOException ioe) {
            throw new RuntimeException(
                "Error while trying to configure logging.",
                ioe
            );
        }
    }

    public Logger getLogger(String className, Level level) {
        Logger logger = Logger.getLogger(String.format(
            "%s_%s",
            className,
            level.toString()
        ));
        logger.setUseParentHandlers(false);
        logger.setLevel(level);
        logger.addHandler(new CustomHandler(level));
        return logger;
    }

    public boolean publishExternalLogs() {
        return this.publishExternalLogs;
    }

    public Logger getLoggerWithLogFile(String className, Level level) {
        Logger logger = getLogger(className, level);
        String logPath = System.getenv("LOG_PATH");
        if (logPath == null) {
            logPath = new File(".").getAbsolutePath().replaceAll("\\.", "");
        }
        try {
            String[] split = className.split("\\.");
            String logFile = String.format(
                "%s%s_%s" + ".log",
                logPath,
                split[split.length - 1],
                level
            );
            LOGGER_INFO.info(String.format(
                "Creating logger file : %s",
                logFile
            ));
            FileHandler fileHandler = new FileHandler(logFile);
            fileHandler.setFormatter(new CustomFileFormatter());
            logger.addHandler(fileHandler);
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        return logger;
    }

    public void setLevel(Level level, boolean publishExternalLogs) {
        Logger root = Logger.getLogger("");
        root.setLevel(level);
        this.publishExternalLogs = publishExternalLogs;
    }
}

测试用的Main方法:

private static final Logger LOGGER_INFO = LogService
    .getInstance()
    .getLoggerWithLogFile(App.class.getCanonicalName(), Level.INFO);

public static void main(String[] args) {
    LogService.getInstance().setLevel(Level.FINEST, false);
    LOGGER_INFO.info("Abcd");
    LOGGER_INFO.info("Abcd");
    LOGGER_INFO.info("Abcd");
    LOGGER_INFO.info("Abcd");
    LOGGER_INFO.info("Abcd");
}

实际输出结果:

实际运行后,控制台只输出了来自LogService#getLoggerWithLogFile方法的这一行日志:

2025-02-12 14:27:02 - INFO - Creating logger file : C:\Users\user\Workspace\myapp\App_INFO.log

日志配置不生效的问题:

另外,像这样配置日志似乎也不起作用——外部库的stderr日志仍然会显示在控制台:

String logConfig =
            "java.util.logging.FileHandler" + ".limit=" + 50000 + "\n";
        logConfig += "java.util.logging.FileHandler" + ".count=" + 5 + "\n";
        //set your custom levels
        logConfig += "jdk.event.security" + ".level=" + Level.SEVERE + "\n";
        logConfig += "org.glassfish.grizzly" + ".level=" + Level.SEVERE + "\n";
        logConfig += "org.glassfish.tyrus" + ".level=" + Level.SEVERE + "\n";
LogService.getInstance().init(logConfig);

核心疑问:

为什么配置不生效?为什么stdout里没有预期的"Abcd"日志输出?


备注:内容来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 03:20:31