0

I made a custom logger for my project by using java.util.logging:

public class SpotifyLogger {
    private static final Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
    public SpotifyLogger(String loggerFilePath) throws IOException {
        Logger myLogger = Logger.getLogger("");
        // suppress console messaging
        Handler[] handlers = myLogger.getHandlers();
        if (handlers[0] instanceof ConsoleHandler) { //exception occurs here
            myLogger.removeHandler(handlers[0]);
        }
        // set level
        LOGGER.setLevel(Level.SEVERE);
        // create a txt handler
        FileHandler textFileHandler = new FileHandler(loggerFilePath);
        SimpleFormatter simpleFormatter = new SimpleFormatter();
        textFileHandler.setFormatter(simpleFormatter);
        LOGGER.addHandler(textFileHandler);
    }
    public void log(String user, Exception e) {
        LOGGER.log(Level.SEVERE, user, e);
    }
}

For the client and the server parts of my program, I create two separate Logger objects:

// class member initialized as null, because of exception handling
private SpotifyLogger logger = null;
//...
//in constructor: 
this.logger = new SpotifyLogger(LOGGER_FILE_NAME); // the LOGGER_FILE_NAME is different for the client and the server

When I test my program manually, the loggers seem to work (the two log files contain exceptions that I have caused). Then, I wrote automatic tests. For each class that I am testing (a total of 5), I create a separate logger object with a different destination path. The tests (for whichever class comes first) work correctly. All other tests fail because I get an ArrayIndexOutOfBoundsException, when I initialize the logger for that particular class. The reason is that I am trying to access handlers[0], when handlers has 0 length. From what I understood after searching the web, this is because the logger is using parent handlers. I tried this:

public SpotifyLogger(String loggerFilePath) throws IOException {
    Logger myLogger = Logger.getLogger("");
    // suppress console messaging
    myLogger.setUseParentHandlers(false);
    Handler[] handlers = myLogger.getHandlers();
    if (handlers.length > 0) {
        if (handlers[0] instanceof ConsoleHandler) {
            myLogger.removeHandler(handlers[0]);
        }
    }
    //etc
}

I don't get an exception anymore but the logging doesn't work. What am I doing wrong?

6
  • For the client and the server parts of my program Do you mean that the server runs in one JVM and the client runs in another, separate JVM? Commented Feb 12, 2022 at 11:45
  • @Abra My program has a server and a client part, each having a logger object. I perform both my manual testing and automatic testing on the same machine (my machine is both the client and the server). The code is written, so that the server and client parts can be executed on different machines though. Commented Feb 12, 2022 at 12:11
  • I didn't ask if you use separate computers, I asked if client and server run in different JVMs. Do you know what a JVM is? I get the impression that it is not what you think it is. A JVM is essentially a computer process. Do you open a terminal window and launch the server and then open another, separate terminal window and launch the client? Commented Feb 12, 2022 at 12:22
  • @Abra Yes, first I launch the server. Then, I start the client process in another terminal. Indeed, I had the impression that a computer could have only one JVM and that these two processes run on it. I didn't know that each process has its own JVM. Commented Feb 12, 2022 at 12:30
  • 1
    For what it’s worth, you are using Logger incorrectly. You are not supposed to make only one Logger instance. Each class is supposed to make its own Logger instance whose name is a class name, like private static final Logger logger = Logger.getLogger(MyClass.class.getName());. The logger name will show up in the log output, so you know where the log message came from. Setting name to "Daniel" isn’t a particularly useful log message if the reader doesn’t know in which class and method it’s happening. Commented Feb 13, 2022 at 1:40

2 Answers 2

1

If you want different Loggers, you need to supply different names for each. Hence this line of your code (in SpotifyLogger constructor) always returns the same Logger.

Logger myLogger = Logger.getLogger("");

This actually returns the java.util.logging.LogManager.RootLogger which has a single Handler which is an instance of ConsoleLogger. You subsequently remove that Handler in the first invocation of SpotifyLogger constructor, hence in every subsequent invocation, method getHandlers returns an empty array.

Since you only ever add Handlers to the global Logger, another FileHandler is added to the global logger every time SpotifyLogger constructor is called. I have not verified but I believe that a Logger will use the first, appropriate Handler in the array returned by method getHandlers, hence the behavior you are seeing whereby only the first log file is being written to, i.e. the file that you passed to the first invocation of SpotifyLogger constructor.

Note that you have not provided a reproducible example so I cannot verify any of the above with regard to your context. I only tested the code in your question in order to arrive at the above.

Consider the following rewrite of class SpotifyLogger – including a main method for testing purposes only.

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class SpotifyLogger {
    private static final String[]  NAMES = {"First", "Second"};
    private static int  count;

    private int  index;

    public SpotifyLogger(String loggerFilePath) throws IOException {
        index = count;
        Logger myLogger = Logger.getLogger(NAMES[count++]);
        myLogger.setUseParentHandlers(false);
        // set level
        myLogger.setLevel(Level.SEVERE);
        // create a txt handler
        FileHandler textFileHandler = new FileHandler(loggerFilePath);
        SimpleFormatter simpleFormatter = new SimpleFormatter();
        textFileHandler.setFormatter(simpleFormatter);
        myLogger.addHandler(textFileHandler);
    }

    public void log(String user, Exception e) {
        Logger myLogger = Logger.getLogger(NAMES[index]);
        myLogger.log(Level.SEVERE, user, e);
    }

    public static void main(String[] args) {
        try {
            SpotifyLogger x = new SpotifyLogger("spotifyx.log");
            SpotifyLogger y = new SpotifyLogger("spotifyy.log");
            x.log("George", new Exception());
            y.log("Martha", new RuntimeException());
        }
        catch (IOException x) {
            x.printStackTrace();
        }
    }
}

Note that you are correct regarding parent Handlers, hence the following line in the above code:

myLogger.setUseParentHandlers(false);

After running the above code, the contents of file spotifyx.log is:

Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: George
java.lang.Exception
    at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:38)

And the contents of file spotifyy.log is:

Feb 12, 2022 2:12:28 PM javalogp.SpotifyLogger log
SEVERE: Martha
java.lang.RuntimeException
    at javalogp/javalogp.SpotifyLogger.main(SpotifyLogger.java:39)

And no log messages are written to the console.

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

1 Comment

That's it! It works now. Also, thank you about the comment under the question. It fixed my understanding of what a JVM is.
1

This doesn't make much sense to me

Logger myLogger = Logger.getLogger("")

Reference: Oracle java docs

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.