我正在尝试使用 XWiki LogRule 来捕获记录器输出。未捕获任何输出。目前尚不清楚我做错了什么。我已经阅读了相关的代码和文档,并且觉得我已经进行了彻底的搜索 - 一切都无济于事。我选择了这个解决方案,因为它似乎最适合手头的问题。
我正在尝试确定问题的范围以及从 Mockito 1.8.5 迁移到 1.9.5 引起的问题的可能解决方案。我们过去能够插入最终字段,但现在不能 - 我理解为什么 - 使用 @InjectMocks。
一种解决方案是移除最终修饰符。另一种是创建重载的构造函数并手动注入。第一个是难吃的。第二个是丑陋的,因为它引入了仅用于测试的代码,并且在模拟记录器时,记录器是唯一的最终字段。因此,在记录器是唯一问题的情况下,我认为使用 xwiki-commons-test-simple-5.4.1.jar 中提供的 XWiki LogRule 是有意义的。
下面是测试类中规则的实现:
@Rule
public LogRule logRule = new LogRule()
{
{
record(LogLevel.INFO);
recordLoggingForType(EmployerNameCacheLoadServiceImpl.class);
}
};
这是测试中规则的执行:
@Test
public void testLoadCache_FormatAndLogData()
{
String key = "key";
String incorrectKey = key + " \n random malicious text";
String incorrectStorageId = STORAGE_ID + " \n random malicious text";
String incorrectUserGuid = USER_GUID + " \n random malicious text";
when(serviceUser.getOwningFirm()).thenReturn(incorrectStorageId);
when(serviceUser.getUserGuid()).thenReturn(incorrectUserGuid);
when(employerNameLoadSummaryCache.get(anyString())).thenReturn(null);
when(searchableEmployerNameFactory.createSearchableEmployerNames(eq(serviceUser), anyString()))
.thenReturn(new HashMap<String, SearchableEmployerName>());
when(stringSecurityFormatter.formatString(incorrectKey)).thenReturn(key);
when(stringSecurityFormatter.formatString(incorrectStorageId)).thenReturn(STORAGE_ID);
when(stringSecurityFormatter.formatString(incorrectUserGuid)).thenReturn(USER_GUID);
employerNameCacheLoadService.loadCache(serviceUser, incorrectKey);
verify(stringSecurityFormatter).formatString(incorrectKey);
verify(stringSecurityFormatter).formatString(incorrectStorageId);
verify(stringSecurityFormatter).formatString(incorrectUserGuid);
String expectedLogEntry =
String.format("Cache load(%s) of firm (%s) for batch (%s) by user(%s). A total of %d entries.",
key,
STORAGE_ID,
anyString(),
USER_GUID,
0);
// 验证(logger).info(expectedLogEntry); System.out.println("this.logRule.size() 为 " + this.logRule.size()); System.out.println("this.logRule.toString() 是 " + this.logRule.toString()); assertThat(this.logRule.size(), is(1)); assertThat(this.logRule.getMessage(0), containsString(expectedLogEntry)); assertThat(this.logRule.toString(), containsString(expectedLogEntry)); }
你可以看到我已经尝试了许多变体。对 logRule.size() 的调用返回 0,它应该始终记录一个 INFO 条目。
这是目标类中记录器的定义:
private final Logger logger = Logger.getLogger(EmployerNameCacheLoadServiceImpl.class);
这是正在测试的方法:
@Override
public String loadCache(final serviceUser user, final String key)
{
NamesLoadSummary namesLoadSummary = employerNameLoadSummaryCache.get(key);
String batchId = null;
Date now = new Date();
if (namesLoadSummary == null || now.after(namesLoadSummary.getExpiresDateTime()))
{
batchId = UUID.randomUUID().toString().replace("-", "");
final Map<String, SearchableEmployerName> mapOfSearchableNames =
searchableEmployerNameFactory.createSearchableEmployerNames(user, batchId);
employerNamesCache.putAll(mapOfSearchableNames);
namesLoadSummary = new NamesLoadSummary();
namesLoadSummary.setBatchId(batchId);
namesLoadSummary.setStorageId(key);
namesLoadSummary.setExpiresDateTime(employerNameCacheExpiryCalculator.calculateExpiryDate());
employerNameLoadSummaryCache.put(key, namesLoadSummary);
logger.info(String.format("Cache load(%s) of firm (%s) for batch (%s) by user(%s). A total of %d entries.",
stringSecurityFormatter.formatString(key),
stringSecurityFormatter.formatString(user.getOwningFirm()),
batchId,
stringSecurityFormatter.formatString(user.getUserGuid()),
mapOfSearchableNames.size()));
}
else
{
batchId = namesLoadSummary.getBatchId();
}
return batchId;
}
这是控制台输出:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/c164033/tools/jars/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/c164033/.ivy2/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
14:41:00,556 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
14:41:00,557 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/C:/Users/c164033/workspace/Contacts/bin/logback-test.xml]
14:41:00,558 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback-test.xml] occurs multiple times on the classpath.
14:41:00,558 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback-test.xml] occurs at [jar:file:/C:/Users/c164033/tools/jars/xwiki-commons-test-simple-5.4.1.jar!/logback-test.xml]
14:41:00,558 |-WARN in ch.qos.logback.classic.LoggerContext[default] - Resource [logback-test.xml] occurs at [file:/C:/Users/c164033/workspace/Contacts/bin/logback-test.xml]
14:41:00,621 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
14:41:00,669 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
14:41:00,681 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
14:41:00,707 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
14:41:00,783 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - com.xxx.yyy.zzz level set to INFO
14:41:00,783 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - org.hibernate level set to ERROR
14:41:00,783 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to WARN
14:41:00,783 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
14:41:00,783 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
14:41:00,784 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@1a7553 - Registering current configuration as safe fallback point
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
2014-03-21 14:41:01,037 INFO [main] EmployerNameCacheLoadServiceImpl: Cache load(key) of firm (storageId) for batch (ded2a5d7c9124fa0b3269afe355001e0) by user(userGuid). A total of 0 entries.
this.logRule.size() is 0
this.logRule.toString() is com.xxx.yyy.zzz.contacts.employer.name.EmployerNameCacheLoadServiceImplTests$1@9fa7ba
如您所见,它确实记录了日志。您还可以看到有一些 slf4j 绑定警告。似乎这些不应该是问题,但一旦发布,就会进一步研究这个角度。
我正在寻找解决此问题的建议,以便 LogRule 将捕获记录的输出。
我意识到 - 在我最初发布之后- 我未能将 logback-test.xml 添加到我的项目中。这样做,测试仍然像以前一样失败,但控制台不同。上面的控制台日志现在反映了这一变化。
这是我的 log4j.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%t] %c{1}: %m%n"/>
</layout>
</appender>
<logger name="com.xxx.yyy.zzz">
<level value="INFO" />
</logger>
<logger name="org.hibernate">
<level value="ERROR" />
</logger>
<root>
<level value="WARN"/>
<appender-ref ref="ConsoleAppender"/>
</root>
</log4j:configuration>
而且,这里是前面提到的 logback-test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<!-- Logback Test configuration that only logs at error level -->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- It's important that we log with the same default values as the ones used at runtime, so that tests see what
is really printed at runtime. Thus tests should capture their output to not print anything in the console
and assert what's printed using the LogRule class. -->
<logger name="com.xxx.yyy.zzz">
<level value="INFO" />
</logger>
<logger name="org.hibernate">
<level value="ERROR" />
</logger>
<root level="warn">
<appender-ref ref="STDOUT" />
</root>
</configuration>