diff options
author | Morten Tokle <mortent@verizonmedia.com> | 2021-01-25 15:08:03 +0100 |
---|---|---|
committer | Morten Tokle <mortent@verizonmedia.com> | 2021-01-25 15:08:03 +0100 |
commit | 663bb9136a91e8d2c7eb729728cece56b661bb11 (patch) | |
tree | 976f3aae31f53d22750221cede8bec9ab73b6c57 | |
parent | bab45d46a29cf43cc53b8c72102f12062ad88a82 (diff) |
Move file handling logic to logthread
-rw-r--r-- | jdisc_http_service/src/main/java/com/yahoo/container/logging/LogFileHandler.java | 625 | ||||
-rw-r--r-- | jdisc_http_service/src/test/java/com/yahoo/container/logging/LogFileHandlerTestCase.java | 12 |
2 files changed, 322 insertions, 315 deletions
diff --git a/jdisc_http_service/src/main/java/com/yahoo/container/logging/LogFileHandler.java b/jdisc_http_service/src/main/java/com/yahoo/container/logging/LogFileHandler.java index 7bd719c161f..f85d58524a8 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/container/logging/LogFileHandler.java +++ b/jdisc_http_service/src/main/java/com/yahoo/container/logging/LogFileHandler.java @@ -34,78 +34,13 @@ import java.util.zip.GZIPOutputStream; * @author Bob Travis * @author bjorncs */ -class LogFileHandler <LOGTYPE> { +class LogFileHandler <LOGTYPE> { - enum Compression { NONE, GZIP, ZSTD } + enum Compression {NONE, GZIP, ZSTD} private final static Logger logger = Logger.getLogger(LogFileHandler.class.getName()); - private final Compression compression; - private final long[] rotationTimes; - private final String filePattern; // default to current directory, ms time stamp - private final String symlinkName; - private final ArrayBlockingQueue<Operation<LOGTYPE>> logQueue = new ArrayBlockingQueue<>(100000); - private final ExecutorService executor = Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory("logfilehandler.compression")); - private final NativeIO nativeIO = new NativeIO(); - private final LogThread<LOGTYPE> logThread; - - private volatile FileOutputStream currentOutputStream = null; - private volatile long nextRotationTime = 0; - private volatile String fileName; - private volatile long lastDropPosition = 0; - - private final LogWriter<LOGTYPE> logWriter; - - static private class LogThread<LOGTYPE> extends Thread { - final LogFileHandler<LOGTYPE> logFileHandler; - long lastFlush = 0; - LogThread(LogFileHandler<LOGTYPE> logFile) { - super("Logger"); - setDaemon(true); - logFileHandler = logFile; - } - @Override - public void run() { - try { - storeLogRecords(); - } catch (InterruptedException e) { - } catch (Exception e) { - com.yahoo.protect.Process.logAndDie("Failed storing log records", e); - } - - logFileHandler.internalFlush(); - } - - private void storeLogRecords() throws InterruptedException { - while (!isInterrupted()) { - Operation<LOGTYPE> r = logFileHandler.logQueue.poll(100, TimeUnit.MILLISECONDS); - if (r!=null) { - if (r.type == Operation.Type.flush) { - logFileHandler.internalFlush(); - } else if (r.type == Operation.Type.close) { - logFileHandler.internalClose(); - } else if (r.type == Operation.Type.rotate) { - logFileHandler.internalRotateNow(); - lastFlush = System.nanoTime(); - } else if (r.type == Operation.Type.log) { - logFileHandler.internalPublish(r.log.get()); - flushIfOld(3, TimeUnit.SECONDS); - } - r.countDownLatch.countDown(); - } else { - flushIfOld(100, TimeUnit.MILLISECONDS); - } - } - } - - private void flushIfOld(long age, TimeUnit unit) { - long now = System.nanoTime(); - if (TimeUnit.NANOSECONDS.toMillis(now - lastFlush) > unit.toMillis(age)) { - logFileHandler.internalFlush(); - lastFlush = now; - } - } - } + final LogThread<LOGTYPE> logThread; LogFileHandler(Compression compression, String filePattern, String rotationTimes, String symlinkName, LogWriter<LOGTYPE> logWriter) { this(compression, filePattern, calcTimesMinutes(rotationTimes), symlinkName, logWriter); @@ -115,16 +50,13 @@ class LogFileHandler <LOGTYPE> { Compression compression, String filePattern, long[] rotationTimes, - String symlinkName, LogWriter<LOGTYPE> logWriter) { - this.compression = compression; - this.filePattern = filePattern; - this.rotationTimes = rotationTimes; - this.symlinkName = (symlinkName != null && !symlinkName.isBlank()) ? symlinkName : null; - this.logWriter = logWriter; - this.logThread = new LogThread<>(this); + String symlinkName, + LogWriter<LOGTYPE> logWriter) { + this.logThread = new LogThread<>(logWriter, filePattern, compression, rotationTimes, symlinkName); this.logThread.start(); } + /** * Sends logrecord to file, first rotating file if needed. * @@ -138,232 +70,48 @@ class LogFileHandler <LOGTYPE> { addOperationAndWait(new Operation<>(Operation.Type.flush)); } - private synchronized void internalFlush() { - try { - FileOutputStream currentOut = this.currentOutputStream; - if (currentOut != null) { - if (compression == Compression.GZIP) { - long newPos = currentOut.getChannel().position(); - if (newPos > lastDropPosition + 102400) { - nativeIO.dropPartialFileFromCache(currentOut.getFD(), lastDropPosition, newPos, true); - lastDropPosition = newPos; - } - } else { - currentOut.flush(); - } - } - } catch (IOException e) { - logger.warning("Failed dropping from cache : " + Exceptions.toMessageString(e)); - } + /** + * Force file rotation now, independent of schedule. + */ + void rotateNow() { + addOperationAndWait(new Operation<>(Operation.Type.rotate)); } public void close() { addOperationAndWait(new Operation<>(Operation.Type.close)); } - public void internalClose() { - try { - internalFlush(); - FileOutputStream currentOut = this.currentOutputStream; - if (currentOut != null) currentOut.close(); - } catch (Exception e) { - logger.log(Level.WARNING, "Got error while closing log file", e); - } - } - - private void internalPublish(LOGTYPE r) { - // first check to see if new file needed. - // if so, use this.internalRotateNow() to do it - - long now = System.currentTimeMillis(); - if (nextRotationTime <= 0) { - nextRotationTime = getNextRotationTime(now); // lazy initialization - } - if (now > nextRotationTime || currentOutputStream == null) { - internalRotateNow(); - } - try { - FileOutputStream out = this.currentOutputStream; - logWriter.write(r, out); - out.write('\n'); - } catch (IOException e) { - logger.warning("Failed writing log record: " + Exceptions.toMessageString(e)); - } - } - private void addOperation(Operation<LOGTYPE> op) { try { - logQueue.put(op); + logThread.logQueue.put(op); } catch (InterruptedException e) { } } + private void addOperationAndWait(Operation<LOGTYPE> op) { try { - logQueue.put(op); + logThread.logQueue.put(op); op.countDownLatch.await(); } catch (InterruptedException e) { } } /** - * Find next rotation after specified time. - * - * @param now the specified time; if zero, current time is used. - * @return the next rotation time - */ - long getNextRotationTime (long now) { - if (now <= 0) { - now = System.currentTimeMillis(); - } - long nowTod = timeOfDayMillis(now); - long next = 0; - for (long rotationTime : rotationTimes) { - if (nowTod < rotationTime) { - next = rotationTime-nowTod + now; - break; - } - } - if (next == 0) { // didn't find one -- use 1st time 'tomorrow' - next = rotationTimes[0]+lengthOfDayMillis-nowTod + now; - } - - return next; - } - - private void checkAndCreateDir(String pathname) { - int lastSlash = pathname.lastIndexOf("/"); - if (lastSlash > -1) { - String pathExcludingFilename = pathname.substring(0, lastSlash); - File filepath = new File(pathExcludingFilename); - if (!filepath.exists()) { - filepath.mkdirs(); - } - } - } - - /** - * Force file rotation now, independent of schedule. + * Flushes all queued messages, interrupts the log thread in this and + * waits for it to end before returning */ - void rotateNow () { - addOperationAndWait(new Operation<>(Operation.Type.rotate)); - } - - // Throw InterruptedException upwards rather than relying on isInterrupted to stop the thread as - // isInterrupted() returns false after interruption in p.waitFor - private void internalRotateNow() { - // figure out new file name, then - // use super.setOutputStream to switch to a new file - - String oldFileName = fileName; - long now = System.currentTimeMillis(); - fileName = LogFormatter.insertDate(filePattern, now); - internalFlush(); - - try { - checkAndCreateDir(fileName); - FileOutputStream os = new FileOutputStream(fileName, true); // append mode, for safety - currentOutputStream = os; - lastDropPosition = 0; - LogFileDb.nowLoggingTo(fileName); - } - catch (IOException e) { - throw new RuntimeException("Couldn't open log file '" + fileName + "'", e); - } - - createSymlinkToCurrentFile(); - - nextRotationTime = 0; //figure it out later (lazy evaluation) - if ((oldFileName != null)) { - File oldFile = new File(oldFileName); - if (oldFile.exists()) { - if (compression != Compression.NONE) { - executor.execute(() -> runCompression(oldFile, compression)); - } else { - nativeIO.dropFileFromCache(oldFile); - } - } - } - } - - - private static void runCompression(File oldFile, Compression compression) { - switch (compression) { - case ZSTD: - runCompressionZstd(oldFile.toPath()); - break; - case GZIP: - runCompressionGzip(oldFile); - break; - default: - throw new IllegalArgumentException("Unknown compression " + compression); - } - } - - private static void runCompressionZstd(Path oldFile) { - try { - Path compressedFile = Paths.get(oldFile.toString() + ".zst"); - Files.createFile(compressedFile); - int bufferSize = 0x400000; // 4M - byte[] buffer = new byte[bufferSize]; - try (ZstdOuputStream out = new ZstdOuputStream(Files.newOutputStream(compressedFile), bufferSize); - InputStream in = Files.newInputStream(oldFile)) { - int read; - while ((read = in.read(buffer)) >= 0) { - out.write(buffer, 0, read); - } - out.flush(); - } - Files.delete(oldFile); - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to compress log file with zstd: " + oldFile, e); - } - } - - private static void runCompressionGzip(File oldFile) { - File gzippedFile = new File(oldFile.getPath() + ".gz"); - try (GZIPOutputStream compressor = new GZIPOutputStream(new FileOutputStream(gzippedFile), 0x100000); - FileInputStream inputStream = new FileInputStream(oldFile)) - { - byte [] buffer = new byte[0x400000]; // 4M buffer - - long totalBytesRead = 0; - NativeIO nativeIO = new NativeIO(); - for (int read = inputStream.read(buffer); read > 0; read = inputStream.read(buffer)) { - compressor.write(buffer, 0, read); - nativeIO.dropPartialFileFromCache(inputStream.getFD(), totalBytesRead, read, false); - totalBytesRead += read; - } - compressor.finish(); - compressor.flush(); - - oldFile.delete(); - nativeIO.dropFileFromCache(gzippedFile); - } catch (IOException e) { - logger.warning("Got '" + e + "' while compressing '" + oldFile.getPath() + "'."); - } - } - - /** Name files by date - create a symlink with a constant name to the newest file */ - private void createSymlinkToCurrentFile() { - if (symlinkName == null) return; - File f = new File(fileName); - File f2 = new File(f.getParent(), symlinkName); - String [] cmd = new String[]{"/bin/ln", "-sf", f.getName(), f2.getPath()}; + void shutdown() { + logThread.interrupt(); try { - int retval = new ProcessExecuter().exec(cmd).getFirst(); - // Detonator pattern: Think of all the fun we can have if ln isn't what we - // think it is, if it doesn't return, etc, etc - if (retval != 0) { - logger.warning("Command '" + Arrays.toString(cmd) + "' + failed with exitcode=" + retval); - } - } catch (IOException e) { - logger.warning("Got '" + e + "' while doing'" + Arrays.toString(cmd) + "'."); + logThread.join(); + logThread.executor.shutdown(); + logThread.executor.awaitTermination(600, TimeUnit.SECONDS); + } catch (InterruptedException e) { } } /** * Calculate rotation times array, given times in minutes, as "0 60 ..." - * */ private static long[] calcTimesMinutes(String times) { ArrayList<Long> list = new ArrayList<>(50); @@ -371,33 +119,36 @@ class LogFileHandler <LOGTYPE> { boolean etc = false; while (i < times.length()) { - if (times.charAt(i) == ' ') { i++; continue; } // skip spaces + if (times.charAt(i) == ' ') { + i++; + continue; + } // skip spaces int j = i; // start of string i = times.indexOf(' ', i); if (i == -1) i = times.length(); - if (times.charAt(j) == '.' && times.substring(j,i).equals("...")) { // ... + if (times.charAt(j) == '.' && times.substring(j, i).equals("...")) { // ... etc = true; break; } - list.add(Long.valueOf(times.substring(j,i))); + list.add(Long.valueOf(times.substring(j, i))); } int size = list.size(); long[] longtimes = new long[size]; - for (i = 0; i<size; i++) { + for (i = 0; i < size; i++) { longtimes[i] = list.get(i) // pick up value in minutes past midnight * 60000; // and multiply to get millis } if (etc) { // fill out rest of day, same as final interval - long endOfDay = 24*60*60*1000; - long lasttime = longtimes[size-1]; - long interval = lasttime - longtimes[size-2]; - long moreneeded = (endOfDay - lasttime)/interval; + long endOfDay = 24 * 60 * 60 * 1000; + long lasttime = longtimes[size - 1]; + long interval = lasttime - longtimes[size - 2]; + long moreneeded = (endOfDay - lasttime) / interval; if (moreneeded > 0) { - int newsize = size + (int)moreneeded; + int newsize = size + (int) moreneeded; long[] temp = new long[newsize]; - for (i=0; i<size; i++) { + for (i = 0; i < size; i++) { temp[i] = longtimes[i]; } while (size < newsize) { @@ -411,37 +162,296 @@ class LogFileHandler <LOGTYPE> { return longtimes; } - // Support staff :-) - private static final long lengthOfDayMillis = 24*60*60*1000; // ? is this close enough ? - - private static long timeOfDayMillis ( long time ) { - return time % lengthOfDayMillis; + /** + * Only for unit testing. Do not use. + */ + String getFileName() { + return logThread.fileName; } /** - * Flushes all queued messages, interrupts the log thread in this and - * waits for it to end before returning + * Handle logging and file operations */ - void shutdown() { - logThread.interrupt(); - try { - logThread.join(); - executor.shutdown(); - executor.awaitTermination(600, TimeUnit.SECONDS); + static class LogThread<LOGTYPE> extends Thread { + long lastFlush = 0; + private FileOutputStream currentOutputStream = null; + private long nextRotationTime = 0; + private final String filePattern; // default to current directory, ms time stamp + private String fileName; + private long lastDropPosition = 0; + private final LogWriter<LOGTYPE> logWriter; + private final ArrayBlockingQueue<Operation<LOGTYPE>> logQueue = new ArrayBlockingQueue<>(100000); + private final Compression compression; + private final long[] rotationTimes; + private final String symlinkName; + private final ExecutorService executor = Executors.newCachedThreadPool(ThreadFactoryFactory.getDaemonThreadFactory("logfilehandler.compression")); + private final NativeIO nativeIO = new NativeIO(); + + + LogThread(LogWriter<LOGTYPE> logWriter, String filePattern, Compression compression, long[] rotationTimes, String symlinkName) { + super("Logger"); + setDaemon(true); + this.logWriter = logWriter; + this.filePattern = filePattern; + this.compression = compression; + this.rotationTimes = rotationTimes; + this.symlinkName = (symlinkName != null && !symlinkName.isBlank()) ? symlinkName : null; } - catch (InterruptedException e) { + + @Override + public void run() { + try { + storeLogRecords(); + } catch (InterruptedException e) { + } catch (Exception e) { + com.yahoo.protect.Process.logAndDie("Failed storing log records", e); + } + + internalFlush(); + } + + private void storeLogRecords() throws InterruptedException { + while (!isInterrupted()) { + Operation<LOGTYPE> r = logQueue.poll(100, TimeUnit.MILLISECONDS); + if (r != null) { + if (r.type == Operation.Type.flush) { + internalFlush(); + } else if (r.type == Operation.Type.close) { + internalClose(); + } else if (r.type == Operation.Type.rotate) { + internalRotateNow(); + lastFlush = System.nanoTime(); + } else if (r.type == Operation.Type.log) { + internalPublish(r.log.get()); + flushIfOld(3, TimeUnit.SECONDS); + } + r.countDownLatch.countDown(); + } else { + flushIfOld(100, TimeUnit.MILLISECONDS); + } + } + } + + private void flushIfOld(long age, TimeUnit unit) { + long now = System.nanoTime(); + if (TimeUnit.NANOSECONDS.toMillis(now - lastFlush) > unit.toMillis(age)) { + internalFlush(); + lastFlush = now; + } + } + + private synchronized void internalFlush() { + try { + FileOutputStream currentOut = this.currentOutputStream; + if (currentOut != null) { + if (compression == Compression.GZIP) { + long newPos = currentOut.getChannel().position(); + if (newPos > lastDropPosition + 102400) { + nativeIO.dropPartialFileFromCache(currentOut.getFD(), lastDropPosition, newPos, true); + lastDropPosition = newPos; + } + } else { + currentOut.flush(); + } + } + } catch (IOException e) { + logger.warning("Failed dropping from cache : " + Exceptions.toMessageString(e)); + } + } + + private void internalClose() { + try { + internalFlush(); + FileOutputStream currentOut = this.currentOutputStream; + if (currentOut != null) currentOut.close(); + } catch (Exception e) { + logger.log(Level.WARNING, "Got error while closing log file", e); + } + } + + private void internalPublish(LOGTYPE r) { + // first check to see if new file needed. + // if so, use this.internalRotateNow() to do it + + long now = System.currentTimeMillis(); + if (nextRotationTime <= 0) { + nextRotationTime = getNextRotationTime(now); // lazy initialization + } + if (now > nextRotationTime || currentOutputStream == null) { + internalRotateNow(); + } + try { + FileOutputStream out = this.currentOutputStream; + logWriter.write(r, out); + out.write('\n'); + } catch (IOException e) { + logger.warning("Failed writing log record: " + Exceptions.toMessageString(e)); + } + } + + /** + * Find next rotation after specified time. + * + * @param now the specified time; if zero, current time is used. + * @return the next rotation time + */ + long getNextRotationTime(long now) { + if (now <= 0) { + now = System.currentTimeMillis(); + } + long nowTod = timeOfDayMillis(now); + long next = 0; + for (long rotationTime : rotationTimes) { + if (nowTod < rotationTime) { + next = rotationTime - nowTod + now; + break; + } + } + if (next == 0) { // didn't find one -- use 1st time 'tomorrow' + next = rotationTimes[0] + lengthOfDayMillis - nowTod + now; + } + + return next; + } + + private void checkAndCreateDir(String pathname) { + int lastSlash = pathname.lastIndexOf("/"); + if (lastSlash > -1) { + String pathExcludingFilename = pathname.substring(0, lastSlash); + File filepath = new File(pathExcludingFilename); + if (!filepath.exists()) { + filepath.mkdirs(); + } + } + } + + + // Throw InterruptedException upwards rather than relying on isInterrupted to stop the thread as + // isInterrupted() returns false after interruption in p.waitFor + private void internalRotateNow() { + // figure out new file name, then + // use super.setOutputStream to switch to a new file + + String oldFileName = fileName; + long now = System.currentTimeMillis(); + fileName = LogFormatter.insertDate(filePattern, now); + internalFlush(); + + try { + checkAndCreateDir(fileName); + FileOutputStream os = new FileOutputStream(fileName, true); // append mode, for safety + currentOutputStream = os; + lastDropPosition = 0; + LogFileDb.nowLoggingTo(fileName); + } catch (IOException e) { + throw new RuntimeException("Couldn't open log file '" + fileName + "'", e); + } + + createSymlinkToCurrentFile(); + + nextRotationTime = 0; //figure it out later (lazy evaluation) + if ((oldFileName != null)) { + File oldFile = new File(oldFileName); + if (oldFile.exists()) { + if (compression != Compression.NONE) { + executor.execute(() -> runCompression(oldFile, compression)); + } else { + nativeIO.dropFileFromCache(oldFile); + } + } + } + } + + + private static void runCompression(File oldFile, Compression compression) { + switch (compression) { + case ZSTD: + runCompressionZstd(oldFile.toPath()); + break; + case GZIP: + runCompressionGzip(oldFile); + break; + default: + throw new IllegalArgumentException("Unknown compression " + compression); + } + } + + private static void runCompressionZstd(Path oldFile) { + try { + Path compressedFile = Paths.get(oldFile.toString() + ".zst"); + Files.createFile(compressedFile); + int bufferSize = 0x400000; // 4M + byte[] buffer = new byte[bufferSize]; + try (ZstdOuputStream out = new ZstdOuputStream(Files.newOutputStream(compressedFile), bufferSize); + InputStream in = Files.newInputStream(oldFile)) { + int read; + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + out.flush(); + } + Files.delete(oldFile); + } catch (IOException e) { + logger.log(Level.WARNING, "Failed to compress log file with zstd: " + oldFile, e); + } + } + + private static void runCompressionGzip(File oldFile) { + File gzippedFile = new File(oldFile.getPath() + ".gz"); + try (GZIPOutputStream compressor = new GZIPOutputStream(new FileOutputStream(gzippedFile), 0x100000); + FileInputStream inputStream = new FileInputStream(oldFile)) { + byte[] buffer = new byte[0x400000]; // 4M buffer + + long totalBytesRead = 0; + NativeIO nativeIO = new NativeIO(); + for (int read = inputStream.read(buffer); read > 0; read = inputStream.read(buffer)) { + compressor.write(buffer, 0, read); + nativeIO.dropPartialFileFromCache(inputStream.getFD(), totalBytesRead, read, false); + totalBytesRead += read; + } + compressor.finish(); + compressor.flush(); + + oldFile.delete(); + nativeIO.dropFileFromCache(gzippedFile); + } catch (IOException e) { + logger.warning("Got '" + e + "' while compressing '" + oldFile.getPath() + "'."); + } + } + + /** + * Name files by date - create a symlink with a constant name to the newest file + */ + private void createSymlinkToCurrentFile() { + if (symlinkName == null) return; + File f = new File(fileName); + File f2 = new File(f.getParent(), symlinkName); + String[] cmd = new String[]{"/bin/ln", "-sf", f.getName(), f2.getPath()}; + try { + int retval = new ProcessExecuter().exec(cmd).getFirst(); + // Detonator pattern: Think of all the fun we can have if ln isn't what we + // think it is, if it doesn't return, etc, etc + if (retval != 0) { + logger.warning("Command '" + Arrays.toString(cmd) + "' + failed with exitcode=" + retval); + } + } catch (IOException e) { + logger.warning("Got '" + e + "' while doing'" + Arrays.toString(cmd) + "'."); + } + } + + // Support staff :-) + private static final long lengthOfDayMillis = 24 * 60 * 60 * 1000; // ? is this close enough ? + + private static long timeOfDayMillis(long time) { + return time % lengthOfDayMillis; } - } - /** - * Only for unit testing. Do not use. - */ - String getFileName() { - return fileName; } private static class Operation<LOGTYPE> { - enum Type {log, flush, close, rotate}; + enum Type {log, flush, close, rotate} + + ; final Type type; @@ -451,12 +461,15 @@ class LogFileHandler <LOGTYPE> { Operation(Type type) { this(type, Optional.empty()); } + Operation(LOGTYPE log) { this(Type.log, Optional.of(log)); } + private Operation(Type type, Optional<LOGTYPE> log) { this.type = type; this.log = log; } } } + diff --git a/jdisc_http_service/src/test/java/com/yahoo/container/logging/LogFileHandlerTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/container/logging/LogFileHandlerTestCase.java index 7b46e1540f6..cd3c174a12e 100644 --- a/jdisc_http_service/src/test/java/com/yahoo/container/logging/LogFileHandlerTestCase.java +++ b/jdisc_http_service/src/test/java/com/yahoo/container/logging/LogFileHandlerTestCase.java @@ -42,20 +42,14 @@ public class LogFileHandlerTestCase { String pattern = root.getAbsolutePath() + "/logfilehandlertest.%Y%m%d%H%M%S"; long[] rTimes = {1000, 2000, 10000}; - Formatter formatter = new Formatter() { - public String format(LogRecord r) { - DateFormat df = new SimpleDateFormat("yyyy.MM.dd:HH:mm:ss.SSS"); - String timeStamp = df.format(new Date(r.getMillis())); - return ("["+timeStamp+"]" + " " + formatMessage(r) + "\n"); - } - }; LogFileHandler<String> h = new LogFileHandler<>(Compression.NONE, pattern, rTimes, null, new StringLogWriter()); long now = System.currentTimeMillis(); long millisPerDay = 60*60*24*1000; long tomorrowDays = (now / millisPerDay) +1; long tomorrowMillis = tomorrowDays * millisPerDay; - assertThat(tomorrowMillis+1000).isEqualTo(h.getNextRotationTime(tomorrowMillis)); - assertThat(tomorrowMillis+10000).isEqualTo(h.getNextRotationTime(tomorrowMillis+3000)); + + assertThat(tomorrowMillis+1000).isEqualTo(h.logThread.getNextRotationTime(tomorrowMillis)); + assertThat(tomorrowMillis+10000).isEqualTo(h.logThread.getNextRotationTime(tomorrowMillis+3000)); String message = "test"; h.publish(message); h.publish( "another test"); |