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.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.io.Serializable;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.FileChannel;
26 import java.nio.channels.FileLock;
27 import java.nio.file.FileSystems;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.nio.file.attribute.FileOwnerAttributeView;
32 import java.nio.file.attribute.FileTime;
33 import java.nio.file.attribute.PosixFileAttributeView;
34 import java.nio.file.attribute.PosixFilePermission;
35 import java.nio.file.attribute.PosixFilePermissions;
36 import java.util.Date;
37 import java.util.HashMap;
38 import java.util.Map;
39 import java.util.Set;
40
41 import org.apache.logging.log4j.core.Layout;
42 import org.apache.logging.log4j.core.LoggerContext;
43 import org.apache.logging.log4j.core.config.Configuration;
44 import org.apache.logging.log4j.core.util.Constants;
45 import org.apache.logging.log4j.core.util.FileUtils;
46
47
48
49
50
51 public class FileManager extends OutputStreamManager {
52
53 private static final FileManagerFactory FACTORY = new FileManagerFactory();
54
55 private final boolean isAppend;
56 private final boolean createOnDemand;
57 private final boolean isLocking;
58 private final String advertiseURI;
59 private final int bufferSize;
60 private final Set<PosixFilePermission> filePermissions;
61 private final String fileOwner;
62 private final String fileGroup;
63 private final boolean attributeViewEnabled;
64
65
66
67
68 @Deprecated
69 protected FileManager(final String fileName, final OutputStream os, final boolean append, final boolean locking,
70 final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize,
71 final boolean writeHeader) {
72 this(fileName, os, append, locking, advertiseURI, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
73 }
74
75
76
77
78
79 @Deprecated
80 protected FileManager(final String fileName, final OutputStream os, final boolean append, final boolean locking,
81 final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader,
82 final ByteBuffer buffer) {
83 super(os, fileName, layout, writeHeader, buffer);
84 this.isAppend = append;
85 this.createOnDemand = false;
86 this.isLocking = locking;
87 this.advertiseURI = advertiseURI;
88 this.bufferSize = buffer.capacity();
89 this.filePermissions = null;
90 this.fileOwner = null;
91 this.fileGroup = null;
92 this.attributeViewEnabled = false;
93 }
94
95
96
97
98
99 @Deprecated
100 protected FileManager(final LoggerContext loggerContext, final String fileName, final OutputStream os, final boolean append, final boolean locking,
101 final boolean createOnDemand, final String advertiseURI, final Layout<? extends Serializable> layout,
102 final boolean writeHeader, final ByteBuffer buffer) {
103 super(loggerContext, os, fileName, createOnDemand, layout, writeHeader, buffer);
104 this.isAppend = append;
105 this.createOnDemand = createOnDemand;
106 this.isLocking = locking;
107 this.advertiseURI = advertiseURI;
108 this.bufferSize = buffer.capacity();
109 this.filePermissions = null;
110 this.fileOwner = null;
111 this.fileGroup = null;
112 this.attributeViewEnabled = false;
113 }
114
115
116
117
118 protected FileManager(final LoggerContext loggerContext, final String fileName, final OutputStream os, final boolean append, final boolean locking,
119 final boolean createOnDemand, final String advertiseURI, final Layout<? extends Serializable> layout,
120 final String filePermissions, final String fileOwner, final String fileGroup, final boolean writeHeader,
121 final ByteBuffer buffer) {
122 super(loggerContext, os, fileName, createOnDemand, layout, writeHeader, buffer);
123 this.isAppend = append;
124 this.createOnDemand = createOnDemand;
125 this.isLocking = locking;
126 this.advertiseURI = advertiseURI;
127 this.bufferSize = buffer.capacity();
128
129 final Set<String> views = FileSystems.getDefault().supportedFileAttributeViews();
130 if (views.contains("posix")) {
131 this.filePermissions = filePermissions != null ? PosixFilePermissions.fromString(filePermissions) : null;
132 this.fileGroup = fileGroup;
133 } else {
134 this.filePermissions = null;
135 this.fileGroup = null;
136 if (filePermissions != null) {
137 LOGGER.warn("Posix file attribute permissions defined but it is not supported by this files system.");
138 }
139 if (fileGroup != null) {
140 LOGGER.warn("Posix file attribute group defined but it is not supported by this files system.");
141 }
142 }
143
144 if (views.contains("owner")) {
145 this.fileOwner = fileOwner;
146 } else {
147 this.fileOwner = null;
148 if (fileOwner != null) {
149 LOGGER.warn("Owner file attribute defined but it is not supported by this files system.");
150 }
151 }
152
153
154 this.attributeViewEnabled = this.filePermissions != null || this.fileOwner != null || this.fileGroup != null;
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 public static FileManager getFileManager(final String fileName, final boolean append, boolean locking,
174 final boolean bufferedIo, final boolean createOnDemand, final String advertiseUri,
175 final Layout<? extends Serializable> layout,
176 final int bufferSize, final String filePermissions, final String fileOwner, final String fileGroup,
177 final Configuration configuration) {
178
179 if (locking && bufferedIo) {
180 locking = false;
181 }
182 return narrow(FileManager.class, getManager(fileName, new FactoryData(append, locking, bufferedIo, bufferSize,
183 createOnDemand, advertiseUri, layout, filePermissions, fileOwner, fileGroup, configuration), FACTORY));
184 }
185
186 @Override
187 protected OutputStream createOutputStream() throws IOException {
188 final String filename = getFileName();
189 LOGGER.debug("Now writing to {} at {}", filename, new Date());
190 final File file = new File(filename);
191 final FileOutputStream fos = new FileOutputStream(file, isAppend);
192 if (file.exists() && file.length() == 0) {
193 try {
194 FileTime now = FileTime.fromMillis(System.currentTimeMillis());
195 Files.setAttribute(file.toPath(), "creationTime", now);
196 } catch (Exception ex) {
197 LOGGER.warn("Unable to set current file tiem for {}", filename);
198 }
199 }
200 defineAttributeView(Paths.get(filename));
201 return fos;
202 }
203
204 protected void defineAttributeView(final Path path) {
205 if (attributeViewEnabled) {
206 try {
207
208 path.toFile().createNewFile();
209
210 FileUtils.defineFilePosixAttributeView(path, filePermissions, fileOwner, fileGroup);
211 } catch (final Exception e) {
212 LOGGER.error("Could not define attribute view on path \"{}\" got {}", path, e.getMessage(), e);
213 }
214 }
215 }
216
217 @Override
218 protected synchronized void write(final byte[] bytes, final int offset, final int length,
219 final boolean immediateFlush) {
220 if (isLocking) {
221 try {
222 @SuppressWarnings("resource")
223 final FileChannel channel = ((FileOutputStream) getOutputStream()).getChannel();
224
225
226
227
228
229
230
231
232 try (final FileLock lock = channel.lock(0, Long.MAX_VALUE, false)) {
233 super.write(bytes, offset, length, immediateFlush);
234 }
235 } catch (final IOException ex) {
236 throw new AppenderLoggingException("Unable to obtain lock on " + getName(), ex);
237 }
238 } else {
239 super.write(bytes, offset, length, immediateFlush);
240 }
241 }
242
243
244
245
246
247
248
249
250
251 @Override
252 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
253 if (isLocking) {
254 try {
255 @SuppressWarnings("resource")
256 final FileChannel channel = ((FileOutputStream) getOutputStream()).getChannel();
257
258
259
260
261
262
263
264
265 try (final FileLock lock = channel.lock(0, Long.MAX_VALUE, false)) {
266 super.writeToDestination(bytes, offset, length);
267 }
268 } catch (final IOException ex) {
269 throw new AppenderLoggingException("Unable to obtain lock on " + getName(), ex);
270 }
271 } else {
272 super.writeToDestination(bytes, offset, length);
273 }
274 }
275
276
277
278
279
280 public String getFileName() {
281 return getName();
282 }
283
284
285
286
287 public boolean isAppend() {
288 return isAppend;
289 }
290
291
292
293
294
295 public boolean isCreateOnDemand() {
296 return createOnDemand;
297 }
298
299
300
301
302
303 public boolean isLocking() {
304 return isLocking;
305 }
306
307
308
309
310
311
312 public int getBufferSize() {
313 return bufferSize;
314 }
315
316
317
318
319
320
321
322 public Set<PosixFilePermission> getFilePermissions() {
323 return filePermissions;
324 }
325
326
327
328
329
330
331
332 public String getFileOwner() {
333 return fileOwner;
334 }
335
336
337
338
339
340
341
342 public String getFileGroup() {
343 return fileGroup;
344 }
345
346
347
348
349
350
351 public boolean isAttributeViewEnabled() {
352 return attributeViewEnabled;
353 }
354
355
356
357
358
359
360 @Override
361 public Map<String, String> getContentFormat() {
362 final Map<String, String> result = new HashMap<>(super.getContentFormat());
363 result.put("fileURI", advertiseURI);
364 return result;
365 }
366
367
368
369
370 private static class FactoryData extends ConfigurationFactoryData {
371 private final boolean append;
372 private final boolean locking;
373 private final boolean bufferedIo;
374 private final int bufferSize;
375 private final boolean createOnDemand;
376 private final String advertiseURI;
377 private final Layout<? extends Serializable> layout;
378 private final String filePermissions;
379 private final String fileOwner;
380 private final String fileGroup;
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396 public FactoryData(final boolean append, final boolean locking, final boolean bufferedIo, final int bufferSize,
397 final boolean createOnDemand, final String advertiseURI, final Layout<? extends Serializable> layout,
398 final String filePermissions, final String fileOwner, final String fileGroup,
399 final Configuration configuration) {
400 super(configuration);
401 this.append = append;
402 this.locking = locking;
403 this.bufferedIo = bufferedIo;
404 this.bufferSize = bufferSize;
405 this.createOnDemand = createOnDemand;
406 this.advertiseURI = advertiseURI;
407 this.layout = layout;
408 this.filePermissions = filePermissions;
409 this.fileOwner = fileOwner;
410 this.fileGroup = fileGroup;
411 }
412 }
413
414
415
416
417 private static class FileManagerFactory implements ManagerFactory<FileManager, FactoryData> {
418
419
420
421
422
423
424
425 @Override
426 public FileManager createManager(final String name, final FactoryData data) {
427 final File file = new File(name);
428 try {
429 FileUtils.makeParentDirs(file);
430 final boolean writeHeader = !data.append || !file.exists();
431 final int actualSize = data.bufferedIo ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
432 final ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[actualSize]);
433 final FileOutputStream fos = data.createOnDemand ? null : new FileOutputStream(file, data.append);
434 final FileManager fm = new FileManager(data.getLoggerContext(), name, fos, data.append, data.locking,
435 data.createOnDemand, data.advertiseURI, data.layout,
436 data.filePermissions, data.fileOwner, data.fileGroup, writeHeader, byteBuffer);
437 if (fos != null && fm.attributeViewEnabled) {
438 fm.defineAttributeView(file.toPath());
439 }
440 return fm;
441 } catch (final IOException ex) {
442 LOGGER.error("FileManager (" + name + ") " + ex, ex);
443 }
444 return null;
445 }
446 }
447
448 }