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.Core;
27  import org.apache.logging.log4j.core.Filter;
28  import org.apache.logging.log4j.core.Layout;
29  import org.apache.logging.log4j.core.LogEvent;
30  import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
31  import org.apache.logging.log4j.core.appender.rolling.DirectFileRolloverStrategy;
32  import org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy;
33  import org.apache.logging.log4j.core.appender.rolling.RollingRandomAccessFileManager;
34  import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
35  import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
36  import org.apache.logging.log4j.core.config.Configuration;
37  import org.apache.logging.log4j.core.config.Property;
38  import org.apache.logging.log4j.core.config.plugins.Plugin;
39  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
40  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
41  import org.apache.logging.log4j.core.config.plugins.PluginElement;
42  import org.apache.logging.log4j.core.net.Advertiser;
43  import org.apache.logging.log4j.core.util.Booleans;
44  import org.apache.logging.log4j.core.util.Integers;
45  
46  /**
47   * An appender that writes to random access files and can roll over at
48   * intervals.
49   */
50  @Plugin(name = "RollingRandomAccessFile", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
51  public final class RollingRandomAccessFileAppender extends AbstractOutputStreamAppender<RollingRandomAccessFileManager> {
52  
53      public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
54              implements org.apache.logging.log4j.core.util.Builder<RollingRandomAccessFileAppender> {
55  
56          public Builder() {
57              super();
58              withBufferSize(RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
59              setIgnoreExceptions(true);
60              withImmediateFlush(true);
61          }
62  
63          @PluginBuilderAttribute("fileName")
64          private String fileName;
65  
66          @PluginBuilderAttribute("filePattern")
67          private String filePattern;
68  
69          @PluginBuilderAttribute("append")
70          private boolean append = true;
71  
72          @PluginElement("Policy")
73          private TriggeringPolicy policy;
74  
75          @PluginElement("Strategy")
76          private RolloverStrategy strategy;
77  
78          @PluginBuilderAttribute("advertise")
79          private boolean advertise;
80  
81          @PluginBuilderAttribute("advertiseURI")
82          private String advertiseURI;
83  
84          @PluginBuilderAttribute
85          private String filePermissions;
86  
87          @PluginBuilderAttribute
88          private String fileOwner;
89  
90          @PluginBuilderAttribute
91          private String fileGroup;
92  
93          @Override
94          public RollingRandomAccessFileAppender build() {
95              final String name = getName();
96              if (name == null) {
97                  LOGGER.error("No name provided for FileAppender");
98                  return null;
99              }
100 
101             if (strategy == null) {
102                 if (fileName != null) {
103                     strategy = DefaultRolloverStrategy.newBuilder()
104                             .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
105                             .withConfig(getConfiguration())
106                             .build();
107                 } else {
108                     strategy = DirectWriteRolloverStrategy.newBuilder()
109                             .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
110                             .withConfig(getConfiguration())
111                             .build();
112                 }
113             } else if (fileName == null && !(strategy instanceof DirectFileRolloverStrategy)) {
114                 LOGGER.error("RollingFileAppender '{}': When no file name is provided a DirectFileRolloverStrategy must be configured");
115                 return null;
116             }
117 
118             if (filePattern == null) {
119                 LOGGER.error("No filename pattern provided for FileAppender with name " + name);
120                 return null;
121             }
122 
123             if (policy == null) {
124                 LOGGER.error("A TriggeringPolicy must be provided");
125                 return null;
126             }
127 
128             final Layout<? extends Serializable> layout = getOrCreateLayout();
129 
130             final boolean immediateFlush = isImmediateFlush();
131             final int bufferSize = getBufferSize();
132             final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager
133                     .getRollingRandomAccessFileManager(fileName, filePattern, append, immediateFlush, bufferSize, policy,
134                             strategy, advertiseURI, layout,
135                             filePermissions, fileOwner, fileGroup, getConfiguration());
136             if (manager == null) {
137                 return null;
138             }
139 
140             manager.initialize();
141 
142             return new RollingRandomAccessFileAppender(name, layout, getFilter(), manager, fileName, filePattern,
143                     isIgnoreExceptions(), immediateFlush, bufferSize,
144                     advertise ? getConfiguration().getAdvertiser() : null, getPropertyArray());
145         }
146 
147         public B withFileName(final String fileName) {
148             this.fileName = fileName;
149             return asBuilder();
150         }
151 
152         public B withFilePattern(final String filePattern) {
153             this.filePattern = filePattern;
154             return asBuilder();
155         }
156 
157         public B withAppend(final boolean append) {
158             this.append = append;
159             return asBuilder();
160         }
161 
162         public B withPolicy(final TriggeringPolicy policy) {
163             this.policy = policy;
164             return asBuilder();
165         }
166 
167         public B withStrategy(final RolloverStrategy strategy) {
168             this.strategy = strategy;
169             return asBuilder();
170         }
171 
172         public B withAdvertise(final boolean advertise) {
173             this.advertise = advertise;
174             return asBuilder();
175         }
176 
177         public B withAdvertiseURI(final String advertiseURI) {
178             this.advertiseURI = advertiseURI;
179             return asBuilder();
180         }
181 
182         public B withFilePermissions(final String filePermissions) {
183             this.filePermissions = filePermissions;
184             return asBuilder();
185         }
186 
187         public B withFileOwner(final String fileOwner) {
188             this.fileOwner = fileOwner;
189             return asBuilder();
190         }
191 
192         public B withFileGroup(final String fileGroup) {
193             this.fileGroup = fileGroup;
194             return asBuilder();
195         }
196 
197     }
198 
199     private final String fileName;
200     private final String filePattern;
201     private final Object advertisement;
202     private final Advertiser advertiser;
203 
204     private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout,
205             final Filter filter, final RollingRandomAccessFileManager manager, final String fileName,
206             final String filePattern, final boolean ignoreExceptions, final boolean immediateFlush,
207             final int bufferSize, final Advertiser advertiser, final Property[] properties) {
208         super(name, layout, filter, ignoreExceptions, immediateFlush, properties, manager);
209         if (advertiser != null) {
210             final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
211             configuration.put("contentType", layout.getContentType());
212             configuration.put("name", name);
213             advertisement = advertiser.advertise(configuration);
214         } else {
215             advertisement = null;
216         }
217         this.fileName = fileName;
218         this.filePattern = filePattern;
219         this.advertiser = advertiser;
220     }
221 
222     @Override
223     public boolean stop(final long timeout, final TimeUnit timeUnit) {
224         setStopping();
225         super.stop(timeout, timeUnit, false);
226         if (advertiser != null) {
227             advertiser.unadvertise(advertisement);
228         }
229         setStopped();
230         return true;
231     }
232 
233     /**
234      * Write the log entry rolling over the file when required.
235      *
236      * @param event The LogEvent.
237      */
238     @Override
239     public void append(final LogEvent event) {
240         final RollingRandomAccessFileManager manager = getManager();
241         manager.checkRollover(event);
242 
243         // Leverage the nice batching behaviour of async Loggers/Appenders:
244         // we can signal the file manager that it needs to flush the buffer
245         // to disk at the end of a batch.
246         // From a user's point of view, this means that all log events are
247         // _always_ available in the log file, without incurring the overhead
248         // of immediateFlush=true.
249         manager.setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted
250 
251         // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
252         super.append(event);
253     }
254 
255     /**
256      * Returns the File name for the Appender.
257      *
258      * @return The file name.
259      */
260     public String getFileName() {
261         return fileName;
262     }
263 
264     /**
265      * Returns the file pattern used when rolling over.
266      *
267      * @return The file pattern.
268      */
269     public String getFilePattern() {
270         return filePattern;
271     }
272 
273     /**
274      * Returns the size of the file manager's buffer.
275      * @return the buffer size
276      */
277     public int getBufferSize() {
278         return getManager().getBufferSize();
279     }
280 
281     /**
282      * Create a RollingRandomAccessFileAppender.
283      *
284      * @param fileName The name of the file that is actively written to.
285      *            (required).
286      * @param filePattern The pattern of the file name to use on rollover.
287      *            (required).
288      * @param append If true, events are appended to the file. If false, the
289      *            file is overwritten when opened. Defaults to "true"
290      * @param name The name of the Appender (required).
291      * @param immediateFlush When true, events are immediately flushed. Defaults
292      *            to "true".
293      * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
294      * @param policy The triggering policy. (required).
295      * @param strategy The rollover strategy. Defaults to
296      *            DefaultRolloverStrategy.
297      * @param layout The layout to use (defaults to the default PatternLayout).
298      * @param filter The Filter or null.
299      * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
300      *               they are propagated to the caller.
301      * @param advertise "true" if the appender configuration should be
302      *            advertised, "false" otherwise.
303      * @param advertiseURI The advertised URI which can be used to retrieve the
304      *            file contents.
305      * @param configuration The Configuration.
306      * @return A RollingRandomAccessFileAppender.
307      * @deprecated Use {@link #newBuilder()}.
308      */
309     @Deprecated
310     public static <B extends Builder<B>> RollingRandomAccessFileAppender createAppender(
311             final String fileName,
312             final String filePattern,
313             final String append,
314             final String name,
315             final String immediateFlush,
316             final String bufferSizeStr,
317             final TriggeringPolicy policy,
318             final RolloverStrategy strategy,
319             final Layout<? extends Serializable> layout,
320             final Filter filter,
321             final String ignoreExceptions,
322             final String advertise,
323             final String advertiseURI,
324             final Configuration configuration) {
325 
326         final boolean isAppend = Booleans.parseBoolean(append, true);
327         final boolean isIgnoreExceptions = Booleans.parseBoolean(ignoreExceptions, true);
328         final boolean isImmediateFlush = Booleans.parseBoolean(immediateFlush, true);
329         final boolean isAdvertise = Boolean.parseBoolean(advertise);
330         final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
331 
332         return RollingRandomAccessFileAppender.<B>newBuilder()
333            .withAdvertise(isAdvertise)
334            .withAdvertiseURI(advertiseURI)
335            .withAppend(isAppend)
336            .withBufferSize(bufferSize)
337            .setConfiguration(configuration)
338            .withFileName(fileName)
339            .withFilePattern(filePattern).setFilter(filter).setIgnoreExceptions(isIgnoreExceptions)
340            .withImmediateFlush(isImmediateFlush).setLayout(layout).setName(name)
341            .withPolicy(policy)
342            .withStrategy(strategy)
343            .build();
344     }
345 
346     @PluginBuilderFactory
347     public static <B extends Builder<B>> B newBuilder() {
348         return new Builder<B>().asBuilder();
349     }
350 
351 }