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