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 super.stop();
144
145 Map<String, String> map = ContextUtil.getFilenameCollisionMap(context);
146 if (map == null || getName() == null)
147 return;
148
149 map.remove(getName());
150 }
151
152 protected boolean checkForFileCollisionInPreviousFileAppenders() {
153 boolean collisionsDetected = false;
154 if (fileName == null) {
155 return false;
156 }
157 @SuppressWarnings("unchecked")
158 Map<String, String> previousFilesMap = (Map<String, String>) context
159 .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 ["
177 + appenderName + "] defined earlier.");
178 }
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 public void openFile(String file_name) throws IOException {
195 lock.lock();
196 try {
197 File file = new File(file_name);
198 boolean result = FileUtil.createMissingParentDirectories(file);
199 if (!result) {
200 addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
201 }
202
203 ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize());
204 resilientFos.setContext(context);
205 setOutputStream(resilientFos);
206 } finally {
207 lock.unlock();
208 }
209 }
210
211
212
213
214
215
216 public boolean isPrudent() {
217 return prudent;
218 }
219
220
221
222
223
224
225
226 public void setPrudent(boolean prudent) {
227 this.prudent = prudent;
228 }
229
230 public void setAppend(boolean append) {
231 this.append = append;
232 }
233
234 public void setBufferSize(FileSize bufferSize) {
235 addInfo("Setting bufferSize to [" + bufferSize.toString() + "]");
236 this.bufferSize = bufferSize;
237 }
238
239 private void safeWrite(E event) throws IOException {
240 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
241 FileChannel fileChannel = resilientFOS.getChannel();
242 if (fileChannel == null) {
243 return;
244 }
245
246
247 boolean interrupted = Thread.interrupted();
248
249 FileLock fileLock = null;
250 try {
251 fileLock = fileChannel.lock();
252 long position = fileChannel.position();
253 long size = fileChannel.size();
254 if (size != position) {
255 fileChannel.position(size);
256 }
257 super.writeOut(event);
258 } catch (IOException e) {
259
260 resilientFOS.postIOFailure(e);
261 } finally {
262 if (fileLock != null && fileLock.isValid()) {
263 fileLock.release();
264 }
265
266
267 if (interrupted) {
268 Thread.currentThread().interrupt();
269 }
270 }
271 }
272
273 @Override
274 protected void writeOut(E event) throws IOException {
275 if (prudent) {
276 safeWrite(event);
277 } else {
278 super.writeOut(event);
279 }
280 }
281 }