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.RollingFileManager;
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.config.plugins.validation.constraints.Required;
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 files and can roll over at intervals.
48   */
49  @Plugin(name = RollingFileAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
50  public final class RollingFileAppender extends AbstractOutputStreamAppender<RollingFileManager> {
51  
52      public static final String PLUGIN_NAME = "RollingFile";
53  
54      /**
55       * Builds FileAppender instances.
56       * 
57       * @param <B>
58       *            The type to build
59       * @since 2.7
60       */
61      public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
62              implements org.apache.logging.log4j.core.util.Builder<RollingFileAppender> {
63  
64          @PluginBuilderAttribute
65          private String fileName;
66  
67          @PluginBuilderAttribute
68          @Required
69          private String filePattern;
70  
71          @PluginBuilderAttribute
72          private boolean append = true;
73  
74          @PluginBuilderAttribute
75          private boolean locking;
76  
77          @PluginElement("Policy") 
78          @Required
79          private TriggeringPolicy policy;
80          
81          @PluginElement("Strategy") 
82          private RolloverStrategy strategy;
83  
84          @PluginBuilderAttribute
85          private boolean advertise;
86  
87          @PluginBuilderAttribute
88          private String advertiseUri;
89  
90          @PluginBuilderAttribute
91          private boolean createOnDemand;
92  
93          @PluginBuilderAttribute
94          private String filePermissions;
95  
96          @PluginBuilderAttribute
97          private String fileOwner;
98  
99          @PluginBuilderAttribute
100         private String fileGroup;
101 
102         @Override
103         public RollingFileAppender build() {
104             // Even though some variables may be annotated with @Required, we must still perform validation here for
105             // call sites that build builders programmatically.
106             final boolean isBufferedIo = isBufferedIo();
107             final int bufferSize = getBufferSize();
108             if (getName() == null) {
109                 LOGGER.error("RollingFileAppender '{}': No name provided.", getName());
110                 return null;
111             }
112 
113             if (!isBufferedIo && bufferSize > 0) {
114                 LOGGER.warn("RollingFileAppender '{}': The bufferSize is set to {} but bufferedIO is not true", getName(), bufferSize);
115             }
116 
117             if (filePattern == null) {
118                 LOGGER.error("RollingFileAppender '{}': No file name pattern provided.", getName());
119                 return null;
120             }
121 
122             if (policy == null) {
123                 LOGGER.error("RollingFileAppender '{}': No TriggeringPolicy provided.", getName());
124                 return null;
125             }
126 
127             if (strategy == null) {
128                 if (fileName != null) {
129                     strategy = DefaultRolloverStrategy.newBuilder()
130                                         .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
131                                         .withConfig(getConfiguration())
132                                         .build();
133                 } else {
134                     strategy = DirectWriteRolloverStrategy.newBuilder()
135                                         .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
136                                         .withConfig(getConfiguration())
137                                         .build();
138                 }
139             } else if (fileName == null && !(strategy instanceof DirectFileRolloverStrategy)) {
140                 LOGGER.error("RollingFileAppender '{}': When no file name is provided a DirectFilenameRolloverStrategy must be configured");
141                 return null;
142             }
143 
144             final Layout<? extends Serializable> layout = getOrCreateLayout();
145             final RollingFileManager manager = RollingFileManager.getFileManager(fileName, filePattern, append,
146                     isBufferedIo, policy, strategy, advertiseUri, layout, bufferSize, isImmediateFlush(),
147                     createOnDemand, filePermissions, fileOwner, fileGroup, getConfiguration());
148             if (manager == null) {
149                 return null;
150             }
151 
152             manager.initialize();
153 
154             return new RollingFileAppender(getName(), layout, getFilter(), manager, fileName, filePattern,
155                     isIgnoreExceptions(), isImmediateFlush(), advertise ? getConfiguration().getAdvertiser() : null);
156         }
157 
158         public String getAdvertiseUri() {
159             return advertiseUri;
160         }
161 
162         public String getFileName() {
163             return fileName;
164         }
165 
166         public boolean isAdvertise() {
167             return advertise;
168         }
169 
170         public boolean isAppend() {
171             return append;
172         }
173 
174         public boolean isCreateOnDemand() {
175             return createOnDemand;
176         }
177 
178         public boolean isLocking() {
179             return locking;
180         }
181 
182         public String getFilePermissions() {
183             return filePermissions;
184         }
185 
186         public String getFileOwner() {
187             return fileOwner;
188         }
189 
190         public String getFileGroup() {
191             return fileGroup;
192         }
193 
194         public B withAdvertise(final boolean advertise) {
195             this.advertise = advertise;
196             return asBuilder();
197         }
198 
199         public B withAdvertiseUri(final String advertiseUri) {
200             this.advertiseUri = advertiseUri;
201             return asBuilder();
202         }
203 
204         public B withAppend(final boolean append) {
205             this.append = append;
206             return asBuilder();
207         }
208 
209         public B withFileName(final String fileName) {
210             this.fileName = fileName;
211             return asBuilder();
212         }
213 
214         public B withCreateOnDemand(final boolean createOnDemand) {
215             this.createOnDemand = createOnDemand;
216             return asBuilder();
217         }
218 
219         public B withLocking(final boolean locking) {
220             this.locking = locking;
221             return asBuilder();
222         }
223 
224         public String getFilePattern() {
225             return filePattern;
226         }
227 
228         public TriggeringPolicy getPolicy() {
229             return policy;
230         }
231 
232         public RolloverStrategy getStrategy() {
233             return strategy;
234         }
235 
236         public B withFilePattern(final String filePattern) {
237             this.filePattern = filePattern;
238             return asBuilder();
239         }
240 
241         public B withPolicy(final TriggeringPolicy policy) {
242             this.policy = policy;
243             return asBuilder();
244         }
245 
246         public B withStrategy(final RolloverStrategy strategy) {
247             this.strategy = strategy;
248             return asBuilder();
249         }
250 
251         public B withFilePermissions(final String filePermissions) {
252             this.filePermissions = filePermissions;
253             return asBuilder();
254         }
255 
256         public B withFileOwner(final String fileOwner) {
257             this.fileOwner = fileOwner;
258             return asBuilder();
259         }
260 
261         public B withFileGroup(final String fileGroup) {
262             this.fileGroup = fileGroup;
263             return asBuilder();
264         }
265 
266     }
267     
268     private static final int DEFAULT_BUFFER_SIZE = 8192;
269 
270     private final String fileName;
271     private final String filePattern;
272     private Object advertisement;
273     private final Advertiser advertiser;
274 
275     private RollingFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
276             final RollingFileManager manager, final String fileName, final String filePattern,
277             final boolean ignoreExceptions, final boolean immediateFlush, final Advertiser advertiser) {
278         super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
279         if (advertiser != null) {
280             final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
281             configuration.put("contentType", layout.getContentType());
282             configuration.put("name", name);
283             advertisement = advertiser.advertise(configuration);
284         }
285         this.fileName = fileName;
286         this.filePattern = filePattern;
287         this.advertiser = advertiser;
288     }
289 
290     @Override
291     public boolean stop(final long timeout, final TimeUnit timeUnit) {
292         setStopping();
293         final boolean stopped = super.stop(timeout, timeUnit, false);
294         if (advertiser != null) {
295             advertiser.unadvertise(advertisement);
296         }
297         setStopped();
298         return stopped;
299     }
300 
301     /**
302      * Writes the log entry rolling over the file when required.
303 
304      * @param event The LogEvent.
305      */
306     @Override
307     public void append(final LogEvent event) {
308         getManager().checkRollover(event);
309         super.append(event);
310     }
311 
312     /**
313      * Returns the File name for the Appender.
314      * @return The file name.
315      */
316     public String getFileName() {
317         return fileName;
318     }
319 
320     /**
321      * Returns the file pattern used when rolling over.
322      * @return The file pattern.
323      */
324     public String getFilePattern() {
325         return filePattern;
326     }
327 
328     /**
329      * Returns the triggering policy.
330      * @param <T> TriggeringPolicy type
331      * @return The TriggeringPolicy
332      */
333     public <T extends TriggeringPolicy> T getTriggeringPolicy() {
334         return getManager().getTriggeringPolicy();
335     }
336 
337     /**
338      * Creates a RollingFileAppender.
339      * @param fileName The name of the file that is actively written to. (required).
340      * @param filePattern The pattern of the file name to use on rollover. (required).
341      * @param append If true, events are appended to the file. If false, the file
342      * is overwritten when opened. Defaults to "true"
343      * @param name The name of the Appender (required).
344      * @param bufferedIO When true, I/O will be buffered. Defaults to "true".
345      * @param bufferSizeStr buffer size for buffered IO (default is 8192).
346      * @param immediateFlush When true, events are immediately flushed. Defaults to "true".
347      * @param policy The triggering policy. (required).
348      * @param strategy The rollover strategy. Defaults to DefaultRolloverStrategy.
349      * @param layout The layout to use (defaults to the default PatternLayout).
350      * @param filter The Filter or null.
351      * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
352      *               they are propagated to the caller.
353      * @param advertise "true" if the appender configuration should be advertised, "false" otherwise.
354      * @param advertiseUri The advertised URI which can be used to retrieve the file contents.
355      * @param config The Configuration.
356      * @return A RollingFileAppender.
357      * @deprecated Use {@link #newBuilder()}.
358      */
359     @Deprecated
360     public static <B extends Builder<B>> RollingFileAppender createAppender(
361             // @formatter:off
362             final String fileName,
363             final String filePattern,
364             final String append,
365             final String name,
366             final String bufferedIO,
367             final String bufferSizeStr,
368             final String immediateFlush,
369             final TriggeringPolicy policy,
370             final RolloverStrategy strategy,
371             final Layout<? extends Serializable> layout,
372             final Filter filter,
373             final String ignore,
374             final String advertise,
375             final String advertiseUri,
376             final Configuration config) {
377             // @formatter:on
378         final int bufferSize = Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE);
379         // @formatter:off
380         return RollingFileAppender.<B>newBuilder()
381                 .withAdvertise(Boolean.parseBoolean(advertise))
382                 .withAdvertiseUri(advertiseUri)
383                 .withAppend(Booleans.parseBoolean(append, true))
384                 .withBufferedIo(Booleans.parseBoolean(bufferedIO, true))
385                 .withBufferSize(bufferSize)
386                 .setConfiguration(config)
387                 .withFileName(fileName)
388                 .withFilePattern(filePattern)
389                 .withFilter(filter)
390                 .withIgnoreExceptions(Booleans.parseBoolean(ignore, true))
391                 .withImmediateFlush(Booleans.parseBoolean(immediateFlush, true))
392                 .withLayout(layout)
393                 .withCreateOnDemand(false)
394                 .withLocking(false)
395                 .withName(name)
396                 .withPolicy(policy)
397                 .withStrategy(strategy)
398                 .build();
399         // @formatter:on
400     }
401 
402     /**
403      * Creates a new Builder.
404      * 
405      * @return a new Builder.
406      * @since 2.7
407      */
408     @PluginBuilderFactory
409     public static <B extends Builder<B>> B newBuilder() {
410         return new Builder<B>().asBuilder();
411     }
412 }