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