1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.rolling;
18
19 import java.io.BufferedOutputStream;
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.io.Serializable;
26 import java.util.concurrent.Semaphore;
27
28 import org.apache.logging.log4j.core.Layout;
29 import org.apache.logging.log4j.core.LogEvent;
30 import org.apache.logging.log4j.core.appender.FileManager;
31 import org.apache.logging.log4j.core.appender.ManagerFactory;
32 import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
33 import org.apache.logging.log4j.core.appender.rolling.action.Action;
34
35
36
37
38 public class RollingFileManager extends FileManager {
39
40 private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
41
42 private long size;
43 private long initialTime;
44 private final PatternProcessor patternProcessor;
45 private final Semaphore semaphore = new Semaphore(1);
46 private final TriggeringPolicy triggeringPolicy;
47 private final RolloverStrategy rolloverStrategy;
48
49 protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
50 final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
51 final RolloverStrategy rolloverStrategy, final String advertiseURI,
52 final Layout<? extends Serializable> layout, final int bufferSize) {
53 super(fileName, os, append, false, advertiseURI, layout, bufferSize);
54 this.size = size;
55 this.initialTime = time;
56 this.triggeringPolicy = triggeringPolicy;
57 this.rolloverStrategy = rolloverStrategy;
58 this.patternProcessor = new PatternProcessor(pattern);
59 triggeringPolicy.initialize(this);
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
76 final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
77 final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize) {
78
79 return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
80 bufferedIO, policy, strategy, advertiseURI, layout, bufferSize), factory);
81 }
82
83 @Override
84 protected synchronized void write(final byte[] bytes, final int offset, final int length) {
85 size += length;
86 super.write(bytes, offset, length);
87 }
88
89
90
91
92
93 public long getFileSize() {
94 return size;
95 }
96
97
98
99
100
101 public long getFileTime() {
102 return initialTime;
103 }
104
105
106
107
108
109 public synchronized void checkRollover(final LogEvent event) {
110 if (triggeringPolicy.isTriggeringEvent(event) && rollover(rolloverStrategy)) {
111 try {
112 size = 0;
113 initialTime = System.currentTimeMillis();
114 createFileAfterRollover();
115 } catch (final IOException ex) {
116 LOGGER.error("FileManager (" + getFileName() + ") " + ex);
117 }
118 }
119 }
120
121 protected void createFileAfterRollover() throws IOException {
122 final OutputStream os = new FileOutputStream(getFileName(), isAppend());
123 if (getBufferSize() > 0) {
124 setOutputStream(new BufferedOutputStream(os, getBufferSize()));
125 } else {
126 setOutputStream(os);
127 }
128 }
129
130
131
132
133
134 public PatternProcessor getPatternProcessor() {
135 return patternProcessor;
136 }
137
138
139
140
141
142 public TriggeringPolicy getTriggeringPolicy() {
143 return this.triggeringPolicy;
144 }
145
146
147
148
149
150 public RolloverStrategy getRolloverStrategy() {
151 return this.rolloverStrategy;
152 }
153
154 private boolean rollover(final RolloverStrategy strategy) {
155
156 try {
157
158 semaphore.acquire();
159 } catch (final InterruptedException ie) {
160 LOGGER.error("Thread interrupted while attempting to check rollover", ie);
161 return false;
162 }
163
164 boolean success = false;
165 Thread thread = null;
166
167 try {
168 final RolloverDescription descriptor = strategy.rollover(this);
169 if (descriptor != null) {
170 writeFooter();
171 close();
172 if (descriptor.getSynchronous() != null) {
173 LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
174 try {
175 success = descriptor.getSynchronous().execute();
176 } catch (final Exception ex) {
177 LOGGER.error("Error in synchronous task", ex);
178 }
179 }
180
181 if (success && descriptor.getAsynchronous() != null) {
182 LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
183 thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this));
184 thread.start();
185 }
186 return true;
187 }
188 return false;
189 } finally {
190 if (thread == null || !thread.isAlive()) {
191 semaphore.release();
192 }
193 }
194
195 }
196
197
198
199
200 private static class AsyncAction extends AbstractAction {
201
202 private final Action action;
203 private final RollingFileManager manager;
204
205
206
207
208
209
210 public AsyncAction(final Action act, final RollingFileManager manager) {
211 this.action = act;
212 this.manager = manager;
213 }
214
215
216
217
218
219
220
221
222
223 @Override
224 public boolean execute() throws IOException {
225 try {
226 return action.execute();
227 } finally {
228 manager.semaphore.release();
229 }
230 }
231
232
233
234
235 @Override
236 public void close() {
237 action.close();
238 }
239
240
241
242
243
244
245 @Override
246 public boolean isComplete() {
247 return action.isComplete();
248 }
249 }
250
251
252
253
254 private static class FactoryData {
255 private final String pattern;
256 private final boolean append;
257 private final boolean bufferedIO;
258 private final int bufferSize;
259 private final TriggeringPolicy policy;
260 private final RolloverStrategy strategy;
261 private final String advertiseURI;
262 private final Layout<? extends Serializable> layout;
263
264
265
266
267
268
269
270
271
272
273 public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
274 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
275 final Layout<? extends Serializable> layout, final int bufferSize) {
276 this.pattern = pattern;
277 this.append = append;
278 this.bufferedIO = bufferedIO;
279 this.bufferSize = bufferSize;
280 this.policy = policy;
281 this.strategy = strategy;
282 this.advertiseURI = advertiseURI;
283 this.layout = layout;
284 }
285 }
286
287
288
289
290 private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
291
292
293
294
295
296
297
298 @Override
299 public RollingFileManager createManager(final String name, final FactoryData data) {
300 final File file = new File(name);
301 final File parent = file.getParentFile();
302 if (null != parent && !parent.exists()) {
303 parent.mkdirs();
304 }
305 try {
306 file.createNewFile();
307 } catch (final IOException ioe) {
308 LOGGER.error("Unable to create file " + name, ioe);
309 return null;
310 }
311 final long size = data.append ? file.length() : 0;
312
313 OutputStream os;
314 try {
315 os = new FileOutputStream(name, data.append);
316 int bufferSize = data.bufferSize;
317 if (data.bufferedIO) {
318 os = new BufferedOutputStream(os, bufferSize);
319 } else {
320 bufferSize = -1;
321 }
322 final long time = file.lastModified();
323 return new RollingFileManager(name, data.pattern, os, data.append, size, time, data.policy,
324 data.strategy, data.advertiseURI, data.layout, bufferSize);
325 } catch (final FileNotFoundException ex) {
326 LOGGER.error("FileManager (" + name + ") " + ex);
327 }
328 return null;
329 }
330 }
331
332 }