Java 中是否有用于设置默认日志级别的命令行选项

发布于 2024-08-20 01:44:04 字数 124 浏览 7 评论 0 原文

我可以做一些类似的事情吗:

-Djava.util.logging.loglevel=FINE

显然这不起作用,但你明白了。有这样的事吗?或者我被迫创建一个属性文件?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乄_柒ぐ汐 2024-08-27 01:44:04

tldr:

对于 java-9+ 将此代码放在 main 方法的开头:
(请参阅 java-8 及更早版本的底部)

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

如果 main 无法修改的解释和解决方法:

通常 java.util.logging 不使用系统属性来配置自身(有有一些例外,例如 java.util.logging.SimpleFormatter.format)。相反,全局 LogManager 单例实例负责配置日志子系统,默认情况下它从 ${JAVA_HOME}/conf/logging.properties 加载属性。有几种方法可以调整此行为:

  • 提供 java.util.logging.config.file 系统属性以及替代日志配置属性文件的路径。
  • 提供 java.util.logging.config.class 系统属性,其类名完全覆盖配置过程(通常通过提供自定义 InputStream 并将配置属性记录到 readConfiguration(is))。
  • 使用 LogManager.updateConfiguration(mapper) 方法 (java 9+) 仅覆盖选定的属性。请注意,此方法只能覆盖现有属性,而不能添加新属性(为此,一种更麻烦的方式 updateConfiguration(inputStream, mapper) 需要来使用)。

因此,如果要覆盖全局根日志级别,最简单的方法可能是在 main 方法开头的某个位置使用 updateConfiguration(mapper) 。 LogManager 使用 .level 属性(空字符串根记录器的级别)作为未显式配置的子记录器的默认值,因此使用 java.util。来自OP的logging.loglevel系统属性,它会像这样:

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

请注意,在默认日志记录配置的情况下,上述内容对于任何FINE(或更低)日志条目是不够的在控制台上输出,如 ConsoleHandler 默认输出 INFO 及更高版本。要更改此设置,您还需要重写 java.util.logging.ConsoleHandler.level 日志记录配置属性。如果您想再次使用相同的系统属性,则需要修改 updateConfiguration 的参数,如下所示:

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

如果您无法更改 main 方法,那么您可以定义前面提到的 java.util.logging.config.class 属性来指向您的类,如下所示:-Djava.util.logging.config.class=com.example。 MyJulConfigurator。这会将记录配置的任务委托给此类的构造函数。在这样的构造函数中,您可以首先以正常方式从文件中读取配置(使用 readConfiguration() 方法),然后使用前面代码片段中的代码:

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

最后一点,我最近编写了一个简单的帮助函数可以更轻松地对日志配置进行任何临时命令行更改:overrideLogLevelsWithSystemProperties(还支持添加新的日志记录属性)。
正如 @TWiStErRob 在 注释,您甚至不需要包含包含的 jul-utils 作为项目的依赖项:只需将其添加到您的类路径(可在 < a href="https://search.maven.org/artifact/pl.morgwai.base/jul-utils/" rel="nofollow noreferrer">central) 启动应用程序时并定义所需的系统属性:

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}

java-8 及更早版本的概述:

  1. 使用 load(is) (参见 这里如何LogManager 确定要读取的文件)
  2. 使用 setProperty(key, value) 使用从系统属性获取的值将
  3. 更新后的属性存储到 ByteArrayOutputStream 中,使用 store(os , null)
  4. 换行 底层数据ByteArrayInputStream(另请参阅此讨论)并将其传递给 readConfiguration(is)

和以前一样,您可以在您的main 方法或在 java.util.logging.config.class 系统属性指向的某个类的构造函数中。

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.

冷清清 2024-08-27 01:44:04

您甚至可以将日志级别作为用户定义的属性传递。

-DmyProp.logLevel=FINE

在您的代码中:

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

但我认为您正在寻找更“内置”且自动处理的属性,对吗? AFAIK,它不存在,但也许我错了。

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.

掩于岁月 2024-08-27 01:44:04

您可以配置代码以根据环境变量设置级别:

String sLoglevel= System.getenv("LOGLEVEL");  
int ilevel = loglevel.parseInt(sLoglevel);
//set the log level based on retrieved value 

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 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文