001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.appender.rolling; 018 019 import java.io.File; 020 import java.io.FileNotFoundException; 021 import java.io.IOException; 022 import java.io.OutputStream; 023 import java.io.RandomAccessFile; 024 import java.nio.ByteBuffer; 025 026 import org.apache.logging.log4j.core.appender.AppenderRuntimeException; 027 import org.apache.logging.log4j.core.appender.ManagerFactory; 028 029 /** 030 * Extends RollingFileManager but instead of using a buffered output stream, 031 * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the 032 * I/O. 033 */ 034 public class FastRollingFileManager extends RollingFileManager { 035 private static final int DEFAULT_BUFFER_SIZE = 256 * 1024; 036 037 private static final FastRollingFileManagerFactory FACTORY = new FastRollingFileManagerFactory(); 038 039 private final boolean isImmediateFlush; 040 private RandomAccessFile randomAccessFile; 041 private final ByteBuffer buffer; 042 private ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>(); 043 044 public FastRollingFileManager(RandomAccessFile raf, String fileName, 045 String pattern, OutputStream os, boolean append, 046 boolean immediateFlush, long size, long time, 047 TriggeringPolicy policy, RolloverStrategy strategy, 048 String advertiseURI) { 049 super(fileName, pattern, os, append, size, time, policy, strategy, 050 advertiseURI); 051 this.isImmediateFlush = immediateFlush; 052 this.randomAccessFile = raf; 053 isEndOfBatch.set(Boolean.FALSE); 054 055 // TODO make buffer size configurable? 056 buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); 057 } 058 059 public static FastRollingFileManager getFastRollingFileManager( 060 String fileName, String filePattern, boolean isAppend, 061 boolean immediateFlush, TriggeringPolicy policy, 062 RolloverStrategy strategy, String advertiseURI) { 063 return (FastRollingFileManager) getManager(fileName, new FactoryData( 064 filePattern, isAppend, immediateFlush, policy, strategy, 065 advertiseURI), FACTORY); 066 } 067 068 public Boolean isEndOfBatch() { 069 return isEndOfBatch.get(); 070 } 071 072 public void setEndOfBatch(boolean isEndOfBatch) { 073 this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch)); 074 } 075 076 @Override 077 protected synchronized void write(byte[] bytes, int offset, int length) { 078 super.write(bytes, offset, length); // writes to dummy output stream 079 080 if (length > buffer.remaining()) { 081 flush(); 082 } 083 buffer.put(bytes, offset, length); 084 if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) { 085 flush(); 086 } 087 } 088 089 @Override 090 protected void createFileAfterRollover() throws IOException { 091 this.randomAccessFile = new RandomAccessFile(getFileName(), "rw"); 092 if (isAppend()) { 093 randomAccessFile.seek(randomAccessFile.length()); 094 } 095 } 096 097 @Override 098 public void flush() { 099 buffer.flip(); 100 try { 101 randomAccessFile.write(buffer.array(), 0, buffer.limit()); 102 } catch (IOException ex) { 103 String msg = "Error writing to RandomAccessFile " + getName(); 104 throw new AppenderRuntimeException(msg, ex); 105 } 106 buffer.clear(); 107 } 108 109 @Override 110 public void close() { 111 flush(); 112 try { 113 randomAccessFile.close(); 114 } catch (final IOException ex) { 115 LOGGER.error("Unable to close RandomAccessFile " + getName() + ". " 116 + ex); 117 } 118 } 119 120 /** 121 * Factory to create a FastRollingFileManager. 122 */ 123 private static class FastRollingFileManagerFactory implements 124 ManagerFactory<FastRollingFileManager, FactoryData> { 125 126 /** 127 * Create the FastRollingFileManager. 128 * 129 * @param name The name of the entity to manage. 130 * @param data The data required to create the entity. 131 * @return a RollingFileManager. 132 */ 133 public FastRollingFileManager createManager(String name, 134 FactoryData data) { 135 File file = new File(name); 136 final File parent = file.getParentFile(); 137 if (null != parent && !parent.exists()) { 138 parent.mkdirs(); 139 } 140 if (!data.append) { 141 file.delete(); 142 } 143 long size = data.append ? file.length() : 0; 144 long time = file.lastModified(); 145 146 RandomAccessFile raf; 147 try { 148 raf = new RandomAccessFile(name, "rw"); 149 return new FastRollingFileManager(raf, name, data.pattern, 150 new DummyOutputStream(), data.append, 151 data.immediateFlush, size, time, data.policy, 152 data.strategy, data.advertiseURI); 153 } catch (FileNotFoundException ex) { 154 LOGGER.error("FastRollingFileManager (" + name + ") " + ex); 155 } 156 return null; 157 } 158 } 159 160 /** {@code OutputStream} subclass that does not write anything. */ 161 private static class DummyOutputStream extends OutputStream { 162 @Override 163 public void write(int b) throws IOException { 164 } 165 166 @Override 167 public void write(byte[] b, int off, int len) throws IOException { 168 } 169 } 170 171 /** 172 * Factory data. 173 */ 174 private static class FactoryData { 175 private final String pattern; 176 private final boolean append; 177 private final boolean immediateFlush; 178 private final TriggeringPolicy policy; 179 private final RolloverStrategy strategy; 180 private final String advertiseURI; 181 182 /** 183 * Create the data for the factory. 184 * 185 * @param pattern The pattern. 186 * @param append The append flag. 187 * @param immediateFlush 188 */ 189 public FactoryData(String pattern, boolean append, 190 boolean immediateFlush, TriggeringPolicy policy, 191 RolloverStrategy strategy, String advertiseURI) { 192 this.pattern = pattern; 193 this.append = append; 194 this.immediateFlush = immediateFlush; 195 this.policy = policy; 196 this.strategy = strategy; 197 this.advertiseURI = advertiseURI; 198 } 199 } 200 201 }