Is there a command line option for setting the default log level in Java
Asked Answered
P

3

20

Can I do something along the lines of:

-Djava.util.logging.loglevel=FINE

Obviously that doesn't work, but you get the idea. Is there anything like that? Or am I forced to create a properties file?

Pragmaticism answered 29/1, 2010 at 7:16 Comment(1)
Yes and it is documented. See java.util.logging.Afoot
C
4

You can even pass your log Level as a user defined property.

-DmyProp.logLevel=FINE

In your code:

String logLevel = System.getProperties("myProp.logLevel");

But I have the idea that your are looking for a more "built-in" and automatically handled property, right? AFAIK, it doesn't exist, but maybe I'm wrong.

Cowhide answered 29/1, 2010 at 8:9 Comment(3)
yeah, i was hoping for something more built-in, but maybe this is the best i can doPragmaticism
if you replaced "myProp.logLevel" by "java.util.logging.loglevel" in the call to getProperties(..), it would work like guessed in the question. (except that you would have to parse the loglevel yourself ?)Irritable
this answer does not explain how to actually use the value obtained from the system property to change logging levels.Slighting
S
5

tldr:

for java-9+ put this code at the beginning of your main method:
(see the bottom for java-8 and earlier)

final var cmdLineVal = System.getProperty("java.util.logging.loglevel");
if (cmdLineVal != null) {
    LogManager.getLogManager().updateConfiguration(
        (key) -> (oldVal, newVal) ->
            key.equals(".level") || key.equals("java.util.logging.ConsoleHandler.level")
            ? cmdLineVal : newVal
    );
}

explanation and workaround if main cannot be modified:

Generally java.util.logging does not use system properties to configure itself (there are a few exceptions, like java.util.logging.SimpleFormatter.format). Instead, the global LogManager singleton instance is responsible for configuring the logging subsystem and by default it loads properties from ${JAVA_HOME}/conf/logging.properties. There are a few ways to tweak this behavior:

  • provide java.util.logging.config.file system property with a path to an alternative logging config properties file.
  • provide java.util.logging.config.class system property with a class name that completely overrides configuration process (usually by providing a custom InputStream with logging config properties to readConfiguration(is)).
  • use LogManager.updateConfiguration(mapper) method (java 9+) to override selected properties only. Note that this method can only override existing properties, but cannot add new ones (for that, a way more cumbersome updateConfiguration(inputStream, mapper) would need to be used).

So in case of overriding the global root log-level, it's probably easiest to use updateConfiguration(mapper) somewhere at the beginning of your main method. LogManager uses .level property (level of the empty-string root logger) as a default for its child loggers not configured explicitly, so using the java.util.logging.loglevel system property from the OP, it would be like this:

final var cmdLineVal = System.getProperty("java.util.logging.loglevel");
if (cmdLineVal != null) {
    LogManager.getLogManager().updateConfiguration(
        (key) -> (oldVal, newVal) ->
            key.equals(".level")
            ? cmdLineVal : newVal
    );
}

Note that in case of the default logging config, the above is not sufficient for any FINE (or lower) log entries to be output on the console, as ConsoleHandler by default outputs INFO and higher. To change this, you need to override also java.util.logging.ConsoleHandler.level logging config property. If you want to use the same system property for this again, then you need to modify the argument of updateConfiguration like the below:

final var cmdLineVal = System.getProperty("java.util.logging.loglevel");
if (cmdLineVal != null) {
    LogManager.getLogManager().updateConfiguration(
        (key) -> (oldVal, newVal) ->
            key.equals(".level")
                || key.equals("java.util.logging.ConsoleHandler.level")
            ? cmdLineVal : newVal
    );
}

If you can't change the main method, then you can define the previously mentioned java.util.logging.config.class property to point to your class like this: -Djava.util.logging.config.class=com.example.MyJulConfigurator. This will delegate the task of logging configuration to the constructor of this class. In such constructor you can first read the config from a file the normal way (using readConfiguration() method) and then use the code from the previous snippet:

