/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.logging; import java.util.Set; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import alma.acs.logging.config.LogConfig; import alma.acs.logging.formatters.ConsoleLogFormatter; import alma.acs.logging.level.AcsLogLevelDefinition; /** * * Tests the class ClientLogManager, which creates and configures all ACS loggers. * @author hsommer */ public class ClientLogManagerTest extends junit.framework.TestCase { ClientLogManager clientLogManager; Logger m_logger = null; public ClientLogManagerTest(String name) { super(name); } protected void setUp() throws Exception { clientLogManager = ClientLogManager.getAcsLogManager(); } protected void tearDown() throws Exception { clientLogManager.shutdown(true); } public void testLoggerStructure() { Logger containerLogger = clientLogManager.getLoggerForContainer("test"); assertNotNull(containerLogger); Logger acsRemoteLogger = containerLogger.getParent(); assertNotNull(acsRemoteLogger); assertFalse(acsRemoteLogger.getUseParentHandlers()); Logger rootLogger = acsRemoteLogger.getParent(); assertNotNull(rootLogger); assertNull(rootLogger.getParent()); Handler[] handlers = containerLogger.getHandlers(); assertTrue(handlers.length == 2); StdOutConsoleHandler localHandler = null; AcsLoggingHandler remoteHandler = null; for (Handler handler : handlers) { if (handler instanceof StdOutConsoleHandler) { localHandler = (StdOutConsoleHandler) handler; } else if (handler instanceof AcsLoggingHandler) { remoteHandler = (AcsLoggingHandler) handler; } else { fail("Unexpected handler type " + handler.getClass().getName() + " encountered."); } } assertNotNull(localHandler); assertNotNull(remoteHandler); Formatter localFormatter = localHandler.getFormatter(); assertTrue("localFormatter should not be of type " + localFormatter.getClass().getName(), localFormatter instanceof ConsoleLogFormatter); Handler[] parentHandlers = acsRemoteLogger.getHandlers(); assertTrue(parentHandlers.length == 0); assertEquals(AcsLogLevel.DELOUSE, remoteHandler.getLevel()); containerLogger.info("I'm a good pedigree logger."); } public void testLoggerNameUniqueness() { AcsLogger compLogger1 = clientLogManager.getLoggerForComponent("component"); assertEquals("component", compLogger1.getLoggerName()); AcsLogger compLogger2 = clientLogManager.getLoggerForComponent("component_1"); assertEquals("component_1", compLogger2.getLoggerName()); AcsLogger compLogger3 = clientLogManager.getLoggerForComponent("component"); assertSame(compLogger1, compLogger3); // reuse for same name and same logger type AcsLogger orbLogger1 = clientLogManager.getLoggerForCorba("component", false); // what a stupid name for an orb logger! assertEquals("component_2", orbLogger1.getLoggerName()); AcsLogger containerLogger1 = clientLogManager.getLoggerForContainer("component"); // what a stupid name for a container logger! assertEquals("component_3", containerLogger1.getLoggerName()); } public void testShutdown() { Logger containerLogger = clientLogManager.getLoggerForContainer("test"); containerLogger.info("---------- testShutdown -----------"); clientLogManager.shutdown(true); // should still log locally containerLogger.info("testShutdown: now with local handler only."); // must survive another call to shutdown after this in tearDown() } /** * <ol> * <li>Logs stupid messages to a container logger, while * {@link ClientLogManager#initRemoteLogging(org.omg.CORBA.ORB, Manager, int, boolean)} has not been called. * <li>Calls {@link ClientLogManager#suppressRemoteLogging()} * <li>Forces garbage collection, and verifies that the previously filled log record queue gets finalized within a * reasonable time. * <li>While or after waiting for queue finalization, we log more messages to the same container logger, just to see * that it doesn't fail (should be local-only). * </ol> * The ClientLogManager shutdown called in method tearDown is an important part of this test. * * @throws Exception */ public void testSuppressRemoteLoggingAndCheckGC() throws Exception { final CyclicBarrier sync = new CyclicBarrier(2); // use a special ClientLogManager that has a queue with smart finalize method, // so that we can detect GC ClientLogManager specialClientLogManager = new ClientLogManager() { protected void prepareRemoteLogging() { logQueue = new DispatchingLogQueue() { protected void finalize() throws Exception { System.out.println("*************** DispatchingLogQueue getting finalized. ************"); if (sync == null) { System.err.println("The final local variable 'sync' appeared as 'null' in the finalizer thread. " + "So far this only happened in certain Eclipse JVMs. " + "Turning it into a member variable would solve the problem."); } else { sync.await(); } } }; super.prepareRemoteLogging(); } }; Logger contLog1 = specialClientLogManager.getLoggerForContainer("contLog1"); for (int i = 0; i < 400; i++) { contLog1.info("stupid queued message number " + i); } specialClientLogManager.suppressRemoteLogging(); System.gc(); // System.runFinalization(); for (int i = 0; i < 50; i++) { contLog1.info("stupid local message number " + i); } try { if (sync.getNumberWaiting() == 0) { System.out .println("Local logging finished before LogQueue#finalize was called, despite an earlier call to System.gc(). " + "Now waiting for at most 20 seconds..."); } sync.await(20, TimeUnit.SECONDS); } catch (TimeoutException e) { fail("The disabled log queue was not garbage collected. Probably some bad code changes have created an immortal reference to it, which should be removed."); } } /** */ public void testSuppressCorbaRemoteLogging() { LogConfig config = clientLogManager.getLogConfig(); assertEquals(1, config.getLoggerNames().size()); // the internal logger of ClientLogManager "alma.acs.logging" AcsLogger orbLog1 = clientLogManager.getLoggerForCorba("jacorb_1", true); assertEquals(2, config.getLoggerNames().size()); AcsLogger orbLog1b = clientLogManager.getLoggerForCorba("jacorb_1", true); assertSame(orbLog1, orbLog1b); assertEquals(2, config.getLoggerNames().size()); AcsLogger orbLog2 = clientLogManager.getLoggerForCorba("jacorb_2", true); assertNotSame(orbLog1, orbLog2); assertEquals(3, config.getLoggerNames().size()); // From the container logger, the process name will be derived, and the corba loggers will get it appended AcsLogger contLog = clientLogManager.getLoggerForContainer("myContainer"); Set<String> loggerNames = config.getLoggerNames(); assertEquals(4, loggerNames.size()); assertTrue(loggerNames.contains("alma.acs.logging")); assertTrue(loggerNames.contains("jacorb_1@myContainer")); assertTrue(loggerNames.contains("jacorb_2@myContainer")); assertTrue(loggerNames.contains("myContainer")); config.setDefaultMinLogLevelLocal(AcsLogLevelDefinition.INFO); config.setDefaultMinLogLevel(AcsLogLevelDefinition.DEBUG); // the log level must be the smaller one of stdout and remote level assertEquals(Level.FINE, orbLog1.getLevel()); assertEquals(Level.FINE, orbLog2.getLevel()); assertEquals(Level.FINE, contLog.getLevel()); clientLogManager.suppressCorbaRemoteLogging(); // now for corba loggers the remote handler's level must be infinite, and the local level should determine the logger level. assertEquals(AcsLogLevel.INFO, orbLog1.getLevel()); // todo: check levels directly on the handlers assertEquals(AcsLogLevel.INFO, orbLog2.getLevel()); assertEquals(AcsLogLevel.FINE, contLog.getLevel()); assertEquals(4, loggerNames.size()); } /** * As of ACS 8.1.0, it is not possible to configure application loggers via the CDB. * This test demonstrates how to do it programmatically, as a workaround. */ public void testConfigureApplicationLogger() { LogConfig sharedLogConfig = clientLogManager.getLogConfig(); // Set default log levels before creating the application logger AcsLogLevelDefinition localLevel = AcsLogLevelDefinition.WARNING; AcsLogLevelDefinition remoteLevel = AcsLogLevelDefinition.CRITICAL; sharedLogConfig.setDefaultMinLogLevelLocal(localLevel); sharedLogConfig.setDefaultMinLogLevel(remoteLevel); // get the logger AcsLogger applLogger = clientLogManager.getLoggerForApplication("testApplicationLogger", true); // verify levels set on handlers assertLogLevels(applLogger, localLevel, remoteLevel); // change the default log levels localLevel = AcsLogLevelDefinition.INFO; remoteLevel = AcsLogLevelDefinition.TRACE; sharedLogConfig.setDefaultMinLogLevelLocal(localLevel); sharedLogConfig.setDefaultMinLogLevel(remoteLevel); assertLogLevels(applLogger, localLevel, remoteLevel); assertLogLevels(applLogger, localLevel, remoteLevel); } public void testHibernateLoggerConfig() throws Exception { LogConfig sharedLogConfig = clientLogManager.getLogConfig(); // default levels < WARNING for first hibernate logger, should result in custom levels WARNING sharedLogConfig.setDefaultMinLogLevelLocal(AcsLogLevelDefinition.TRACE); sharedLogConfig.setDefaultMinLogLevel(AcsLogLevelDefinition.DEBUG); AcsLogger hibernateLogger1 = clientLogManager.getLoggerForCorba("hibernate", true); assertLogLevels(hibernateLogger1, AcsLogLevelDefinition.WARNING, AcsLogLevelDefinition.WARNING); assertTrue(sharedLogConfig.hasCustomConfig(hibernateLogger1.getLoggerName())); // default levels >= WARNING for second hibernate logger, should result in custom levels equal to default levels sharedLogConfig.setDefaultMinLogLevelLocal(AcsLogLevelDefinition.ERROR); sharedLogConfig.setDefaultMinLogLevel(AcsLogLevelDefinition.WARNING); AcsLogger hibernateLogger2 = clientLogManager.getLoggerForCorba("hibernateSql", true); assertLogLevels(hibernateLogger2, AcsLogLevelDefinition.ERROR, AcsLogLevelDefinition.WARNING); assertTrue(sharedLogConfig.hasCustomConfig(hibernateLogger2.getLoggerName())); // ClientLogManager should create new hibernate loggers ,no reuse assertNotSame("Hibernate logger reuse should happen inside org.slf4j.impl.ACSLoggerFactory, but not in ClientLogManager.", hibernateLogger1, hibernateLogger2); } /** * Checks that an AcsLogger has local and remote handlers configured with the correct levels. */ private void assertLogLevels(AcsLogger logger, AcsLogLevelDefinition expectedLocalLevel, AcsLogLevelDefinition expectedRemoteLevel) { Handler[] handlers = logger.getHandlers(); assertTrue(handlers.length == 2); StdOutConsoleHandler localHandler = null; AcsLoggingHandler remoteHandler = null; for (Handler handler : handlers) { if (handler instanceof StdOutConsoleHandler) { localHandler = (StdOutConsoleHandler) handler; } else if (handler instanceof AcsLoggingHandler) { remoteHandler = (AcsLoggingHandler) handler; } else { fail("Unexpected handler type " + handler.getClass().getName() + " encountered."); } } assertNotNull(localHandler); assertNotNull(remoteHandler); assertEquals(AcsLogLevel.getLowestMatchingJdkLevel(expectedLocalLevel), localHandler.getLevel()); assertEquals(AcsLogLevel.getLowestMatchingJdkLevel(expectedRemoteLevel), remoteHandler.getLevel()); } }