java Dropwizard 不会将自定义记录器记录到文件中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27483442/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dropwizard doesn't log custom loggers to file
提问by Carmine Giangregorio
I have a dropwizard app, where I configured logger appenders to file as follows:
我有一个 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
And, created logger in my app:
并且,在我的应用程序中创建了记录器:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger OpLogger = LoggerFactory.getLogger("mylogger");
(and)
private final Logger ClassLogger = LoggerFactory.getLogger(pathToClass.class);
Do some test logging in main():
在 main() 中做一些测试日志:
OpLogger.info("test 1");
ClassLogger.info("test 2);
The application starts and runs without problems; but I don't get any logs (except for Jetty access logs, of course, that are correctly printed to mylogs.log), neither in stdout or in mylogs.log file. Instead, if I remove the loggers configuration in configuration.yml, I get all logs printed to stdout. Perhaps it's a problem of dropwizard or I have to add something to configuration.yml? I'm using Dropwizard 0.8.0
应用程序启动和运行没有问题;但我没有收到任何日志(当然,Jetty 访问日志除外,它们正确打印到 mylogs.log),无论是在 stdout 还是在 mylogs.log 文件中。相反,如果我删除 configuration.yml 中的记录器配置,我会将所有日志打印到标准输出。也许这是dropwizard的问题,或者我必须在configuration.yml中添加一些东西?我正在使用 Dropwizard 0.8.0
采纳答案by pandaadb
UPDATEThe latest version of dropwizard supports logging configurations out of the box
更新最新版本的 dropwizard 支持开箱即用的日志配置
I ran into the same issue trying to set up Dropwizard (0.8.4) with a separate files. I ran into the same issue. So I dug a bit deeper and found a solution for me (not the cleanest but I couldn't seem to get that working differently).
我在尝试使用单独的文件设置 Dropwizard (0.8.4) 时遇到了同样的问题。我遇到了同样的问题。所以我挖得更深一些,为我找到了一个解决方案(不是最干净的,但我似乎无法以不同的方式工作)。
The issue is that LoggingFactory#configure
automatically adds every appender to root. This is not very ideal so it needed overwriting. What I did was:
问题是LoggingFactory#configure
自动将每个 appender 添加到 root。这不是很理想,因此需要覆盖。我所做的是:
- Overwrite
LoggingFactory
.
- 覆盖
LoggingFactory
。
This is slightly messy since there is a few things that need to be copied sadly :( Here is my implementation:
这有点混乱,因为有一些东西需要遗憾地复制:(这是我的实现:
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.Hymanson.annotation.JsonIgnore;
import com.fasterxml.Hymanson.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 hiHymanJDKLogging() {
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) {
hiHymanJDKLogging();
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);
}
}
As you can se, I sadly had to copy/paste a few private members and methods to make things work as intended.
如您所见,遗憾的是,我不得不复制/粘贴一些私有成员和方法,以使事情按预期工作。
I added a new field:
我添加了一个新字段:
@JsonProperty("loggerMapping")
private ImmutableMap<String, String> loggerMappings;
This allows me to configure a mapping for each logger. This wasn't out of the box allowed as I can't get a name (dropwizard defaults the appender names, very inconvenient ...)
这允许我为每个记录器配置一个映射。这不是开箱即用的,因为我无法获得名称(dropwizard 默认附加程序名称,非常不方便......)
So I added a new Logger which in my case also does hostname substitution which I needed for different reasons. For this I overwrite the good old FileAppenderFactory
and implement my own interface MappedLogger
. Implementation here:
所以我添加了一个新的 Logger,在我的情况下,它也进行主机名替换,我出于不同的原因需要它。为此,我覆盖了旧的FileAppenderFactory
并实现了我自己的界面MappedLogger
。在这里实现:
import com.fasterxml.Hymanson.annotation.JsonProperty;
import com.fasterxml.Hymanson.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;
}
}
Please note that in order to add a new json type, you will have to follow the JavaDoc in AppenderFactory
(Add Meta-inf to the classpath and make the new appender discoverable)
请注意,为了添加新的 json 类型,您必须遵循中的 JavaDoc AppenderFactory
(将 Meta-inf 添加到类路径并使新的 appender 可发现)
So far so good, we now have a config that can pick up on logger mappings, we have a logger that can take an optional name.
到目前为止一切顺利,我们现在有一个可以获取记录器映射的配置,我们有一个可以采用可选名称的记录器。
In the configure method I now tie those two together:
在 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);
}
}
For backwards compatibility I kept the default behaviour. If there is no name defined, the appender will be added to the root logger. Otherwise I resolve the typed logger and add the appender to it as wished.
为了向后兼容,我保留了默认行为。如果没有定义名称,appender 将被添加到根记录器中。否则,我会解析输入的记录器并根据需要将附加程序添加到其中。
And last but not least the good old yaml config:
最后但并非最不重要的是旧的 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"
As you can see I am mapping the events appender to the events logger. This way all my events end up in file A, while the other information ends up somewhere else.
如您所见,我正在将事件附加程序映射到事件记录器。这样我的所有事件都在文件 A 中结束,而其他信息则在其他地方结束。
I hope this helps. Might not be the cleanest solution but I don't think Dropwizard allows this feature currently.
我希望这有帮助。可能不是最干净的解决方案,但我认为 Dropwizard 目前不允许使用此功能。
回答by Kartik Jajal
You can implement separate logger with the dropwizard using logback.
您可以使用 logback 使用 dropwizard 实现单独的记录器。
1.Configure logger in your Application class (i.e application start point with main method) like below.
1.在您的应用程序类中配置记录器(即应用程序起始点与主要方法),如下所示。
LoggerContext context = (LoggerContext)LoggerFactory.getILoggerFactory();
context.reset();
ContextInitializer initializer = new ContextInitializer(context);
initializer.autoConfig();
2.Configure logback.xml like below.
2.配置logback.xml如下。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="OpLogger " class="ch.qos.logback.core.FileAppender">
<file>/var/log/applicationname-mylogger.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>false</append>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="classLogger" class="ch.qos.logback.core.FileAppender">
<file>/var/log/applicationame-com.path.to.class.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<append>false</append>
<encoder>
<pattern>%-5relative %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<logger name="mylogger">
<level value="INFO" />
<appender-ref ref="OpLogger" />
</logger>
<logger name="com.path.to.class">
<level value="INFO" />
<appender-ref ref="classLogger" />
</logger>
</configuration>
3.Now use logger
3.现在使用记录器
static final Logger OpLogger = LoggerFactory.getLogger("mylogger");
static final Logger classLogger = LoggerFactory.getLogger("com.path.to.class");
EDIT :
编辑 :
I have try to implement the same logger in my example project. It works fine in my case. We can not use the LOGGER before the Dropwizard application initialize. The Dropwizard initialized only when you call
我尝试在我的示例项目中实现相同的记录器。在我的情况下它工作正常。我们不能在 Dropwizard 应用程序初始化之前使用 LOGGER。Dropwizard 仅在您调用时初始化
new ExampleApplication().run(args);
So, if logger is used before Dropwizard initialized, your log will not be there. I have tried to implemented scenario with main method. The first log statement is not printed as we have used logger before the Dropwizard initialization, but the second log statement will be printed.
因此,如果在 Dropwizard 初始化之前使用 logger,您的日志将不会在那里。我试图用 main 方法实现场景。由于我们在 Dropwizard 初始化之前使用了 logger,因此不会打印第一条日志语句,但会打印第二条日志语句。
OpLogger.info("test 1");
new ExampleApplication().run(args);
ClassLogger.info("test 2);
Hope this will help you out to solve your problem.
希望这将帮助您解决您的问题。