| /* |
| * 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.juli; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.Serial; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.LogManager; |
| import java.util.logging.Logger; |
| import java.util.stream.Stream; |
| |
| import jakarta.servlet.http.HttpServlet; |
| import jakarta.servlet.http.HttpServletRequest; |
| import jakarta.servlet.http.HttpServletResponse; |
| |
| import org.junit.Assert; |
| import org.junit.Assume; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.startup.Tomcat; |
| import org.apache.catalina.startup.TomcatBaseTest; |
| import org.apache.tomcat.util.buf.ByteChunk; |
| |
| public class TestPerWebappJuliIntegration extends TomcatBaseTest { |
| @Before |
| public void assumeJuliIsUsed() { |
| Assume.assumeTrue(LogManager.getLogManager().getClass().getName().equals(ClassLoaderLogManager.class.getName())); |
| } |
| private static final String APP_ID_A = "A"; |
| private static final String APP_ID_B = "B"; |
| private static final String HANDLER_ISOLATION_LOGGER = "handlerIsolationLogger"; |
| @Test |
| public void testPerWebappRootLogLevelIsolation() throws Exception { |
| Tomcat tomcat = getTomcatInstance(); |
| constructAppForLogLevelIsolationTest(tomcat, APP_ID_A, Level.FINE); |
| constructAppForLogLevelIsolationTest(tomcat, APP_ID_B, Level.INFO); |
| tomcat.start(); |
| ByteChunk res = getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_A + "/log_level"); |
| ByteChunk res2 = getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_B + "/log_level"); |
| Assert.assertEquals(Level.FINE.toString(), res.toString().trim()); |
| Assert.assertEquals(Level.INFO.toString(), res2.toString().trim()); |
| tomcat.stop(); |
| } |
| @Test |
| public void testPerWebappHandlersIsolation() throws Exception { |
| Tomcat tomcat = getTomcatInstance(); |
| ConstructAppResult resultA = constructAppForHandlerIsolationTest(tomcat, APP_ID_A, Level.INFO); |
| ConstructAppResult resultB = constructAppForHandlerIsolationTest(tomcat, APP_ID_B, Level.WARNING); |
| try(LogCapture logCaptureA = TomcatBaseTest.attachWebappLogCapture(resultA.context(), null, HANDLER_ISOLATION_LOGGER); |
| LogCapture logCaptureB = TomcatBaseTest.attachWebappLogCapture(resultB.context(), null, HANDLER_ISOLATION_LOGGER)) { |
| tomcat.start(); |
| Assert.assertEquals(200, getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_A + "/test", new ByteChunk(), null)); |
| Assert.assertEquals(200, getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_B + "/test", new ByteChunk(), null)); |
| Assert.assertTrue(logCaptureA.containsText("JULI-" + APP_ID_A + "-INFO")); |
| Assert.assertTrue(logCaptureB.containsText("JULI-" + APP_ID_B + "-INFO")); |
| |
| File logFileA = findLogFile(resultA.logsDir(), "juli" + APP_ID_A + "."); |
| File logFileB = findLogFile(resultB.logsDir(), "juli" + APP_ID_B + "."); |
| |
| Assert.assertNotNull(logFileA); |
| Assert.assertTrue("App " + APP_ID_A + " log file should contain the INFO message", Files.readString(logFileA.toPath()).contains("JULI-" + APP_ID_A + "-INFO")); |
| Assert.assertNull("App " + APP_ID_B + " log file should not exist", logFileB); |
| |
| tomcat.stop(); |
| } |
| } |
| |
| private void constructAppForLogLevelIsolationTest(Tomcat tomcat, String appId, Level logLevel) throws FileNotFoundException { |
| File appDir = new File(getTemporaryDirectory(), "juli" + appId); |
| addDeleteOnTearDown(appDir); |
| Assert.assertTrue(appDir.mkdirs() && appDir.isDirectory()); |
| File webInfClassesDir = new File(appDir, "WEB-INF/classes"); |
| Assert.assertTrue(webInfClassesDir.mkdirs() && webInfClassesDir.isDirectory()); |
| |
| File loggingPropertiesFile = new File(webInfClassesDir, "logging.properties"); |
| try (PrintWriter writer = new PrintWriter(loggingPropertiesFile)) { |
| writer.write(".level = " + logLevel); |
| } |
| |
| Context context = tomcat.addContext("/juli" + appId, appDir.getAbsolutePath()); |
| Tomcat.addServlet(context, "log_level", new HttpServlet() { |
| @Serial |
| private static final long serialVersionUID = 1L; |
| |
| @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { |
| resp.getWriter().print(Logger.getLogger("").getLevel()); |
| } |
| }); |
| context.addServletMappingDecoded("/log_level", "log_level"); |
| } |
| private ConstructAppResult constructAppForHandlerIsolationTest(Tomcat tomcat, String appId, Level logLevel) throws FileNotFoundException { |
| File appDir = new File(getTemporaryDirectory(), "juliHandler" + appId); |
| addDeleteOnTearDown(appDir); |
| Assert.assertTrue(appDir.mkdirs() && appDir.isDirectory()); |
| File webInfClassesDir = new File(appDir, "WEB-INF/classes"); |
| Assert.assertTrue(webInfClassesDir.mkdirs() && webInfClassesDir.isDirectory()); |
| File logsDir = new File(appDir, "logs"); |
| Assert.assertTrue(logsDir.mkdirs() && logsDir.isDirectory()); |
| |
| File loggingProperties = new File(webInfClassesDir, "logging.properties"); |
| try (PrintWriter writer = new PrintWriter(loggingProperties)) { |
| writer.write("handlers = org.apache.juli.FileHandler\r\n" + |
| "org.apache.juli.FileHandler.level = " + logLevel + "\r\n" + |
| "org.apache.juli.FileHandler.directory = " + logsDir.getAbsolutePath().replace("\\", "\\\\") + "\r\n" + |
| "org.apache.juli.FileHandler.prefix = juli" + appId + ".\r\n" |
| ); |
| } |
| |
| Context context = tomcat.addContext("/juli" + appId, appDir.getAbsolutePath()); |
| Tomcat.addServlet(context, "test", new HttpServlet() { |
| @Serial |
| private static final long serialVersionUID = 1L; |
| |
| @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { |
| Logger root = Logger.getLogger(HANDLER_ISOLATION_LOGGER); |
| root.info("JULI-" + appId + "-INFO"); |
| } |
| }); |
| context.addServletMappingDecoded("/test", "test"); |
| return new ConstructAppResult(logsDir, context); |
| } |
| private record ConstructAppResult(File logsDir, Context context) {} |
| private static File findLogFile(File dir, String prefix) throws Exception { |
| List<File> files; |
| int deadlineCounter = 0; |
| do { |
| try (Stream<Path> paths = Files.walk(dir.toPath())) { |
| files = paths.map(Path::toFile).filter(f -> f.isFile() && f.getName().startsWith(prefix)).toList(); |
| } |
| if (deadlineCounter > 0) { |
| Thread.sleep(100); |
| } |
| } while (++deadlineCounter < 3); |
| return files.isEmpty() ? null : files.getFirst(); |
| } |
| } |