Paul
Paul

Reputation: 1061

Custom handler not printing to stdout

Java Logging doesn't work at all as expected. Either it prints to the stderr, either it prints nothing, either it doesn't take configuration into account. What the hell is going on?

I've used it in many projects but it's the first time it behaves like this without any explanation. Here is the code.


Custom Handler to set level, formatter and outputstream to 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();
        }
    }
}

Custom formatter

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()
            );
        }
    }

}

Custom formatter for log files

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()
            );
        }
    }

}

A service for handling all of this.

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;
    }
}

Now in a simple 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");
    }

Prints only (from the method LogService#getLoggerWithLogFile) :

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

And configuring logs like this doesn't seem to works since there is stderr logs from external libraries showing in the console.

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);

Why the configuration is not taken into account? Why there is nothing in the stdout?

Upvotes: 1

Views: 33

Answers (1)

Paul
Paul

Reputation: 1061

Another module was using a class named "LogService" thus probably conflicting with the one described above. Removing the class solved the issue.

Upvotes: 1

Related Questions