View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.appender.rolling;
    18
    19  import java.io.BufferedOutputStream;
    20  import java.io.File;
    21  import java.io.FileNotFoundException;
    22  import java.io.FileOutputStream;
    23  import java.io.IOException;
    24  import java.io.OutputStream;
    25  import java.io.Serializable;
    26  import java.util.concurrent.Semaphore;
    27
    28  import org.apache.logging.log4j.core.Layout;
    29  import org.apache.logging.log4j.core.LogEvent;
    30  import org.apache.logging.log4j.core.appender.FileManager;
    31  import org.apache.logging.log4j.core.appender.ManagerFactory;
    32  import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
    33  import org.apache.logging.log4j.core.appender.rolling.action.Action;
    34
    35  /**
    36   * The Rolling File Manager.
    37   */
    38  public class RollingFileManager extends FileManager {
    39
    40      private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
    41
    42      private long size;
    43      private long initialTime;
    44      private final PatternProcessor patternProcessor;
    45      private final Semaphore semaphore = new Semaphore(1);
    46      private final TriggeringPolicy triggeringPolicy;
    47      private final RolloverStrategy rolloverStrategy;
    48
    49      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
    50              final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
    51              final RolloverStrategy rolloverStrategy, final String advertiseURI,
    52              final Layout<? extends Serializable> layout, final int bufferSize) {
    53          super(fileName, os, append, false, advertiseURI, layout, bufferSize);
    54          this.size = size;
    55          this.initialTime = time;
    56          this.triggeringPolicy = triggeringPolicy;
    57          this.rolloverStrategy = rolloverStrategy;
    58          this.patternProcessor = new PatternProcessor(pattern);
    59          triggeringPolicy.initialize(this);
    60      }
    61
    62      /**
    63       * Returns a RollingFileManager.
    64       * @param fileName The file name.
    65       * @param pattern The pattern for rolling file.
    66       * @param append true if the file should be appended to.
    67       * @param bufferedIO true if data should be buffered.
    68       * @param policy The TriggeringPolicy.
    69       * @param strategy The RolloverStrategy.
    70       * @param advertiseURI the URI to use when advertising the file
    71       * @param layout The Layout.
    72       * @param bufferSize buffer size to use if bufferedIO is true
    73       * @return A RollingFileManager.
    74       */
    75      public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
    76              final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
    77              final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize) {
    78
    79          return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
    80              bufferedIO, policy, strategy, advertiseURI, layout, bufferSize), factory);
    81      }
    82
    83      @Override
    84      protected synchronized void write(final byte[] bytes, final int offset, final int length) {
    85          size += length;
    86          super.write(bytes, offset, length);
    87      }
    88
    89      /**
    90       * Returns the current size of the file.
    91       * @return The size of the file in bytes.
    92       */
    93      public long getFileSize() {
    94          return size;
    95      }
    96
    97      /**
    98       * Returns the time the file was created.
    99       * @return The time the file was created.
    100      */
    101     public long getFileTime() {
    102         return initialTime;
    103     }
    104
    105     /**
    106      * Determine if a rollover should occur.
    107      * @param event The LogEvent.
    108      */
    109     public synchronized void checkRollover(final LogEvent event) {
    110         if (triggeringPolicy.isTriggeringEvent(event) && rollover(rolloverStrategy)) {
    111             try {
    112                 size = 0;
    113                 initialTime = System.currentTimeMillis();
    114                 createFileAfterRollover();
    115             } catch (final IOException ex) {
    116                 LOGGER.error("FileManager (" + getFileName() + ") " + ex);
    117             }
    118         }
    119     }
    120
    121     protected void createFileAfterRollover() throws IOException {
    122         final OutputStream os = new FileOutputStream(getFileName(), isAppend());
    123         if (getBufferSize() > 0) { // negative buffer size means no buffering
    124             setOutputStream(new BufferedOutputStream(os, getBufferSize()));
    125         } else {
    126             setOutputStream(os);
    127         }
    128     }
    129
    130     /**
    131      * Returns the pattern processor.
    132      * @return The PatternProcessor.
    133      */
    134     public PatternProcessor getPatternProcessor() {
    135         return patternProcessor;
    136     }
    137
    138     /**
    139      * Returns the triggering policy
    140      * @return The TriggeringPolicy
    141      */
    142     public TriggeringPolicy getTriggeringPolicy() {
    143         return this.triggeringPolicy;
    144     }
    145
    146     /**
    147      * Returns the rollover strategy
    148      * @return The RolloverStrategy
    149      */
    150     public RolloverStrategy getRolloverStrategy() {
    151         return this.rolloverStrategy;
    152     }
    153
    154     private boolean rollover(final RolloverStrategy strategy) {
    155
    156         try {
    157             // Block until the asynchronous operation is completed.
    158             semaphore.acquire();
    159         } catch (final InterruptedException ie) {
    160             LOGGER.error("Thread interrupted while attempting to check rollover", ie);
    161             return false;
    162         }
    163
    164         boolean success = false;
    165         Thread thread = null;
    166
    167         try {
    168             final RolloverDescription descriptor = strategy.rollover(this);
    169             if (descriptor != null) {
    170                 writeFooter();
    171                 close();
    172                 if (descriptor.getSynchronous() != null) {
    173                     LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
    174                     try {
    175                         success = descriptor.getSynchronous().execute();
    176                     } catch (final Exception ex) {
    177                         LOGGER.error("Error in synchronous task", ex);
    178                     }
    179                 }
    180
    181                 if (success && descriptor.getAsynchronous() != null) {
    182                     LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
    183                     thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this));
    184                     thread.start();
    185                 }
    186                 return true;
    187             }
    188             return false;
    189         } finally {
    190             if (thread == null || !thread.isAlive()) {
    191                 semaphore.release();
    192             }
    193         }
    194
    195     }
    196
    197     /**
    198      * Performs actions asynchronously.
    199      */
    200     private static class AsyncAction extends AbstractAction {
    201
    202         private final Action action;
    203         private final RollingFileManager manager;
    204
    205         /**
    206          * Constructor.
    207          * @param act The action to perform.
    208          * @param manager The manager.
    209          */
    210         public AsyncAction(final Action act, final RollingFileManager manager) {
    211             this.action = act;
    212             this.manager = manager;
    213         }
    214
    215         /**
    216          * Perform an action.
    217          *
    218          * @return true if action was successful.  A return value of false will cause
    219          *         the rollover to be aborted if possible.
    220          * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
    221          *                             to be aborted if possible.
    222          */
    223         @Override
    224         public boolean execute() throws IOException {
    225             try {
    226                 return action.execute();
    227             } finally {
    228                 manager.semaphore.release();
    229             }
    230         }
    231
    232         /**
    233          * Cancels the action if not already initialized or waits till completion.
    234          */
    235         @Override
    236         public void close() {
    237             action.close();
    238         }
    239
    240         /**
    241          * Determines if action has been completed.
    242          *
    243          * @return true if action is complete.
    244          */
    245         @Override
    246         public boolean isComplete() {
    247             return action.isComplete();
    248         }
    249     }
    250
    251     /**
    252      * Factory data.
    253      */
    254     private static class FactoryData {
    255         private final String pattern;
    256         private final boolean append;
    257         private final boolean bufferedIO;
    258         private final int bufferSize;
    259         private final TriggeringPolicy policy;
    260         private final RolloverStrategy strategy;
    261         private final String advertiseURI;
    262         private final Layout<? extends Serializable> layout;
    263
    264         /**
    265          * Create the data for the factory.
    266          * @param pattern The pattern.
    267          * @param append The append flag.
    268          * @param bufferedIO The bufferedIO flag.
    269          * @param advertiseURI
    270          * @param layout The Layout.
    271          * @param bufferSize the buffer size
    272          */
    273         public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
    274                 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
    275                 final Layout<? extends Serializable> layout, final int bufferSize) {
    276             this.pattern = pattern;
    277             this.append = append;
    278             this.bufferedIO = bufferedIO;
    279             this.bufferSize = bufferSize;
    280             this.policy = policy;
    281             this.strategy = strategy;
    282             this.advertiseURI = advertiseURI;
    283             this.layout = layout;
    284         }
    285     }
    286
    287     /**
    288      * Factory to create a RollingFileManager.
    289      */
    290     private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
    291
    292         /**
    293          * Create the RollingFileManager.
    294          * @param name The name of the entity to manage.
    295          * @param data The data required to create the entity.
    296          * @return a RollingFileManager.
    297          */
    298         @Override
    299         public RollingFileManager createManager(final String name, final FactoryData data) {
    300             final File file = new File(name);
    301             final File parent = file.getParentFile();
    302             if (null != parent && !parent.exists()) {
    303                 parent.mkdirs();
    304             }
    305             try {
    306                 file.createNewFile();
    307             } catch (final IOException ioe) {
    308                 LOGGER.error("Unable to create file " + name, ioe);
    309                 return null;
    310             }
    311             final long size = data.append ? file.length() : 0;
    312
    313             OutputStream os;
    314             try {
    315                 os = new FileOutputStream(name, data.append);
    316                 int bufferSize = data.bufferSize;
    317                 if (data.bufferedIO) {
    318                     os = new BufferedOutputStream(os, bufferSize);
    319                 } else {
    320                     bufferSize = -1; // negative buffer size signals bufferedIO was configured false
    321                 }
    322                 final long time = file.lastModified(); // LOG4J2-531 create file first so time has valid value
    323                 return new RollingFileManager(name, data.pattern, os, data.append, size, time, data.policy,
    324                     data.strategy, data.advertiseURI, data.layout, bufferSize);
    325             } catch (final FileNotFoundException ex) {
    326                 LOGGER.error("FileManager (" + name + ") " + ex);
    327             }
    328             return null;
    329         }
    330     }
    331
    332 }