1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core;
15
16 import static ch.qos.logback.core.CoreConstants.CODES_URL;
17 import static ch.qos.logback.core.CoreConstants.MORE_INFO_PREFIX;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.channels.FileChannel;
22 import java.nio.channels.FileLock;
23 import java.util.Map;
24 import java.util.Map.Entry;
25
26 import ch.qos.logback.core.recovery.ResilientFileOutputStream;
27 import ch.qos.logback.core.util.ContextUtil;
28 import ch.qos.logback.core.util.FileSize;
29 import ch.qos.logback.core.util.FileUtil;
30
31
32
33
34
35
36
37
38
39 public class FileAppender<E> extends OutputStreamAppender<E> {
40
41 public static final long DEFAULT_BUFFER_SIZE = 8192;
42
43 static protected String COLLISION_WITH_EARLIER_APPENDER_URL = CODES_URL + "#earlier_fa_collision";
44
45
46
47
48
49
50 protected boolean append = true;
51
52
53
54
55 protected String fileName = null;
56
57 private boolean prudent = false;
58
59 private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE);
60
61
62
63
64
65 public void setFile(String file) {
66 if (file == null) {
67 fileName = file;
68 } else {
69
70
71 fileName = file.trim();
72 }
73 }
74
75
76
77
78 public boolean isAppend() {
79 return append;
80 }
81
82
83
84
85
86
87
88 final public String rawFileProperty() {
89 return fileName;
90 }
91
92
93
94
95
96
97
98
99 public String getFile() {
100 return fileName;
101 }
102
103
104
105
106
107 public void start() {
108 int errors = 0;
109 if (getFile() != null) {
110 addInfo("File property is set to [" + fileName + "]");
111
112 if (prudent) {
113 if (!isAppend()) {
114 setAppend(true);
115 addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode");
116 }
117 }
118
119 if (checkForFileCollisionInPreviousFileAppenders()) {
120 addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.");
121 addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL);
122 errors++;
123 } else {
124
125 try {
126 openFile(getFile());
127 } catch (java.io.IOException e) {
128 errors++;
129 addError("openFile(" + fileName + "," + append + ") call failed.", e);
130 }
131 }
132 } else {
133 errors++;
134 addError("\"File\" property not set for appender named [" + name + "].");
135 }
136 if (errors == 0) {
137 super.start();
138 }
139 }
140
141 @Override
142 public void stop() {
143 if(!isStarted())
144 return;
145
146 super.stop();
147
148 Map<String, String> map = ContextUtil.getFilenameCollisionMap(context);
149 if (map == null || getName() == null)
150 return;
151
152 map.remove(getName());
153 }
154
155 protected boolean checkForFileCollisionInPreviousFileAppenders() {
156 boolean collisionsDetected = false;
157 if (fileName == null) {
158 return false;
159 }
160 @SuppressWarnings("unchecked")
161 Map<String, String> previousFilesMap = (Map<String, String>) context
162 .getObject(CoreConstants.FA_FILENAME_COLLISION_MAP);
163 if (previousFilesMap == null) {
164 return collisionsDetected;
165 }
166 for (Entry<String, String> entry : previousFilesMap.entrySet()) {
167 if (fileName.equals(entry.getValue())) {
168 addErrorForCollision("File", entry.getValue(), entry.getKey());
169 collisionsDetected = true;
170 }
171 }
172 if (name != null) {
173 previousFilesMap.put(getName(), fileName);
174 }
175 return collisionsDetected;
176 }
177
178 protected void addErrorForCollision(String optionName, String optionValue, String appenderName) {
179 addError("'" + optionName + "' option has the same value \"" + optionValue + "\" as that given for appender ["
180 + appenderName + "] defined earlier.");
181 }
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 public void openFile(String file_name) throws IOException {
198 streamWriteLock.lock();
199 try {
200 File file = new File(file_name);
201 boolean result = FileUtil.createMissingParentDirectories(file);
202 if (!result) {
203 addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
204 }
205
206 ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize());
207 resilientFos.setContext(context);
208 setOutputStream(resilientFos);
209 } finally {
210 streamWriteLock.unlock();
211 }
212 }
213
214
215
216
217
218
219 public boolean isPrudent() {
220 return prudent;
221 }
222
223
224
225
226
227
228
229 public void setPrudent(boolean prudent) {
230 this.prudent = prudent;
231 }
232
233 public void setAppend(boolean append) {
234 this.append = append;
235 }
236
237 public void setBufferSize(FileSize bufferSize) {
238 addInfo("Setting bufferSize to [" + bufferSize.toString() + "]");
239 this.bufferSize = bufferSize;
240 }
241
242 @Override
243 protected void writeOut(E event) throws IOException {
244 if (prudent) {
245 safeWriteOut(event);
246 } else {
247 super.writeOut(event);
248 }
249 }
250
251 private void safeWriteOut(E event) {
252 byte[] byteArray = this.encoder.encode(event);
253 if (byteArray == null || byteArray.length == 0)
254 return;
255
256 streamWriteLock.lock();
257 try {
258 safeWriteBytes(byteArray);
259 } finally {
260 streamWriteLock.unlock();
261 }
262 }
263
264 private void safeWriteBytes(byte[] byteArray) {
265 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
266 FileChannel fileChannel = resilientFOS.getChannel();
267 if (fileChannel == null) {
268 return;
269 }
270
271
272 boolean interrupted = Thread.interrupted();
273
274 FileLock fileLock = null;
275 try {
276 fileLock = fileChannel.lock();
277 long position = fileChannel.position();
278 long size = fileChannel.size();
279 if (size != position) {
280 fileChannel.position(size);
281 }
282 writeByteArrayToOutputStreamWithPossibleFlush(byteArray);
283 } catch (IOException e) {
284
285 resilientFOS.postIOFailure(e);
286 } finally {
287 releaseFileLock(fileLock);
288
289
290 if (interrupted) {
291 Thread.currentThread().interrupt();
292 }
293 }
294 }
295
296 private void releaseFileLock(FileLock fileLock) {
297 if (fileLock != null && fileLock.isValid()) {
298 try {
299 fileLock.release();
300 } catch (IOException e) {
301 addError("failed to release lock", e);
302 }
303 }
304 }
305 }