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