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
108 public void start() {
109 int errors = 0;
110 if (getFile() != null) {
111 addInfo("File property is set to [" + fileName + "]");
112
113 if (prudent) {
114 if (!isAppend()) {
115 setAppend(true);
116 addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode");
117 }
118 }
119
120 if (checkForFileCollisionInPreviousFileAppenders()) {
121 addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.");
122 addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL);
123 errors++;
124 } else {
125
126 try {
127 openFile(getFile());
128 } catch (java.io.IOException e) {
129 errors++;
130 addError("openFile(" + fileName + "," + append + ") call failed.", e);
131 }
132 }
133 } else {
134 errors++;
135 addError("\"File\" property not set for appender named [" + name + "].");
136 }
137 if (errors == 0) {
138 super.start();
139 }
140 }
141
142 @Override
143 public void stop() {
144 super.stop();
145
146 Map<String, String> map = ContextUtil.getFilenameCollisionMap(context);
147 if (map == null || getName() == null)
148 return;
149
150 map.remove(getName());
151 }
152
153 protected boolean checkForFileCollisionInPreviousFileAppenders() {
154 boolean collisionsDetected = false;
155 if (fileName == null) {
156 return false;
157 }
158 @SuppressWarnings("unchecked")
159 Map<String, String> previousFilesMap = (Map<String, String>) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP);
160 if (previousFilesMap == null) {
161 return collisionsDetected;
162 }
163 for (Entry<String, String> entry : previousFilesMap.entrySet()) {
164 if (fileName.equals(entry.getValue())) {
165 addErrorForCollision("File", entry.getValue(), entry.getKey());
166 collisionsDetected = true;
167 }
168 }
169 if (name != null) {
170 previousFilesMap.put(getName(), fileName);
171 }
172 return collisionsDetected;
173 }
174
175 protected void addErrorForCollision(String optionName, String optionValue, String appenderName) {
176 addError("'" + optionName + "' option has the same value \"" + optionValue + "\" as that given for appender [" + appenderName + "] defined earlier.");
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 public void openFile(String file_name) throws IOException {
196 lock.lock();
197 try {
198 File file = new File(file_name);
199 boolean result = FileUtil.createMissingParentDirectories(file);
200 if (!result) {
201 addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
202 }
203
204 ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize());
205 resilientFos.setContext(context);
206 setOutputStream(resilientFos);
207 } finally {
208 lock.unlock();
209 }
210 }
211
212
213
214
215
216
217 public boolean isPrudent() {
218 return prudent;
219 }
220
221
222
223
224
225
226
227 public void setPrudent(boolean prudent) {
228 this.prudent = prudent;
229 }
230
231 public void setAppend(boolean append) {
232 this.append = append;
233 }
234
235 public void setBufferSize(FileSize bufferSize) {
236 addInfo("Setting bufferSize to ["+bufferSize.toString()+"]");
237 this.bufferSize = bufferSize;
238 }
239
240 private void safeWrite(E event) throws IOException {
241 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
242 FileChannel fileChannel = resilientFOS.getChannel();
243 if (fileChannel == null) {
244 return;
245 }
246
247
248 boolean interrupted = Thread.interrupted();
249
250 FileLock fileLock = null;
251 try {
252 fileLock = fileChannel.lock();
253 long position = fileChannel.position();
254 long size = fileChannel.size();
255 if (size != position) {
256 fileChannel.position(size);
257 }
258 super.writeOut(event);
259 } catch (IOException e) {
260
261 resilientFOS.postIOFailure(e);
262 } finally {
263 if (fileLock != null && fileLock.isValid()) {
264 fileLock.release();
265 }
266
267
268 if (interrupted) {
269 Thread.currentThread().interrupt();
270 }
271 }
272 }
273
274 @Override
275 protected void writeOut(E event) throws IOException {
276 if (prudent) {
277 safeWrite(event);
278 } else {
279 super.writeOut(event);
280 }
281 }
282 }