001/* 002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Salem Aouana 018 */ 019 020package org.nuxeo.runtime.test.logging; 021 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertNotNull; 025import static org.junit.Assert.assertTrue; 026 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.List; 030 031import org.apache.logging.log4j.Level; 032import org.apache.logging.log4j.LogManager; 033import org.apache.logging.log4j.Logger; 034import org.apache.logging.log4j.core.Appender; 035import org.apache.logging.log4j.core.Filter; 036import org.apache.logging.log4j.core.LoggerContext; 037import org.apache.logging.log4j.core.appender.ConsoleAppender; 038import org.apache.logging.log4j.core.filter.ThresholdFilter; 039import org.junit.ClassRule; 040import org.junit.Rule; 041import org.junit.Test; 042import org.junit.rules.TestRule; 043import org.junit.runner.Description; 044import org.junit.runner.RunWith; 045import org.junit.runners.model.FrameworkMethod; 046import org.junit.runners.model.Statement; 047import org.nuxeo.runtime.test.runner.ConsoleLogLevelThreshold; 048import org.nuxeo.runtime.test.runner.Features; 049import org.nuxeo.runtime.test.runner.FeaturesRunner; 050import org.nuxeo.runtime.test.runner.LogFeature; 051import org.nuxeo.runtime.test.runner.LoggerLevel; 052import org.nuxeo.runtime.test.runner.LoggerLevels; 053import org.nuxeo.runtime.test.runner.RuntimeFeature; 054 055/** 056 * Test for {@link LogFeature}. 057 * 058 * @since 11.1 059 */ 060@RunWith(FeaturesRunner.class) 061@Features({ RuntimeFeature.class, LogFeature.class }) 062@LoggerLevel(name = "org.nuxeo.FakeClass2", level = "DEBUG") 063@LoggerLevel(name = "org.nuxeo.FakeClass3", level = "FATAL") 064public class TestLogFeature { 065 066 protected static final Logger log = LogManager.getLogger(TestLogFeature.class); 067 068 protected static final String FAKE_LOGGER_NAME_1 = "org.nuxeo.FakeClass1"; 069 070 protected static final String FAKE_LOGGER_NAME_2 = "org.nuxeo.FakeClass2"; 071 072 protected static final String FAKE_LOGGER_NAME_3 = "org.nuxeo.FakeClass3"; 073 074 @ClassRule 075 public final static LogFeatureClassCheckerRule LOG_FEATURE_CLASS_CHECKER_RULE = new LogFeatureClassCheckerRule(); 076 077 @Rule 078 public final LogFeatureMethodCheckerRule logFeatureMethodCheckerRule = new LogFeatureMethodCheckerRule(); 079 080 @Test 081 @LoggerLevel(name = FAKE_LOGGER_NAME_1, level = "TRACE") 082 public void shouldAddNewLogger() { 083 assertTrue(LogManager.getLogger(FAKE_LOGGER_NAME_1).isTraceEnabled()); 084 } 085 086 @Test 087 @LoggerLevel(klass = TestLogFeature.class, level = "DEBUG") 088 public void shouldUpdateLogger() { 089 assertTrue(LogManager.getLogger(TestLogFeature.class).isDebugEnabled()); 090 } 091 092 @Test 093 @LoggerLevel(name = FAKE_LOGGER_NAME_2, level = "TRACE") 094 @LoggerLevel(name = FAKE_LOGGER_NAME_3, level = "INFO") 095 public void shouldOverrideTestLoggerDefinition() { 096 assertTrue(LogManager.getLogger(FAKE_LOGGER_NAME_2).isTraceEnabled()); 097 assertTrue(LogManager.getLogger(FAKE_LOGGER_NAME_3).isInfoEnabled()); 098 } 099 100 @Test 101 public void shouldInheritLogger() { 102 assertTrue(LogManager.getLogger(FAKE_LOGGER_NAME_2).isDebugEnabled()); 103 assertTrue(LogManager.getLogger(FAKE_LOGGER_NAME_3).isFatalEnabled()); 104 } 105 106 @Test 107 @ConsoleLogLevelThreshold("FATAL") 108 public void shouldSetConsoleLogThreshold() { 109 LoggerContext context = LoggerContext.getContext(false); 110 org.apache.logging.log4j.core.Logger rootLogger = context.getRootLogger(); 111 Appender appender = rootLogger.getAppenders().get("CONSOLE_LOG_FEATURE"); 112 assertNotNull(appender); 113 ConsoleAppender console = (ConsoleAppender) appender; 114 Filter filter = console.getFilter(); 115 assertNotNull(filter); 116 assertTrue(filter instanceof ThresholdFilter); 117 ThresholdFilter threshold = (ThresholdFilter) filter; 118 assertEquals(Level.FATAL, threshold.getLevel()); 119 } 120 121 /** 122 * Ensures that all log levels are preserved before launching the whole class test 123 * {@link org.nuxeo.runtime.test.runner.RunnerFeature#beforeRun(FeaturesRunner)} and correctly restored after that 124 * which means after the execution of {@link org.nuxeo.runtime.test.runner.RunnerFeature#afterRun(FeaturesRunner)} 125 */ 126 public static class LogFeatureClassCheckerRule implements TestRule { 127 @Override 128 public Statement apply(final Statement base, final Description description) { 129 return new Statement() { 130 @Override 131 public void evaluate() throws Throwable { 132 before(); 133 try { 134 base.evaluate(); 135 } finally { 136 after(); 137 } 138 } 139 140 protected void before() { 141 LoggerContext context = LoggerContext.getContext(false); 142 143 // Inherit from "org.nuxeo" see log4j2-test.xml 144 assertTrue(log.isInfoEnabled()); 145 146 // Ensure that there is no loggers for these two fake classes 147 assertFalse(context.hasLogger(FAKE_LOGGER_NAME_1)); 148 assertFalse(context.hasLogger(FAKE_LOGGER_NAME_2)); 149 assertFalse(context.hasLogger(FAKE_LOGGER_NAME_3)); 150 } 151 152 protected void after() { 153 // Ensure that we have the original level value, as it was before the whole test. 154 assertTrue(log.isInfoEnabled()); 155 156 // The new created loggers should be turned off. 157 assertEquals(Level.OFF, LogManager.getLogger(FAKE_LOGGER_NAME_1).getLevel()); 158 assertEquals(Level.OFF, LogManager.getLogger(FAKE_LOGGER_NAME_2).getLevel()); 159 assertEquals(Level.OFF, LogManager.getLogger(FAKE_LOGGER_NAME_3).getLevel()); 160 } 161 }; 162 } 163 } 164 165 /** 166 * Ensures that all log levels are preserved before launching each test method 167 * {@link org.nuxeo.runtime.test.runner.RunnerFeature#beforeMethodRun(FeaturesRunner, FrameworkMethod, Object)} and 168 * correctly restored after that which means after the execution of 169 * {@link org.nuxeo.runtime.test.runner.RunnerFeature#afterMethodRun(FeaturesRunner, FrameworkMethod, Object)} 170 */ 171 public class LogFeatureMethodCheckerRule implements TestRule { 172 @Override 173 public Statement apply(final Statement base, final Description description) { 174 return new Statement() { 175 @Override 176 public void evaluate() throws Throwable { 177 before(description); 178 try { 179 base.evaluate(); 180 } finally { 181 after(description); 182 } 183 } 184 185 protected void before(final Description description) { 186 getLoggersAnnotations(description).forEach(a -> check(a, true)); 187 } 188 189 protected void after(final Description description) { 190 getLoggersAnnotations(description).forEach(a -> check(a, false)); 191 } 192 }; 193 } 194 195 protected Collection<LoggerLevel> getLoggersAnnotations(Description description) { 196 var annotations = new ArrayList<LoggerLevel>(); 197 198 LoggerLevel logger = description.getAnnotation(LoggerLevel.class); 199 if (logger != null) { 200 annotations.add(logger); 201 } 202 203 LoggerLevels loggers = description.getAnnotation(LoggerLevels.class); 204 if (loggers != null) { 205 annotations.addAll(List.of(loggers.value())); 206 } 207 208 return annotations; 209 } 210 211 protected void check(LoggerLevel logger, boolean before) { 212 LoggerContext context = LoggerContext.getContext(false); 213 if (logger.klass().equals(TestLogFeature.class)) { 214 assertEquals(Level.INFO, log.getLevel()); 215 } else { 216 switch (logger.name()) { 217 case FAKE_LOGGER_NAME_1: 218 if (before) { 219 assertFalse(context.hasLogger(FAKE_LOGGER_NAME_1)); 220 } else { 221 assertEquals(Level.OFF, LogManager.getLogger(FAKE_LOGGER_NAME_1).getLevel()); 222 } 223 break; 224 case FAKE_LOGGER_NAME_2: 225 assertEquals(Level.DEBUG, LogManager.getLogger(FAKE_LOGGER_NAME_2).getLevel()); 226 break; 227 case FAKE_LOGGER_NAME_3: 228 assertEquals(Level.FATAL, LogManager.getLogger(FAKE_LOGGER_NAME_3).getLevel()); 229 break; 230 231 default: 232 return; 233 } 234 } 235 236 } 237 } 238}