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