bio photo

After being puzzled for a while why he wasn't able to configure logging in jME-Nifty properly Ben ("ben dot foxmoore at gmail dot com") set aside some time to figure it out. He was kind enough to share his findings with us in this guest blog post :)

So here is Ben explaining a proper way to configure Nifty logging:

It seems that most people who have issues with Nifty's logs want to cut down on the number shown, but I, on the other hand, wanted to see more. Unfortunately, setting the global logging level to Info caused JME3 to also log lots of information that wasn't relevant to the problem at hand. I attempted a quick fix using the following code:

public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
Logger.getLogger("de.lessvoid.nifty").setLevel(Level.INFO);
SimpleApplication app = new SimpleApplication() {
public void simpleInitApp() {
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(
assetManager, inputManager, audioRenderer, guiViewPort);
Nifty nifty = niftyDisplay.getNifty();
nifty.loadStyleFile("nifty-default-styles.xml");
nifty.loadControlFile("nifty-default-controls.xml");
guiViewPort.addProcessor(niftyDisplay);
}
};
app.start();
}</pre></p>

However, you'll quickly realise that this doesn't actually cause Nifty to display any extra logs. After many hours trying to work out why, I discovered that it's all down to an intricacy in the way LogManager keeps track of the Loggers in your program. LogManager only keeps a weakReference to each Logger, allowing any that are no longer being used to be garbage collected. Because of this, in the time between line 3 and line 6, the JRE garbage collects the original "de.lessvoid.nifty" Logger created at line 3. When the "de.lessvoid.nifty.Nifty" Logger is retrieved in the Nifty constructor (called by the NiftyJmeDisplay constructor at line 6), a new one is created and it inherits its Level from the rootLogger, which is set to Warning at line 2. (The LogManager keeps a strong reference to the rootLogger so it is never garbage collected.)

A quick (but not very nifty) solution to this problem is to just keep a static (strong) reference to the "de.lessvoid.nifty" Logger in your class, as such:

    private static Logger logger;</p>

public static void main(String[] args) {
Logger.getLogger("").setLevel(Level.WARNING);
logger = Logger.getLogger("de.lessvoid.nifty");
logger.setLevel(Level.INFO);
SimpleApplication app = new SimpleApplication() {
public void simpleInitApp() {
...
}
};
app.start();
}</pre>

Ultimately though, the niftiest solution to this problem is to use a custom properties file which specifies to the LogManager how to initialize all of the Loggers. The following "logging.properties" file will initialize the Logging system in the same way as the code above:

handlers=java.util.logging.ConsoleHandler
.level=WARNING
de.lessvoid.nifty.level=INFO</pre></p>

There are two ways to tell the LogManager to use this file. Either use the

"-Djava.util.logging.config.file=pathToLogging.properties"</pre></p>

flag when running your program, or, alternatively, use the following code:

    public static void main(String[] args) throws Exception {
InputStream inputStream =
ClassLoader.getSystemResourceAsStream("logging.properties");
LogManager.getLogManager().readConfiguration(inputStream);
SimpleApplication app = new SimpleApplication() {
public void simpleInitApp() {
...
}
};
app.start();
}</pre></p>

Hopefully this should help anyone who comes across the same issue that I did!

(As a small side note, there is one final alternative: LogManager can also use the "java.util.logging.config.class" property. If present, the given class will be loaded, an object will be instantiated, and that object's constructor is responsible for providing the initial configuration as an InputStream, just as above.)

So that's it! Finally you can configure logging correctly! THANKS A LOT BEN! :D

void

PS: Nifty 1.3.3 and Nifty 1.4 have already been configured to log less by default. So most people should now be more happy with the new defaults. But if you really need the logging to debug an issue you now can do it properly - thanks to Ben :)