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;
18  
19  import java.io.Serializable;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.concurrent.TimeUnit;
23  import java.util.zip.Deflater;
24  
25  import org.apache.logging.log4j.core.Appender;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.Layout;
28  import org.apache.logging.log4j.core.LogEvent;
29  import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
30  import org.apache.logging.log4j.core.appender.rolling.RollingRandomAccessFileManager;
31  import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
32  import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
33  import org.apache.logging.log4j.core.config.Configuration;
34  import org.apache.logging.log4j.core.config.plugins.Plugin;
35  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
36  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
37  import org.apache.logging.log4j.core.config.plugins.PluginElement;
38  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
39  import org.apache.logging.log4j.core.layout.PatternLayout;
40  import org.apache.logging.log4j.core.net.Advertiser;
41  import org.apache.logging.log4j.core.util.Booleans;
42  import org.apache.logging.log4j.core.util.Integers;
43  
44  /**
45   * An appender that writes to random access files and can roll over at
46   * intervals.
47   */
48  @Plugin(name = "RollingRandomAccessFile", category = "Core", elementType = Appender.ELEMENT_TYPE, printObject = true)
49  public final class RollingRandomAccessFileAppender extends AbstractOutputStreamAppender<RollingRandomAccessFileManager> {
50  
51      private final String fileName;
52      private final String filePattern;
53      private final Object advertisement;
54      private final Advertiser advertiser;
55  
56      private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout,
57              final Filter filter, final RollingRandomAccessFileManager manager, final String fileName,
58              final String filePattern, final boolean ignoreExceptions,
59              final boolean immediateFlush, final int bufferSize, final Advertiser advertiser) {
60          super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
61          if (advertiser != null) {
62              final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
63              configuration.put("contentType", layout.getContentType());
64              configuration.put("name", name);
65              advertisement = advertiser.advertise(configuration);
66          } else {
67              advertisement = null;
68          }
69          this.fileName = fileName;
70          this.filePattern = filePattern;
71          this.advertiser = advertiser;
72      }
73  
74      @Override
75      public boolean stop(final long timeout, final TimeUnit timeUnit) {
76          setStopping();
77          super.stop(timeout, timeUnit, false);
78          if (advertiser != null) {
79              advertiser.unadvertise(advertisement);
80          }
81          setStopped();
82          return true;
83      }
84  
85      /**
86       * Write the log entry rolling over the file when required.
87       *
88       * @param event The LogEvent.
89       */
90      @Override
91      public void append(final LogEvent event) {
92          final RollingRandomAccessFileManager manager = getManager();
93          manager.checkRollover(event);
94  
95          // Leverage the nice batching behaviour of async Loggers/Appenders:
96          // we can signal the file manager that it needs to flush the buffer
97          // to disk at the end of a batch.
98          // From a user's point of view, this means that all log events are
99          // _always_ available in the log file, without incurring the overhead
100         // of immediateFlush=true.
101         manager.setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted
102 
103         // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
104         super.append(event);
105     }
106 
107     /**
108      * Returns the File name for the Appender.
109      *
110      * @return The file name.
111      */
112     public String getFileName() {
113         return fileName;
114     }
115 
116     /**
117      * Returns the file pattern used when rolling over.
118      *
119      * @return The file pattern.
120      */
121     public String getFilePattern() {
122         return filePattern;
123     }
124 
125     /**
126      * Returns the size of the file manager's buffer.
127      * @return the buffer size
128      */
129     public int getBufferSize() {
130         return getManager().getBufferSize();
131     }
132 
133     /**
134      * Create a RollingRandomAccessFileAppender.
135      *
136      * @param fileName The name of the file that is actively written to.
137      *            (required).
138      * @param filePattern The pattern of the file name to use on rollover.
139      *            (required).
140      * @param append If true, events are appended to the file. If false, the
141      *            file is overwritten when opened. Defaults to "true"
142      * @param name The name of the Appender (required).
143      * @param immediateFlush When true, events are immediately flushed. Defaults
144      *            to "true".
145      * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
146      * @param policy The triggering policy. (required).
147      * @param strategy The rollover strategy. Defaults to
148      *            DefaultRolloverStrategy.
149      * @param layout The layout to use (defaults to the default PatternLayout).
150      * @param filter The Filter or null.
151      * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
152      *               they are propagated to the caller.
153      * @param advertise "true" if the appender configuration should be
154      *            advertised, "false" otherwise.
155      * @param advertiseURI The advertised URI which can be used to retrieve the
156      *            file contents.
157      * @param config The Configuration.
158      * @return A RollingRandomAccessFileAppender.
159      */
160     @PluginFactory
161     public static RollingRandomAccessFileAppender createAppender(
162             @PluginAttribute("fileName") final String fileName,
163             @PluginAttribute("filePattern") final String filePattern,
164             @PluginAttribute("append") final String append,
165             @PluginAttribute("name") final String name,
166             @PluginAttribute("immediateFlush") final String immediateFlush,
167             @PluginAttribute("bufferSize") final String bufferSizeStr,
168             @PluginElement("Policy") final TriggeringPolicy policy,
169             @PluginElement("Strategy") RolloverStrategy strategy,
170             @PluginElement("Layout") Layout<? extends Serializable> layout,
171             @PluginElement("Filter") final Filter filter,
172             @PluginAttribute("ignoreExceptions") final String ignore,
173             @PluginAttribute("advertise") final String advertise,
174             @PluginAttribute("advertiseURI") final String advertiseURI,
175             @PluginConfiguration final Configuration config) {
176 
177         final boolean isAppend = Booleans.parseBoolean(append, true);
178         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
179         final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
180         final boolean isAdvertise = Boolean.parseBoolean(advertise);
181         final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
182 
183         if (name == null) {
184             LOGGER.error("No name provided for FileAppender");
185             return null;
186         }
187 
188         if (fileName == null) {
189             LOGGER.error("No filename was provided for FileAppender with name " + name);
190             return null;
191         }
192 
193         if (filePattern == null) {
194             LOGGER.error("No filename pattern provided for FileAppender with name " + name);
195             return null;
196         }
197 
198         if (policy == null) {
199             LOGGER.error("A TriggeringPolicy must be provided");
200             return null;
201         }
202 
203         if (strategy == null) {
204             strategy = DefaultRolloverStrategy.createStrategy(null, null, null,
205                     String.valueOf(Deflater.DEFAULT_COMPRESSION), null, true, config);
206         }
207 
208         if (layout == null) {
209             layout = PatternLayout.createDefaultLayout();
210         }
211 
212         final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager.getRollingRandomAccessFileManager(
213                 fileName, filePattern, isAppend, isFlush, bufferSize, policy, strategy, advertiseURI, layout, config);
214         if (manager == null) {
215             return null;
216         }
217 
218         manager.initialize();
219 
220         return new RollingRandomAccessFileAppender(name, layout, filter, manager,
221                 fileName, filePattern, ignoreExceptions, isFlush, bufferSize,
222                 isAdvertise ? config.getAdvertiser() : null);
223     }
224 }