| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache license, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the license for the specific language governing permissions and |
| * limitations under the license. |
| */ |
| package org.apache.logging.log4j.core.appender.rolling; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.Serializable; |
| import java.util.concurrent.Semaphore; |
| |
| import org.apache.logging.log4j.core.Layout; |
| import org.apache.logging.log4j.core.LogEvent; |
| import org.apache.logging.log4j.core.appender.FileManager; |
| import org.apache.logging.log4j.core.appender.ManagerFactory; |
| import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction; |
| import org.apache.logging.log4j.core.appender.rolling.action.Action; |
| |
| /** |
| * The Rolling File Manager. |
| */ |
| public class RollingFileManager extends FileManager { |
| |
| private static RollingFileManagerFactory factory = new RollingFileManagerFactory(); |
| |
| private long size; |
| private long initialTime; |
| private final PatternProcessor patternProcessor; |
| private final Semaphore semaphore = new Semaphore(1); |
| private final TriggeringPolicy triggeringPolicy; |
| private final RolloverStrategy rolloverStrategy; |
| |
| protected RollingFileManager(final String fileName, final String pattern, final OutputStream os, |
| final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy, |
| final RolloverStrategy rolloverStrategy, final String advertiseURI, final Layout<? extends Serializable> layout) { |
| super(fileName, os, append, false, advertiseURI, layout); |
| this.size = size; |
| this.initialTime = time; |
| this.triggeringPolicy = triggeringPolicy; |
| this.rolloverStrategy = rolloverStrategy; |
| this.patternProcessor = new PatternProcessor(pattern); |
| triggeringPolicy.initialize(this); |
| } |
| |
| /** |
| * Returns a RollingFileManager. |
| * @param fileName The file name. |
| * @param pattern The pattern for rolling file. |
| * @param append true if the file should be appended to. |
| * @param bufferedIO true if data should be buffered. |
| * @param policy The TriggeringPolicy. |
| * @param strategy The RolloverStrategy. |
| * @param advertiseURI the URI to use when advertising the file |
| * @param layout The Layout. |
| * @return A RollingFileManager. |
| */ |
| public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append, |
| final boolean bufferedIO, final TriggeringPolicy policy, |
| final RolloverStrategy strategy, final String advertiseURI, |
| final Layout<? extends Serializable> layout) { |
| |
| return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append, |
| bufferedIO, policy, strategy, advertiseURI, layout), factory); |
| } |
| |
| @Override |
| protected synchronized void write(final byte[] bytes, final int offset, final int length) { |
| size += length; |
| super.write(bytes, offset, length); |
| } |
| |
| /** |
| * Returns the current size of the file. |
| * @return The size of the file in bytes. |
| */ |
| public long getFileSize() { |
| return size; |
| } |
| |
| /** |
| * Returns the time the file was created. |
| * @return The time the file was created. |
| */ |
| public long getFileTime() { |
| return initialTime; |
| } |
| |
| /** |
| * Determine if a rollover should occur. |
| * @param event The LogEvent. |
| */ |
| public synchronized void checkRollover(final LogEvent event) { |
| if (triggeringPolicy.isTriggeringEvent(event) && rollover(rolloverStrategy)) { |
| try { |
| size = 0; |
| initialTime = System.currentTimeMillis(); |
| createFileAfterRollover(); |
| } catch (final IOException ex) { |
| LOGGER.error("FileManager (" + getFileName() + ") " + ex); |
| } |
| } |
| } |
| |
| protected void createFileAfterRollover() throws IOException { |
| final OutputStream os = new FileOutputStream(getFileName(), isAppend()); |
| setOutputStream(os); |
| } |
| |
| /** |
| * Returns the pattern processor. |
| * @return The PatternProcessor. |
| */ |
| public PatternProcessor getPatternProcessor() { |
| return patternProcessor; |
| } |
| |
| /** |
| * Returns the triggering policy |
| * @return The TriggeringPolicy |
| */ |
| public TriggeringPolicy getTriggeringPolicy() { |
| return this.triggeringPolicy; |
| } |
| |
| /** |
| * Returns the rollover strategy |
| * @return The RolloverStrategy |
| */ |
| public RolloverStrategy getRolloverStrategy() { |
| return this.rolloverStrategy; |
| } |
| |
| private boolean rollover(final RolloverStrategy strategy) { |
| |
| try { |
| // Block until the asynchronous operation is completed. |
| semaphore.acquire(); |
| } catch (final InterruptedException ie) { |
| LOGGER.error("Thread interrupted while attempting to check rollover", ie); |
| return false; |
| } |
| |
| boolean success = false; |
| Thread thread = null; |
| |
| try { |
| final RolloverDescription descriptor = strategy.rollover(this); |
| |
| if (descriptor != null) { |
| |
| close(); |
| |
| if (descriptor.getSynchronous() != null) { |
| |
| try { |
| success = descriptor.getSynchronous().execute(); |
| } catch (final Exception ex) { |
| LOGGER.error("Error in synchronous task", ex); |
| } |
| } |
| |
| if (success && descriptor.getAsynchronous() != null) { |
| thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this)); |
| thread.start(); |
| } |
| return true; |
| } |
| return false; |
| } finally { |
| if (thread == null) { |
| semaphore.release(); |
| } |
| } |
| |
| } |
| |
| /** |
| * Performs actions asynchronously. |
| */ |
| private static class AsyncAction extends AbstractAction { |
| |
| private final Action action; |
| private final RollingFileManager manager; |
| |
| /** |
| * Constructor. |
| * @param act The action to perform. |
| * @param manager The manager. |
| */ |
| public AsyncAction(final Action act, final RollingFileManager manager) { |
| this.action = act; |
| this.manager = manager; |
| } |
| |
| /** |
| * Perform an action. |
| * |
| * @return true if action was successful. A return value of false will cause |
| * the rollover to be aborted if possible. |
| * @throws java.io.IOException if IO error, a thrown exception will cause the rollover |
| * to be aborted if possible. |
| */ |
| @Override |
| public boolean execute() throws IOException { |
| try { |
| return action.execute(); |
| } finally { |
| manager.semaphore.release(); |
| } |
| } |
| |
| /** |
| * Cancels the action if not already initialized or waits till completion. |
| */ |
| @Override |
| public void close() { |
| action.close(); |
| } |
| |
| /** |
| * Determines if action has been completed. |
| * |
| * @return true if action is complete. |
| */ |
| @Override |
| public boolean isComplete() { |
| return action.isComplete(); |
| } |
| } |
| |
| /** |
| * Factory data. |
| */ |
| private static class FactoryData { |
| private final String pattern; |
| private final boolean append; |
| private final boolean bufferedIO; |
| private final TriggeringPolicy policy; |
| private final RolloverStrategy strategy; |
| private final String advertiseURI; |
| private final Layout<? extends Serializable> layout; |
| |
| /** |
| * Create the data for the factory. |
| * @param pattern The pattern. |
| * @param append The append flag. |
| * @param bufferedIO The bufferedIO flag. |
| * @param advertiseURI |
| * @param layout The Layout. |
| */ |
| public FactoryData(final String pattern, final boolean append, final boolean bufferedIO, |
| final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI, |
| final Layout<? extends Serializable> layout) { |
| this.pattern = pattern; |
| this.append = append; |
| this.bufferedIO = bufferedIO; |
| this.policy = policy; |
| this.strategy = strategy; |
| this.advertiseURI = advertiseURI; |
| this.layout = layout; |
| } |
| } |
| |
| /** |
| * Factory to create a RollingFileManager. |
| */ |
| private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> { |
| |
| /** |
| * Create the RollingFileManager. |
| * @param name The name of the entity to manage. |
| * @param data The data required to create the entity. |
| * @return a RollingFileManager. |
| */ |
| @Override |
| public RollingFileManager createManager(final String name, final FactoryData data) { |
| final File file = new File(name); |
| final File parent = file.getParentFile(); |
| if (null != parent && !parent.exists()) { |
| parent.mkdirs(); |
| } |
| try { |
| file.createNewFile(); |
| } catch (final IOException ioe) { |
| LOGGER.error("Unable to create file " + name, ioe); |
| return null; |
| } |
| final long size = data.append ? file.length() : 0; |
| final long time = file.lastModified(); |
| |
| OutputStream os; |
| try { |
| os = new FileOutputStream(name, data.append); |
| if (data.bufferedIO) { |
| os = new BufferedOutputStream(os); |
| } |
| return new RollingFileManager(name, data.pattern, os, data.append, size, time, data.policy, |
| data.strategy, data.advertiseURI, data.layout); |
| } catch (final FileNotFoundException ex) { |
| LOGGER.error("FileManager (" + name + ") " + ex); |
| } |
| return null; |
| } |
| } |
| |
| } |