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

2024-04-04

我有一个 dropwizard 应用程序,我在其中将记录器附加程序配置为文件,如下所示:

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 中的记录器配置,我会将所有日志打印到标准输出。 也许这是 dropwizard 的问题,或者我必须在configuration.yml 中添加一些内容? 我正在使用 Dropwizard 0.8.0


UPDATE最新版本的 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 默认附加程序名称,非常不方便......)

因此,我添加了一个新的记录器,在我的例子中,它还进行主机名替换,这是我出于不同原因需要的。为此我覆盖了旧的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 类型,您必须遵循 JavaDocAppenderFactory(将 Meta-inf 添加到类路径并使新的附加程序可发现)

到目前为止一切顺利,我们现在有了一个可以获取记录器映射的配置,我们有一个可以采用可选名称的记录器。

在配置方法中,我现在将这两者联系在一起:

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 目前不允许此功能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Dropwizard 不会将自定义记录器记录到文件中 的相关文章

随机推荐

  • ng-if="true" + data-ng-if="true" 计算结果为 false?

    事实是 如果你使用ng if truthyValue and data ng if truthyValue 里面一个html元素使用角JS 1 6 受影响的元素不会在DOM 即使您使用 这也是如此ng if and data ng if与b
  • ActionScript 的 File.upload 不适用于 iOS 设备的 Air SDK

    我尝试使用 ActionScript 的 File upload 在 Air SDK for iOS 环境中上传文件 但 File upload 无法正常工作 调用 File upload 后 不会执行有关文件上传的处理程序 也不会捕获任何
  • Node.js 12 的 TypeScript tsconfig 设置?

    最佳的 TypeScript 是什么tsconfig用于输出将在 Node js 12 上运行的代码的设置 从 Node js 开始12 0 0 100 支持ES2019 如果您知道您的目标是该版本或更高版本 则最佳配置将如下所示 modu
  • 为什么 TypeScript 中受保护的成员可以被公共成员覆盖?

    我是 Typescript 的新手 我尝试在本文中使用 TypeScript 进行一些尝试操场 http www typescriptlang org play 我注意到在 TypeScript 中 基类中的受保护成员可以被公共成员覆盖 c
  • 如何创建 pip 可安装项目?

    如何创建 pip 可安装项目 如何注册 pip 所有项目都应该具有哪些元数据配置 以便允许集成和轻松导入 或者 如果你感觉很奇特 阅读 lazy sudo easy install PasteScript paster create myn
  • 实体框架 4 Visual Studio 2008

    我一直在四处寻找并试图查看 Entity Framework 4 是否可以在 Visual Studio 2008 下运行 但找不到任何对它的引用 可以让 EF4 在 VS2008 上运行吗 On the ADO NET 实体框架团队博客
  • 动画同步、光标和突出显示

    所以我几乎有了我的code http jsfiddle net bplumb PBFWV 9 按照我想要的方式工作 但无法让我的动画正确同步 我正在尝试对光标进行动画突出显示文本 然后单击按钮 问题是光标要么太慢 要么太快 我正在尝试做这个
  • C 语言快速联合实现中的分段错误(核心转储)

    include
  • 如何创建包含不同元素和操作的列表?

    我想创建包含不同类型项目的列表 他们应该调用不同的意图或做其他事情 显示地图等 它应该像联系方式一样 项目和操作的数量是预定义的 如何优雅地达到这样的效果呢 我不需要确切的代码 但需要指导方针和信息 任何帮助将不胜感激 UPDATE 我所说
  • 如何从 CDK 上的 CodePipeline 获取源工件?

    我正在使用官方 AWS 文档来使用 CDK 创建管道 https docs aws amazon com cdk latest guide cdk pipeline html cdk pipeline define https docs a
  • Spark:Java 中 forEach 循环中的任务不可序列化异常

    我正在尝试迭代 JavaPairRDD 并使用 JavaPairRDD 的键和值执行一些计算 然后将每个 JavaPair 的结果输出到处理后的数据 list 我已经尝试过的 将我在 lambda 函数内部使用的变量设为静态 make 方法
  • C# SWIG 向量 到 string[]

    给定 C 方法 例如 std vector lt std string gt getFoo void setFoo const std vector lt std string gt foo 我怎样才能让 SWIG 像这样将它暴露给 C s
  • Object.assign 与 lodash _.assign

    查看文档ES6 对象 分配 https developer mozilla org en US docs Web JavaScript Reference Global Objects Object assign and Lodash 分配
  • 在有序列表 CSS/HTML 中删除数字

    我有一个 HTML 有序列表 我需要对其应用删除线 我在 CSS 中做到了这一点 如下所示 Lower Alpha list style lower alpha margin top 2pt margin bottom 2pt text d
  • Java 类和接口名称冲突

    interface A void print class A implements A public void print System out println Hello public static void main String ar
  • Web 服务版本控制和服务器端处理

    我正在尝试设计一种 Web 服务版本控制策略以及如何从 SCM 的角度处理版本 我们正在做自下而上 JAX WS 服务 因此对架构的控制较少 并且无法遵循最佳实践的某些架构版本控制 我目前的想法是 1 主要变化 不向后兼容 通过新服务 UR
  • 如何避免全局常量的“多重定义”错误?

    我正在使用 Windows API 编写 C 程序 每个主要函数都有自己的文件 并且有一个用于原型和包含内容的标头 Headers global constants pragma once define WIN32 LEAN AND MEA
  • 创建一个转换,其删除取决于视图删除时的 @State 值

    我想创建一个过渡 其删除取决于 State删除视图时的值 这是我尝试过的 If isValid当视图出现时为 true 它将使用 move edge trailing 为了去除 即使isValid在此期间变为假 我试图获得的是 move e
  • 应用程序之间的通信

    我有 3 个选择可以使用 套接字 activeX com 以便在一台计算机上的应用程序之间进行通信 哪个更快 只要它在一台机器上运行 进程间通信从根本上就会受到总线带宽的限制 内存到内存的复制 无论是在 TCP IP 堆栈 命名管道支持代码
  • Dropwizard 不会将自定义记录器记录到文件中

    我有一个 dropwizard 应用程序 我在其中将记录器附加程序配置为文件 如下所示 logging level INFO loggers mylogger INFO com path to class INFO appenders ty