public MyJulConfigurator() throws IOException {
    System.clearProperty("java.util.logging.config.class");
    LogManager.getLogManager().readConfiguration();

    // the same code as in the previous snippet:
    final var cmdLineVal = System.getProperty("java.util.logging.loglevel");
    if (cmdLineVal != null) {
        LogManager.getLogManager().updateConfiguration(
            (key) -> (oldVal, newVal) ->
                key.equals(".level") || key.equals("java.util.logging.ConsoleHandler.level")
                ? cmdLineVal : newVal
        );
    }
}

As a final note, I've recently written a simple helper function to make any ad-hoc command-line changes to logging config easier: overrideLogLevelsWithSystemProperties (also supports adding new logging properties).
As mentioned by @TWiStErRob in the comment, you don't even need to include the containing jul-utils as a dependency of your project: just add it to your classpath (available in central) when starting your app and define your desired system properties:

java -cp /path/to/jul-utils.jar:${CLASSPATH} \
-Djava.util.logging.config.class=pl.morgwai.base.jul.JulConfigurator \
-Djava.util.logging.overrideLevel=,java.util.logging.ConsoleHandler,com.third.party.talkative.lib \
-D.level=FINE \
-Djava.util.logging.ConsoleHandler.level=FINE \
-Dcom.third.party.talkative.lib.level=SEVERE \
${MY_JAVA_APP_MAINCLASS_AND_ARGUMENTS}

outline for java-8 and earlier:

  1. parse the config file manually into a Properties using load(is) (see here how LogManager determines which file to read)
  2. replace values for .level and java.util.logging.ConsoleHandler.level using setProperty(key, value) with the value obtained from the system property
  3. store the updated properties into a ByteArrayOutputStream using store(os, null)
  4. wrap the underlying data with a ByteArrayInputStream (see also this discussion) and pass it to readConfiguration(is)

as before, you can do it either in your main method or in a constructor of some class pointed by java.util.logging.config.class system property.

Slighting answered 11/6, 2021 at 2:1 Comment(4)
The code you provided along with the java.util.logging.config.class property is pretty good. The class set in the property can be part of the project OR it could be just appended to the classpath as an extra JAR file, adding functionality to any JVM execution. Still I think java.util.logging.config.file is the best configuration option. Future readers: take a look at the default properties file, it's well-documented in the file itself.Tight
@Tight java.util.logging.config.file is best as a permanent solution for sure. updateLogLevels is meant for quick temporary changes when starting program manually from a command line (for example when debugging etc), which is exactly what OP was looking for.Slighting
@Tight I didn't even think about non-dependency, runtime-only usage of it: I guess it's a good selling point :) Thanks for pointing this!Slighting
+1 for the tidbit about SimpleFormatter.format being the exception about being usable in command line! That was what got me here... Seeing that working I assumed anything that can be put into the properties file could also be passed as a system property (through the command line). A bad assumption it seems! :)Gorden
C
4

You can even pass your log Level as a user defined property.

-DmyProp.logLevel=FINE

In your code:

String logLevel = System.getProperties("myProp.logLevel");

But I have the idea that your are looking for a more "built-in" and automatically handled property, right? AFAIK, it doesn't exist, but maybe I'm wrong.

Cowhide answered 29/1, 2010 at 8:9 Comment(3)
yeah, i was hoping for something more built-in, but maybe this is the best i can doPragmaticism
if you replaced "myProp.logLevel" by "java.util.logging.loglevel" in the call to getProperties(..), it would work like guessed in the question. (except that you would have to parse the loglevel yourself ?)Irritable
this answer does not explain how to actually use the value obtained from the system property to change logging levels.Slighting
S
2

you can configure your code to set the level based on an envrioment variable :

String sLoglevel= System.getenv("LOGLEVEL");  
int ilevel = loglevel.parseInt(sLoglevel);
//set the log level based on retrieved value 
Silin answered 29/1, 2010 at 7:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.