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