1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
56
57
58
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
104
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
302
303
304
305 @Override
306 public void append(final LogEvent event) {
307 getManager().checkRollover(event);
308 super.append(event);
309 }
310
311
312
313
314
315 public String getFileName() {
316 return fileName;
317 }
318
319
320
321
322
323 public String getFilePattern() {
324 return filePattern;
325 }
326
327
328
329
330
331
332 public <T extends TriggeringPolicy> T getTriggeringPolicy() {
333 return getManager().getTriggeringPolicy();
334 }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358 @Deprecated
359 public static <B extends Builder<B>> RollingFileAppender createAppender(
360
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
377 final int bufferSize = Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE);
378
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
399 }
400
401 @PluginBuilderFactory
402 public static <B extends Builder<B>> B newBuilder() {
403 return new Builder<B>().asBuilder();
404 }
405 }