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 writeHeader(fos);
200 }
201 defineAttributeView(Paths.get(filename));
202 return fos;
203 }
204
205 protected void defineAttributeView(final Path path) {
206 if (attributeViewEnabled) {
207 try {
208
209 path.toFile().createNewFile();
210
211 FileUtils.defineFilePosixAttributeView(path, filePermissions, fileOwner, fileGroup);
212 } catch (final Exception e) {
213 LOGGER.error("Could not define attribute view on path \"{}\" got {}", path, e.getMessage(), e);
214 }
215 }
216 }
217
218 @Override
219 protected synchronized void write(final byte[] bytes, final int offset, final int length,
220 final boolean immediateFlush) {
221 if (isLocking) {
222 try {
223 @SuppressWarnings("resource")
224 final FileChannel channel = ((FileOutputStream) getOutputStream()).getChannel();
225
226
227
228
229
230
231
232
233 try (final FileLock lock = channel.lock(0, Long.MAX_VALUE, false)) {
234 super.write(bytes, offset, length, immediateFlush);
235 }
236 } catch (final IOException ex) {
237 throw new AppenderLoggingException("Unable to obtain lock on " + getName(), ex);
238 }
239 } else {
240 super.write(bytes, offset, length, immediateFlush);
241 }
242 }
243
244
245
246
247
248
249
250
251
252 @Override
253 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
254 if (isLocking) {
255 try {
256 @SuppressWarnings("resource")
257 final FileChannel channel = ((FileOutputStream) getOutputStream()).getChannel();
258
259
260
261
262
263
264
265
266 try (final FileLock lock = channel.lock(0, Long.MAX_VALUE, false)) {
267 super.writeToDestination(bytes, offset, length);
268 }
269 } catch (final IOException ex) {
270 throw new AppenderLoggingException("Unable to obtain lock on " + getName(), ex);
271 }
272 } else {
273 super.writeToDestination(bytes, offset, length);
274 }
275 }
276
277
278
279
280
281 public String getFileName() {
282 return getName();
283 }
284
285
286
287
288 public boolean isAppend() {
289 return isAppend;
290 }
291
292
293
294
295
296 public boolean isCreateOnDemand() {
297 return createOnDemand;
298 }
299
300
301
302
303
304 public boolean isLocking() {
305 return isLocking;
306 }
307
308
309
310
311
312
313 public int getBufferSize() {
314 return bufferSize;
315 }
316
317
318
319
320
321
322
323 public Set<PosixFilePermission> getFilePermissions() {
324 return filePermissions;
325 }
326
327
328
329
330
331
332
333 public String getFileOwner() {
334 return fileOwner;
335 }
336
337
338
339
340
341
342
343 public String getFileGroup() {
344 return fileGroup;
345 }
346
347
348
349
350
351
352 public boolean isAttributeViewEnabled() {
353 return attributeViewEnabled;
354 }
355
356
357
358
359
360
361 @Override
362 public Map<String, String> getContentFormat() {
363 final Map<String, String> result = new HashMap<>(super.getContentFormat());
364 result.put("fileURI", advertiseURI);
365 return result;
366 }
367
368
369
370
371 private static class FactoryData extends ConfigurationFactoryData {
372 private final boolean append;
373 private final boolean locking;
374 private final boolean bufferedIo;
375 private final int bufferSize;
376 private final boolean createOnDemand;
377 private final String advertiseURI;
378 private final Layout<? extends Serializable> layout;
379 private final String filePermissions;
380 private final String fileOwner;
381 private final String fileGroup;
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397 public FactoryData(final boolean append, final boolean locking, final boolean bufferedIo, final int bufferSize,
398 final boolean createOnDemand, final String advertiseURI, final Layout<? extends Serializable> layout,
399 final String filePermissions, final String fileOwner, final String fileGroup,
400 final Configuration configuration) {
401 super(configuration);
402 this.append = append;
403 this.locking = locking;
404 this.bufferedIo = bufferedIo;
405 this.bufferSize = bufferSize;
406 this.createOnDemand = createOnDemand;
407 this.advertiseURI = advertiseURI;
408 this.layout = layout;
409 this.filePermissions = filePermissions;
410 this.fileOwner = fileOwner;
411 this.fileGroup = fileGroup;
412 }
413 }
414
415
416
417
418 private static class FileManagerFactory implements ManagerFactory<FileManager, FactoryData> {
419
420
421
422
423
424
425
426 @Override
427 public FileManager createManager(final String name, final FactoryData data) {
428 final File file = new File(name);
429 try {
430 FileUtils.makeParentDirs(file);
431 final boolean writeHeader = !data.append || !file.exists();
432 final int actualSize = data.bufferedIo ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
433 final ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[actualSize]);
434 final FileOutputStream fos = data.createOnDemand ? null : new FileOutputStream(file, data.append);
435 final FileManager fm = new FileManager(data.getLoggerContext(), name, fos, data.append, data.locking,
436 data.createOnDemand, data.advertiseURI, data.layout,
437 data.filePermissions, data.fileOwner, data.fileGroup, writeHeader, byteBuffer);
438 if (fos != null && fm.attributeViewEnabled) {
439 fm.defineAttributeView(file.toPath());
440 }
441 return fm;
442 } catch (final IOException ex) {
443 LOGGER.error("FileManager (" + name + ") " + ex, ex);
444 }
445 return null;
446 }
447 }
448
449 }