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 org.apache.logging.log4j.core.LogEvent; 020 import org.apache.logging.log4j.core.appender.FileManager; 021 import org.apache.logging.log4j.core.appender.ManagerFactory; 022 import org.apache.logging.log4j.core.appender.rolling.helper.Action; 023 import org.apache.logging.log4j.core.appender.rolling.helper.AbstractAction; 024 025 import java.io.BufferedOutputStream; 026 import java.io.File; 027 import java.io.FileNotFoundException; 028 import java.io.FileOutputStream; 029 import java.io.IOException; 030 import java.io.OutputStream; 031 import java.util.concurrent.Semaphore; 032 033 /** 034 * The Rolling File Manager. 035 */ 036 public class RollingFileManager extends FileManager { 037 038 private static RollingFileManagerFactory factory = new RollingFileManagerFactory(); 039 040 private long size; 041 private long initialTime; 042 private final PatternProcessor processor; 043 private final Semaphore semaphore = new Semaphore(1); 044 045 protected RollingFileManager(String fileName, String pattern, OutputStream os, boolean append, long size, 046 long time) { 047 super(fileName, os, append, false); 048 this.size = size; 049 this.initialTime = time; 050 processor = new PatternProcessor(pattern); 051 } 052 053 /** 054 * Returns a RollingFileManager. 055 * @param fileName The file name. 056 * @param pattern The pattern for rolling file. 057 * @param append true if the file should be appended to. 058 * @param bufferedIO true if data should be buffered. 059 * @return A RollingFileManager. 060 */ 061 public static RollingFileManager getFileManager(String fileName, String pattern, boolean append, 062 boolean bufferedIO) { 063 064 return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append, 065 bufferedIO), factory); 066 } 067 068 @Override 069 protected synchronized void write(byte[] bytes, int offset, int length) { 070 size += length; 071 super.write(bytes, offset, length); 072 } 073 074 /** 075 * Returns the current size of the file. 076 * @return The size of the file in bytes. 077 */ 078 public long getFileSize() { 079 return size; 080 } 081 082 /** 083 * Returns the time the file was created. 084 * @return The time the file was created. 085 */ 086 public long getFileTime() { 087 return initialTime; 088 } 089 090 /** 091 * Determine if a rollover should occur. 092 * @param event The LogEvent. 093 * @param policy The TriggeringPolicy. 094 * @param strategy The RolloverStrategy. 095 */ 096 public synchronized void checkRollover(LogEvent event, TriggeringPolicy policy, RolloverStrategy strategy) { 097 if (policy.isTriggeringEvent(event) && rollover(strategy)) { 098 try { 099 size = 0; 100 initialTime = System.currentTimeMillis(); 101 OutputStream os = new FileOutputStream(getFileName(), isAppend()); 102 setOutputStream(os); 103 } catch (FileNotFoundException ex) { 104 LOGGER.error("FileManager (" + getFileName() + ") " + ex); 105 } 106 } 107 } 108 109 /** 110 * Returns the pattern processor. 111 * @return The PatternProcessor. 112 */ 113 public PatternProcessor getProcessor() { 114 return processor; 115 } 116 117 private boolean rollover(RolloverStrategy strategy) { 118 119 try { 120 // Block until the asynchronous operation is completed. 121 semaphore.acquire(); 122 } catch (InterruptedException ie) { 123 LOGGER.error("Thread interrupted while attempting to check rollover", ie); 124 return false; 125 } 126 127 boolean success = false; 128 Thread thread = null; 129 130 try { 131 RolloverDescription descriptor = strategy.rollover(this); 132 133 if (descriptor != null) { 134 135 close(); 136 137 if (descriptor.getSynchronous() != null) { 138 139 try { 140 success = descriptor.getSynchronous().execute(); 141 } catch (Exception ex) { 142 LOGGER.error("Error in synchronous task", ex); 143 } 144 } 145 146 if (success && descriptor.getAsynchronous() != null) { 147 thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this)); 148 thread.start(); 149 } 150 return true; 151 } 152 return false; 153 } finally { 154 if (thread == null) { 155 semaphore.release(); 156 } 157 } 158 159 } 160 161 /** 162 * Performs actions asynchronously. 163 */ 164 private static class AsyncAction extends AbstractAction { 165 166 private final Action action; 167 private final RollingFileManager manager; 168 169 /** 170 * Constructor. 171 * @param act The action to perform. 172 * @param manager The manager. 173 */ 174 public AsyncAction(Action act, RollingFileManager manager) { 175 this.action = act; 176 this.manager = manager; 177 } 178 179 /** 180 * Perform an action. 181 * 182 * @return true if action was successful. A return value of false will cause 183 * the rollover to be aborted if possible. 184 * @throws java.io.IOException if IO error, a thrown exception will cause the rollover 185 * to be aborted if possible. 186 */ 187 @Override 188 public boolean execute() throws IOException { 189 try { 190 return action.execute(); 191 } finally { 192 manager.semaphore.release(); 193 } 194 } 195 196 /** 197 * Cancels the action if not already initialized or waits till completion. 198 */ 199 @Override 200 public void close() { 201 action.close(); 202 } 203 204 /** 205 * Determines if action has been completed. 206 * 207 * @return true if action is complete. 208 */ 209 @Override 210 public boolean isComplete() { 211 return action.isComplete(); 212 } 213 } 214 215 /** 216 * Factory data. 217 */ 218 private static class FactoryData { 219 private String pattern; 220 private boolean append; 221 private boolean bufferedIO; 222 223 /** 224 * Create the data for the factory. 225 * @param pattern The pattern. 226 * @param append The append flag. 227 * @param bufferedIO The bufferedIO flag. 228 */ 229 public FactoryData(String pattern, boolean append, boolean bufferedIO) { 230 this.pattern = pattern; 231 this.append = append; 232 this.bufferedIO = bufferedIO; 233 } 234 } 235 236 /** 237 * Factory to create a RollingFileManager. 238 */ 239 private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> { 240 241 /** 242 * Create the RollingFileManager. 243 * @param name The name of the entity to manage. 244 * @param data The data required to create the entity. 245 * @return a RollingFileManager. 246 */ 247 public RollingFileManager createManager(String name, FactoryData data) { 248 File file = new File(name); 249 final File parent = file.getParentFile(); 250 if (null != parent && !parent.exists()) { 251 parent.mkdirs(); 252 } 253 try { 254 file.createNewFile(); 255 } catch (IOException ioe) { 256 LOGGER.error("Unable to create file " + name, ioe); 257 return null; 258 } 259 long size = data.append ? file.length() : 0; 260 long time = file.lastModified(); 261 262 OutputStream os; 263 try { 264 os = new FileOutputStream(name, data.append); 265 if (data.bufferedIO) { 266 os = new BufferedOutputStream(os); 267 } 268 return new RollingFileManager(name, data.pattern, os, data.append, size, time); 269 } catch (FileNotFoundException ex) { 270 LOGGER.error("FileManager (" + name + ") " + ex); 271 } 272 return null; 273 } 274 } 275 276 }