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