当前位置: 首页 > 面试题库 >

Dropwizard不会将自定义记录程序记录到文件中

翁俊良
2023-03-14
问题内容

我有一个dropwizard应用程序,在该应用程序中,我配置了logger附加程序,使其文件如下:

logging:
  level: INFO

  loggers:
    "mylogger": INFO
    "com.path.to.class": INFO

  appenders:
    - type: file
      currentLogFilename: .logs/mylogs.log
      archivedLogFilenamePattern: .logs/archive.%d.log.gz
      archivedFileCount: 14

并且,在我的应用中创建了记录器:

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

private final Logger OpLogger = LoggerFactory.getLogger("mylogger");
(and)
private final Logger ClassLogger = LoggerFactory.getLogger(pathToClass.class);

在main()中进行一些测试记录:

OpLogger.info("test 1");
ClassLogger.info("test 2);

该应用程序启动并运行没有问题。但是我没有在stdout或mylogs.log文件中得到任何日志(当然,除了Jetty访问日志之外,这些日志也已正确打印到mylogs.log中)。相反,如果我删除configuration.yml中的记录器配置,则会将所有日志打印到stdout。也许这是dropwizard的问题,还是我必须在configuration.yml中添加一些内容?我正在使用Dropwizard
0.8.0


问题答案:

更新 最新版本的dropwizard支持开箱即用地记录配置

我在尝试使用单独的文件设置Dropwizard(0.8.4)时遇到了相同的问题。我遇到了同样的问题。因此,我进行了更深入的研究,为我找到了一个解决方案(不是最干净的解决方案,但是我似乎无法以不同的方式进行工作)。

问题是LoggingFactory#configure自动将每个追加程序添加到根目录。这不是很理想,因此需要覆盖。我所做的是:

  1. 覆盖LoggingFactory

这有点混乱,因为有些事情需要复制:(这是我的实现:

import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.util.Map;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.logback.InstrumentedAppender;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.jmx.JMXConfigurator;
import ch.qos.logback.classic.jul.LevelChangePropagator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.util.StatusPrinter;
import io.dropwizard.logging.AppenderFactory;
import io.dropwizard.logging.LoggingFactory;

public class BetterDropWizardLoggingConfig extends LoggingFactory {

    @JsonIgnore
    final LoggerContext loggerContext;

    @JsonIgnore
    final PrintStream configurationErrorsStream;

    @JsonProperty("loggerMapping")
    private ImmutableMap<String, String> loggerMappings;

    private static void hijackJDKLogging() {
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
    }

    public BetterDropWizardLoggingConfig() {
        PatternLayout.defaultConverterMap.put("h", HostNameConverter.class.getName());
        this.loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        this.configurationErrorsStream = System.err;
    }

    private Logger configureLevels() {
        final Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
        loggerContext.reset();

        final LevelChangePropagator propagator = new LevelChangePropagator();
        propagator.setContext(loggerContext);
        propagator.setResetJUL(true);

        loggerContext.addListener(propagator);

        root.setLevel(getLevel());

        for (Map.Entry<String, Level> entry : getLoggers().entrySet()) {
            loggerContext.getLogger(entry.getKey()).setLevel(entry.getValue());
        }

        return root;
    }

    @Override
    public void configure(MetricRegistry metricRegistry, String name) {
        hijackJDKLogging();

        final Logger root = configureLevels();

        for (AppenderFactory output : getAppenders()) {
            Appender<ILoggingEvent> build = output.build(loggerContext, name, null);
            if(output instanceof MappedLogger && ((MappedLogger) output).getLoggerName() != null) {
                String appenderName = ((MappedLogger) output).getLoggerName();
                String loggerName = loggerMappings.get(appenderName);
                Logger logger = this.loggerContext.getLogger(loggerName);
                logger.addAppender(build);
            } else {
                root.addAppender(build);
            }
        }

        StatusPrinter.setPrintStream(configurationErrorsStream);
        try {
            StatusPrinter.printIfErrorsOccured(loggerContext);
        } finally {
            StatusPrinter.setPrintStream(System.out);
        }

        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        try {
            final ObjectName objectName = new ObjectName("io.dropwizard:type=Logging");
            if (!server.isRegistered(objectName)) {
                server.registerMBean(new JMXConfigurator(loggerContext, server, objectName), objectName);
            }
        } catch (MalformedObjectNameException | InstanceAlreadyExistsException | NotCompliantMBeanException
                | MBeanRegistrationException e) {
            throw new RuntimeException(e);
        }

        configureInstrumentation(root, metricRegistry);
    }

    private void configureInstrumentation(Logger root, MetricRegistry metricRegistry) {
        final InstrumentedAppender appender = new InstrumentedAppender(metricRegistry);
        appender.setContext(loggerContext);
        appender.start();
        root.addAppender(appender);
    }

}

如您所知,不幸的是,我不得不复制/粘贴一些私有成员和方法以使事情按预期进行。

我添加了一个新字段:

@JsonProperty("loggerMapping")
private ImmutableMap<String, String> loggerMappings;

这使我可以为每个记录器配置一个映射。这不是开箱即用的,因为我无法获得名称(dropwizard默认为附加程序名称,非常不便…)

因此,我添加了一个新的Logger,以我为例,由于不同的原因,它也可以替换主机名。为此,我覆盖了旧版本FileAppenderFactory并实现了自己的接口MappedLogger。此处的实现:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import io.dropwizard.logging.AppenderFactory;
import io.dropwizard.logging.FileAppenderFactory;

@JsonTypeName("hostnameFile")
public class HostnameFileAppender extends FileAppenderFactory implements AppenderFactory, MappedLogger {

    private static String uuid = UUID.randomUUID().toString();

    @JsonProperty
    private String name;

    public void setCurrentLogFilename(String currentLogFilename) {
        super.setCurrentLogFilename(substitute(currentLogFilename));
    }

    private String substitute(final String pattern) {
        String substitute = null;

        try {
            substitute = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            System.err.println("Failed to get local hostname:");
            e.printStackTrace(System.err);
            substitute = uuid;
            System.err.println("Using " + substitute + " as fallback.");
        }
        return pattern.replace("${HOSTNAME}", substitute);
    }

    @Override
    public void setArchivedLogFilenamePattern(String archivedLogFilenamePattern) {
        super.setArchivedLogFilenamePattern(substitute(archivedLogFilenamePattern));
    }

    @Override
    public String getLoggerName() {
        return name;
    }
}

请注意,为了添加新的json类型,您将必须遵循JavaDoc AppenderFactory(将Meta-inf添加到类路径并使新的附加程序可发现)中

到目前为止,到目前为止,我们已经有了一个可以使用记录器映射的配置,我们有一个可以使用可选名称的记录器。

现在,在configure方法中,将这两个绑定在一起:

for (AppenderFactory output : getAppenders()) {
        Appender<ILoggingEvent> build = output.build(loggerContext, name, null);
        if(output instanceof MappedLogger && ((MappedLogger) output).getLoggerName() != null) {
            String appenderName = ((MappedLogger) output).getLoggerName();
            String loggerName = loggerMappings.get(appenderName);
            Logger logger = this.loggerContext.getLogger(loggerName);
            logger.addAppender(build);
        } else {
            root.addAppender(build);
        }
    }

为了向后兼容,我保留了默认行为。如果未定义名称,则将附加程序添加到根记录器。否则,我将解析键入的记录器,并根据需要向其添加追加器。

最后但并非最不重要的一点是,旧的yaml配置良好:

logging:
  # The default level of all loggers. Can be OFF, ERROR, WARN, INFO, DEBUG, TRACE, or ALL.
  level: INFO

  loggers:
    "EVENT" : INFO

  loggerMapping:
    # for easier search this is defined as: appenderName -> loggerName rather than the other way around
    "eventLog" : "EVENT"

  appenders:
   - type: console   
     threshold: ALL
     logFormat: "myformat"

   - type: hostnameFile # NOTE THE NEW TYPE WITH HOSTNAME RESOLVE
     currentLogFilename: /Users/artur/tmp/log/my-${HOSTNAME}.log
     threshold: ALL
     archive: true
     archivedLogFilenamePattern: mypattern
     archivedFileCount: 31
     timeZone: UTC
     logFormat: "myFormat"

   - type: hostnameFile
     name: eventLog # NOTE THE APPENDER NAME
     currentLogFilename: something
     threshold: ALL
     archive: true
     archivedLogFilenamePattern: something
     archivedFileCount: 31
     timeZone: UTC
     logFormat: "myFormat"

   - type: hostnameFile
     currentLogFilename: something
     threshold: ERROR
     archive: true
     archivedLogFilenamePattern: something
     archivedFileCount: 31
     timeZone: UTC
     logFormat: "myFormat"

如您所见,我正在将事件追加器映射到事件记录器。这样,我所有的事件都结束于文件A中,而其他信息结束于其他地方。

我希望这有帮助。可能不是最干净的解决方案,但我不认为Dropwizard当前允许此功能。



 类似资料:
  • 我在为Oracle SOA套件开发自定义log4j2记录器方面处于中间位置。类是一个胖的或自包含的罐子。它有一个log4j2.xml配置文件,其中有一个记录器和附加器 null 运行JUnit测试时,记录器会按预期写入jsonLogger.json文件。 在我的weblogic服务器上,我部署了log4j2.xml配置文件,并将Java属性log4j.configurationfile设置到log

  • 问题内容: 我对jdk日志记录配置有疑问。我有一个使用JDK Logging输出消息的EJB(已部署到glassfish中)。因此,我使用具有以下代码的命名记录器: 我知道可以通过将以下行添加到Glassfish的logging.properties文件中来为记录器配置日志级别: 但是,如何为记录器指定输出文件?我想将来自名为“ org.imixs.workflow”的记录器的所有消息放入单独的文

  • 我在一个Spring Boot应用程序中定义了一对用于日志返回的自定义记录器。这个想法是将所有日志发送到一个文件中,而不是Spring Boot默认提供的标准控制台输出。我所看到的是,日志记录同时进入自定义文件和控制台,我找不到发生这种情况的任何原因。对两个自定义追加器的所有日志记录都处于调试和/或跟踪级别。我已经设置了logging.level.root=info,认为这样可以防止出现此日志记录

  • 我已经创建了一个触发Azure功能的服务总线,并希望在应用程序洞察中记录自定义事件。 我只能看到该日志记录在跟踪中。但自定义事件和度量不记录到应用程序洞察中。知道会发生什么吗?

  • 将PostSharp用于C#应用程序,我有以下场景: Namespace_ACustomLoggingMethod Namespace_B.DoThings thingMethod(实际上是几种不同的方法) DoMomthingMethod调用CustomLoggingMethod,它以所需的格式创建日志条目并且运行良好。正如预期的那样,日志条目将源记录为CustomLoggingMethod,我

  • 我有一个使用slf4j进行日志记录的应用程序。现在我想添加一个功能,每当我记录错误时,就会调用一个特定的url。目前,我在应用程序级别添加了一个包装器方法,该方法发送http请求,然后调用logger方法。但这样我就错过了一些输出,一些异常只是被记录下来,而没有发送相应的请求。我正在尝试创建我的自定义logger类。我的问题是应该扩展哪些类(或要实现的接口)。我无法扩展Logger类,因为它的构造