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