1

I am trying to create a Custom Logger. My code is given below and the Properties file is also given below.

It works in the sense that I get log messages and details in the log file in the format I want. However, all logs are logged with same ClassName - MyLogger. I want the 'name' I am passing in the constructor to be logged as ClassName instead of the MyLogger ClassName. Appreciate any help in that regard.

Java Code:

    public class MyLogger {

    private static Logger log;
    
    public static MyLogger getLogger(String name) {
        return new MyLogger(name);
    }

    private MyLogger(String name) {
        log = Logger.getLogger(name);
        try {
            LogManager.getLogManager().readConfiguration(new FileInputStream("./logging.properties"));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException | SecurityException ex) {
            Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void log(Level LogLevel, String logMessage, Object param1) {
        log.log(LogLevel, logMessage, param1);
    }

    public void log(Level LogLevel, String logMessage, Throwable e) {
        log.log(LogLevel, logMessage, e);
    }

    public void log(Level LogLevel, String logMessage) {
        log.log(LogLevel, logMessage);
    }
}

logging.properties File content:

handlers = java.util.logging.FileHandler
java.util.logging.FileHandler.level     = ALL
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$s %2$s %5$s%6$s%n
java.util.logging.FileHandler.limit     = 1048576
java.util.logging.FileHandler.count     = 5
java.util.logging.FileHandler.append    = true
java.util.logging.FileHandler.pattern   = ./logs/log-%u-%g.log

--

4
  • I don't understand. You have a MyLogger but you show the properties for FileHander? Do you have the part where you configure your logger? EDIT: also where do you use this class? Commented Aug 30, 2021 at 1:49
  • 2
    Why is the log field static? Every time you create a new instance that field will be overridden for all previous instances. Also, this MyLogger class seems redundant; unless there are other convenience methods you aren't showing here, you're basically just recreating the Logger API for no apparent benefit. Commented Aug 30, 2021 at 2:45
  • The Original Logger, does not load the logging properties file unless it is passed as -D option to JVM or the file is in Java Home. I implemented this class to Logger to load the config by default. That is the one of the benefits I am expecting from this custom logger. Commented Aug 30, 2021 at 12:27
  • 1
    try { LogManager.getLogManager().readConfiguration(new FileInputStream("./logging.properties")); } catch (FileNotFoundException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException | SecurityException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); } This part is not part of the original logger and so does not create log files unless we pass it as JVM option. Commented Aug 30, 2021 at 12:28

2 Answers 2

4

The documentation for SimpleFormatter.format explains that the source parameter (%2$s in the format string) is "a string representing the caller, if available; otherwise, the logger's name". This looks at the stack to determine the direct caller, which in your case will always be the MyLogger class.

To use the logger name instead, use %3$s in the java.util.logging.SimpleFormatter.format configuration rather than %2$s.

Sign up to request clarification or add additional context in comments.

1 Comment

True but there's also an issue with the static as said in the comments
1

Per API docs you can use the log precise methods:

There are a set of "logp" methods (for "log precise") that are like the "log" methods, but also take an explicit source class name and method name.

Combine that with the inferCaller method to make your bridge look transparent:

 public void log(Level LogLevel, String logMessage) {
    StackTraceElement stack = inferCaller();
    log.logp(stack.getClassName(), stack.getMethodName(), LogLevel, logMessage);
 }

private boolean isPrintImplFrame(String cname) {
    return MyLogger.class.getName().equals(cname);
}

private StackTraceElement inferCaller() {
    StackTraceElement stack[] = (new Throwable()).getStackTrace();
    int ix = 0;
    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (isPrintImplFrame(cname)) {
            break;
        }
        ix++;
    }

    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (!isPrintImplFrame(cname)) {
            return frame;
        }
        ix++;
    }

    return new StackTraceElement(MyLogger.class.getName(), "log",
            MyLogger.class.getName(), -1);
}

Using this method your code will find the stack frame that is calling your bridge logger that is not the bridge logger itself.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.