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 lock.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 lock.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 private void safeWrite(E event) throws IOException {
243 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
244 FileChannel fileChannel = resilientFOS.getChannel();
245 if (fileChannel == null) {
246 return;
247 }
248
249
250 boolean interrupted = Thread.interrupted();
251
252 FileLock fileLock = null;
253 try {
254 fileLock = fileChannel.lock();
255 long position = fileChannel.position();
256 long size = fileChannel.size();
257 if (size != position) {
258 fileChannel.position(size);
259 }
260 super.writeOut(event);
261 } catch (IOException e) {
262
263 resilientFOS.postIOFailure(e);
264 } finally {
265 if (fileLock != null && fileLock.isValid()) {
266 fileLock.release();
267 }
268
269
270 if (interrupted) {
271 Thread.currentThread().interrupt();
272 }
273 }
274 }
275
276 @Override
277 protected void writeOut(E event) throws IOException {
278 if (prudent) {
279 safeWrite(event);
280 } else {
281 super.writeOut(event);
282 }
283 }
284 }