首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java命令行工具,详细输出标志

Java命令行工具,详细输出标志
EN

Stack Overflow用户
提问于 2016-05-03 23:22:17
回答 3查看 14.1K关注 0票数 2

我正在开发一个基于Java的命令行工具。我想利用slf4j的日志抽象。

当应用程序定期被调用时

代码语言:javascript
复制
java -jar myapp.jar someparameter

然后,应用程序将在stdout上提供“普通”CLI输出,例如:

代码语言:javascript
复制
/some/file1
/some/file2

使用附加的--verbose标志/选项调用应用程序时

代码语言:javascript
复制
java -jar myapp.jar --verbose someparameter

然后,应用程序将提供更高级的日志记录:

代码语言:javascript
复制
16:06:09.031 [main] DEBUG com.example.MyApp - Starting application.
16:06:09.031 [main] DEBUG com.example.MyApp - Entering directory /some/.
16:06:09.046 [main] INFO  com.example.MyApp - /some/file1
16:06:09.046 [main] INFO  com.example.MyApp - /some/file2

虽然很容易确定是否提供了--verbose (例如,通过使用jCommander CLI库),但slf4j似乎不允许在运行时设置根记录器级别,slf4j也不允许在运行时更改日志条目模式布局。

  • 在运行时(或至少在启动期间),我有哪些修改日志级别和日志入口模式布局的选项?
  • 我是否应该避免使用slf4j抽象层的崇高立场,而应该使用实际的底层日志实现(不管是logback、slf4j2、.)
  • 其他基于Java的命令行工具是如何解决这一挑战的?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-05-20 23:51:57

我想出了以下解决方案:

对于命令行工具的可执行Java应用程序端,我决定使用slf4j和logback。我用两个logback配置文件( logback.xmllogback-verbose.xml )捆绑应用程序。

logback.xml

代码语言:javascript
复制
<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="STDOUT" />
  </root>

  <logger name="com.example.some.chatty.library.i.want.to.turn.off.logging" level="OFF">
    <appender-ref ref="STDOUT" />
  </logger>

</configuration>

logback-verbose.xml

代码语言:javascript
复制
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%date{ISO8601} [%-17thread] %-5level %-36logger - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

在应用程序启动阶段的早期,我检查--verbose标志。如果设置了该标志,我命令logback使用配置文件logback-verbose.xml而不是默认的logback.xml

Application.java

代码语言:javascript
复制
package com.example.my;
//

public class Application {

    public static void main(String[] args) throws Exception {

        // reconfigure JUL for slf4j
        LogManager.getLogManager().reset();
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
        Logger.getLogger("global").setLevel(Level.FINEST);

        // about to check for verbose output request
        if ( isVerboseOutputRequested() ) {
            LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();

            try {
              JoranConfigurator configurator = new JoranConfigurator();
              configurator.setContext(context);
              // Call context.reset() to clear any previous configuration, e.g. default 
              // configuration. For multi-step configuration, omit calling context.reset().
              context.reset(); 
              configurator.doConfigure( getClass().getResourceAsStream("/logback-verbose.xml") );
            }
            catch (JoranException je) {
              // StatusPrinter will handle this
            }
            StatusPrinter.printInCaseOfErrorsOrWarnings(context);
        }

       startApplication(); 
    }

    // ... implement isVerboseOutputRequested() and startApplication()
}

基本原理

此设置要记住可执行Java应用程序与该应用程序使用的库之间的分离。

库只能依赖于slf4j,而可执行的slf4j应用程序(只是该库的一个薄包装器)依赖于slf4j和logback,以便为最终的可执行工件提供一个实际的日志实现。

感谢Carlitos和Tansir1为这个解决方案提供了答案。

票数 2
EN

Stack Overflow用户

发布于 2016-05-04 00:45:09

我建议在这个问题上使用“装潢师”模式。

您必须定义自己的LoggerFactory,并定义方法“getLogger”.就像这样:

代码语言:javascript
复制
public class LoggerFactory {
    public static Logger getLogger(String category) {
        return (Boolean.parseBoolean(System.getProperty("verboseFlag"))
                  ? org.slf4j.LoggerFactory.getLogger("verbose-" + category)
                  : org.slf4j.LoggerFactory.getLogger(category);
    }

然后,在您的配置文件中,假设您正在使用log4j,您必须配置两个类别;一个是带有详细前缀的,另一个没有.稍后,每个类别都必须定义自己的追加器,并且必须相应地配置每个附录.

代码语言:javascript
复制
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
        <appender name="VerboseConsoleAppender" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] %m%n"/>
    </layout>
</appender>

        <appender name="RegularConsoleAppender" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%m%n"/>
    </layout>
</appender>
    <category name="verbose-org.xyz" additivity="false">
        <priority value="DEBUG" />
        <appender-ref ref="VerboseConsoleAppender" />
    </category>

    <category name="org.xyz" additivity="false">
        <priority value="INFO" />
        <appender-ref ref="RegularConsoleAppender" />
    </category>
</log4j:configuration>
票数 1
EN

Stack Overflow用户

发布于 2016-05-04 00:03:10

在运行时动态更改日志配置不是SLF4J的一部分。日志配置特定于抽象的日志机制(log4j、logback等)。

假设您正在使用SLF4J下面的Logback库,您可以通过更改JoranConfigurator来以编程方式重新配置所有内容。此代码片段是从Logback文档网站中的直接调用JoranConfigurator复制的。您可以更改日志记录模式、附加程序、日志记录级别,以及JoranConfigurator中有关记录器的几乎所有其他参数。

代码语言:javascript
复制
package chapters.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class MyApp3 {
  final static Logger logger = LoggerFactory.getLogger(MyApp3.class);

  public static void main(String[] args) {
    // assume SLF4J is bound to logback in the current environment
    LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();

    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(context);
      // Call context.reset() to clear any previous configuration, e.g. default 
      // configuration. For multi-step configuration, omit calling context.reset().
      context.reset(); 
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
      // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(context);

    logger.info("Entering application.");

    Foo foo = new Foo();
    foo.doIt();
    logger.info("Exiting application.");
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37015976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档