| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core.async; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.ThreadContext; |
| import org.apache.logging.log4j.ThreadContextTestAccess; |
| import org.apache.logging.log4j.core.CoreLoggerContexts; |
| import org.apache.logging.log4j.core.config.ConfigurationFactory; |
| import org.apache.logging.log4j.core.jmx.RingBufferAdmin; |
| import org.apache.logging.log4j.core.util.Constants; |
| import org.apache.logging.log4j.spi.DefaultThreadContextMap; |
| import org.apache.logging.log4j.spi.LoggerContext; |
| import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap; |
| import org.apache.logging.log4j.util.PropertiesUtil; |
| import org.apache.logging.log4j.util.Unbox; |
| import org.junit.AfterClass; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| import static org.junit.Assert.*; |
| |
| public abstract class AbstractAsyncThreadContextTestBase { |
| |
| private final static int LINE_COUNT = 130; |
| |
| @BeforeClass |
| public static void beforeClass() { |
| System.setProperty("log4j2.is.webapp", "false"); |
| System.setProperty("AsyncLogger.RingBufferSize", "128"); // minimum ringbuffer size |
| System.setProperty("AsyncLoggerConfig.RingBufferSize", "128"); // minimum ringbuffer size |
| } |
| |
| @AfterClass |
| public static void afterClass() { |
| System.clearProperty("AsyncLogger.RingBufferSize"); |
| System.clearProperty("AsyncLoggerConfig.RingBufferSize"); |
| System.clearProperty(Constants.LOG4J_CONTEXT_SELECTOR); |
| System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY); |
| System.clearProperty("log4j2.garbagefree.threadContextMap"); |
| System.clearProperty("log4j2.is.webapp"); |
| System.clearProperty("log4j2.threadContextMap"); |
| } |
| |
| enum Mode { |
| ALL_ASYNC, MIXED, BOTH_ALL_ASYNC_AND_MIXED; |
| |
| void initSelector() { |
| if (this == ALL_ASYNC || this == BOTH_ALL_ASYNC_AND_MIXED) { |
| System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, AsyncLoggerContextSelector.class.getName()); |
| } else { |
| System.clearProperty(Constants.LOG4J_CONTEXT_SELECTOR); |
| } |
| } |
| |
| void initConfigFile() { |
| // NOTICE: PLEASE DON'T REFACTOR: keep "file" local variable for confirmation in debugger. |
| final String file = this == ALL_ASYNC // |
| ? "AsyncLoggerThreadContextTest.xml" // |
| : "AsyncLoggerConfigThreadContextTest.xml"; |
| System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, file); |
| } |
| } |
| |
| enum ContextImpl { |
| WEBAPP, GARBAGE_FREE, COPY_ON_WRITE; |
| |
| void init() { |
| System.clearProperty("log4j2.threadContextMap"); |
| final String PACKAGE = "org.apache.logging.log4j.spi."; |
| System.setProperty("log4j2.threadContextMap", PACKAGE + implClassSimpleName()); |
| PropertiesUtil.getProperties().reload(); |
| ThreadContextTestAccess.init(); |
| } |
| |
| public String implClassSimpleName() { |
| switch (this) { |
| case WEBAPP: |
| return DefaultThreadContextMap.class.getSimpleName(); |
| case GARBAGE_FREE: |
| return "GarbageFreeSortedArrayThreadContextMap"; |
| case COPY_ON_WRITE: |
| return "CopyOnWriteSortedArrayThreadContextMap"; |
| } |
| throw new IllegalStateException("Unknown state " + this); |
| } |
| } |
| |
| private final ContextImpl contextImpl; |
| private final Mode asyncMode; |
| |
| public AbstractAsyncThreadContextTestBase(final ContextImpl contextImpl, final Mode asyncMode) { |
| this.contextImpl = contextImpl; |
| this.asyncMode = asyncMode; |
| |
| asyncMode.initSelector(); |
| asyncMode.initConfigFile(); |
| |
| contextImpl.init(); |
| } |
| |
| @Test |
| public void testAsyncLogWritesToLog() throws Exception { |
| final File[] files = new File[] { |
| new File("target", "AsyncLoggerTest.log"), // |
| new File("target", "SynchronousContextTest.log"), // |
| new File("target", "AsyncLoggerAndAsyncAppenderTest.log"), // |
| new File("target", "AsyncAppenderContextTest.log"), // |
| }; |
| for (final File f : files) { |
| f.delete(); |
| } |
| |
| ThreadContext.push("stackvalue"); |
| ThreadContext.put("KEY", "mapvalue"); |
| |
| final Logger log = LogManager.getLogger("com.foo.Bar"); |
| final LoggerContext loggerContext = LogManager.getContext(false); |
| final String loggerContextName = loggerContext.getClass().getSimpleName(); |
| RingBufferAdmin ring; |
| if (loggerContext instanceof AsyncLoggerContext) { |
| ring = ((AsyncLoggerContext) loggerContext).createRingBufferAdmin(); |
| } else { |
| ring = ((AsyncLoggerConfig) ((org.apache.logging.log4j.core.Logger) log).get()).createRingBufferAdmin(""); |
| } |
| |
| for (int i = 0; i < LINE_COUNT; i++) { |
| while (i >= 128 && ring.getRemainingCapacity() == 0) { // buffer may be full |
| Thread.sleep(1); |
| } |
| if ((i & 1) == 1) { |
| ThreadContext.put("count", String.valueOf(i)); |
| } else { |
| ThreadContext.remove("count"); |
| } |
| log.info("{} {} {} i={}", contextImpl, contextMap(), loggerContextName, Unbox.box(i)); |
| } |
| ThreadContext.pop(); |
| CoreLoggerContexts.stopLoggerContext(false, files[0]); // stop async thread |
| |
| checkResult(files[0], loggerContextName); |
| if (asyncMode == Mode.MIXED || asyncMode == Mode.BOTH_ALL_ASYNC_AND_MIXED) { |
| for (int i = 1; i < files.length; i++) { |
| checkResult(files[i], loggerContextName); |
| } |
| } |
| LogManager.shutdown(); |
| } |
| |
| private static String contextMap() { |
| final ReadOnlyThreadContextMap impl = ThreadContext.getThreadContextMap(); |
| return impl == null ? ContextImpl.WEBAPP.implClassSimpleName() : impl.getClass().getSimpleName(); |
| } |
| |
| private void checkResult(final File file, final String loggerContextName) throws IOException { |
| final String contextDesc = contextImpl + " " + contextImpl.implClassSimpleName() + " " + loggerContextName; |
| try (final BufferedReader reader = new BufferedReader(new FileReader(file))) { |
| String expect; |
| for (int i = 0; i < LINE_COUNT; i++) { |
| final String line = reader.readLine(); |
| if ((i & 1) == 1) { |
| expect = "INFO c.f.Bar mapvalue [stackvalue] {KEY=mapvalue, configProp=configValue, configProp2=configValue2, count=" + i + "} " |
| + contextDesc + " i=" + i; |
| } else { |
| expect = "INFO c.f.Bar mapvalue [stackvalue] {KEY=mapvalue, configProp=configValue, configProp2=configValue2} " + contextDesc + " i=" + i; |
| } |
| assertEquals(file.getName() + ": line " + i, expect, line); |
| } |
| final String noMoreLines = reader.readLine(); |
| assertNull("done", noMoreLines); |
| } finally { |
| file.delete(); |
| } |
| } |
| } |