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