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