/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * Licensed 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.openspaces.usm; import java.io.File; import java.io.FilenameFilter; import java.util.Date; import java.util.logging.ConsoleHandler; import java.util.logging.Handler; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.RollingFileAppender; import org.cloudifysource.usm.tail.RollingFileAppenderTailer; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.BlockJUnit4ClassRunner; import org.springframework.util.FileSystemUtils; @RunWith(BlockJUnit4ClassRunner.class) public class USMRollingFileAppenderTailerTest { private static final int RFAT_SAMPLING_RATE_MILLISECOND = 100; private static final int NUMBER_OF_LINES_TO_LOG = 30; private static final int EXPECTED_NUMBER_OF_FILE_PARTS = 2; // This is the pattern that Log4j will use when logging: // %d{ISO8601} %5p %c{1}:%L - %m%n private static final String LOG_PATTERN = "%d{ISO8601} %5p [%l] - %m%n"; private static final int LOG_IO_BUFFER_SIZE_BYTES = 1024; private static final String LOG_FILENAME = "my.log"; private static final int MAX_LOG_BACKUP_FILES = 20; private static final String MAX_LOG_FILE_SIZE = "3KB"; private static final double MAX_LOG_FILE_SIZE_DOUBLE = 3; // We use the root logger for everything so we can capture all of the output // from shared libraries that use Log4j too public static final Logger testLogger = Logger.getRootLogger(); private java.util.logging.Logger logger = java.util.logging.Logger.getLogger(this.getClass().getName()); private String logsDirectory = new File(System.getProperty("java.io.tmpdir"), "testRollingFileAppenderTailer").getAbsolutePath(); private String regex = "my.*\\.log"; private RollingFileAppender rfp; @Before public void before() throws Exception { //add console handler for the test log. logger.addHandler(new ConsoleHandler()); // Where the logs will go. final File logDir = new File(logsDirectory); FileSystemUtils.deleteRecursively(logDir); logDir.mkdirs(); final File logFile = new File(logDir, String.format("%s%s", System.getProperty("file.separator"), LOG_FILENAME)); // Create a new pattern layout with our requested log pattern. final PatternLayout pl = new PatternLayout(LOG_PATTERN); rfp = new RollingFileAppender(pl, logFile.getCanonicalPath(), true); // We want the logger to flush its output to the log file // stream immediately; if you don't have this set, then // Log4j will buffer the log file output. rfp.setImmediateFlush(true); rfp.setBufferedIO(false); rfp.setBufferSize(LOG_IO_BUFFER_SIZE_BYTES); // Set the Max number of files and max size of each log // file to keep around. rfp.setMaxBackupIndex(MAX_LOG_BACKUP_FILES); rfp.setMaxFileSize(MAX_LOG_FILE_SIZE); // Set the default level of this logger. testLogger.setLevel(Level.INFO); // This logger will use the rolling appender. testLogger.addAppender(rfp); testLogger.getAllAppenders(); testLogger.info("Log directory: " + logDir.getAbsolutePath()); } @Ignore @Test public void rollingFileAppenderTailer() throws InterruptedException { //added handler to monitor tailing of the new file. StringHandler stringHandler = addHandlerToJavaUtilsLogger(); //Start tailing the logs folder. RollingFileAppenderTailer.start(logsDirectory, this.regex, RFAT_SAMPLING_RATE_MILLISECOND); //Start the log4j logging. startLogging(); assertFileRolling(); assertJavaUtilsTailedLogging(stringHandler.getLoggedMessages()); } @After public void after() { if (rfp != null) { rfp.close(); } FileSystemUtils.deleteRecursively(new File(logsDirectory)); } private void assertFileRolling() { File folder = new File(logsDirectory); //Get list of files according to regex. File[] files = folder.listFiles(new FilenameFilter(){ @Override public boolean accept(File dir, String name){ return java.util.regex.Pattern.matches("my.*\\.log.*", name); } }); //Check file rolling occurred. logger.log(java.util.logging.Level.INFO, "Asserting number of file parts is " + EXPECTED_NUMBER_OF_FILE_PARTS); Assert.assertTrue("Was expecting " + EXPECTED_NUMBER_OF_FILE_PARTS + " files. Got " + files.length, files.length == EXPECTED_NUMBER_OF_FILE_PARTS); //Test file sizes logger.log(java.util.logging.Level.INFO, "Asserting files do not exceed the maximal log file size " + MAX_LOG_FILE_SIZE_DOUBLE + 1); for (File file : files) { long fileSize = file.length(); double fileSizeInKB = (double)fileSize/LOG_IO_BUFFER_SIZE_BYTES; //assert no file is over the size limit defined in the RollingFileAppender. Assert.assertTrue("Expecting filesize " + fileSize + " to be smaller then max allowed size " + MAX_LOG_FILE_SIZE_DOUBLE ,fileSizeInKB < MAX_LOG_FILE_SIZE_DOUBLE + 1); } } private void assertJavaUtilsTailedLogging(String loggedMessages) { int counter = 0; //use "[\\r\\n]+ regular expression to filter out new lines without empty lines. String seporatedLines[] = loggedMessages.split("[\\r\\n]+"); counter += seporatedLines.length; //worst case is that the tailer will miss the last line of a file before it's being rolled, //So we expect for the number of lines to be [Total number of lines - number of files] in the worst case. logger.log(java.util.logging.Level.INFO, "asserting number of lines tailed. expecting " + (NUMBER_OF_LINES_TO_LOG - counter) + " to be smaller then " + EXPECTED_NUMBER_OF_FILE_PARTS); Assert.assertTrue(NUMBER_OF_LINES_TO_LOG - counter < EXPECTED_NUMBER_OF_FILE_PARTS); } /** * write some information to the log. this writing should produce 7 log files * all of size that is not bigger then 128KB. */ private void startLogging() { for(int i = 0; i < NUMBER_OF_LINES_TO_LOG; i++){ testLogger.info( "Epoch is " + new Date().getTime() + " " + i); try { Thread.sleep(RFAT_SAMPLING_RATE_MILLISECOND * 2); } catch (InterruptedException e) { // Do nothing } } } //Add a logging handler. private StringHandler addHandlerToJavaUtilsLogger() { java.util.logging.Logger logger = java.util.logging.Logger.getLogger(""); StringHandler sh = new StringHandler(); logger.addHandler(sh); Handler[] handlers = logger.getHandlers(); for (Handler handler : handlers) { if (handler instanceof ConsoleHandler){ logger.removeHandler(handler); break; } } return sh; } }