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 try {
120 openFile(getFile());
121 } catch (java.io.IOException e) {
122 errors++;
123 addError("openFile(" + fileName + "," + append + ") call failed.", e);
124 }
125 } else {
126 errors++;
127 addError("\"File\" property not set for appender named [" + name + "].");
128 }
129 if (errors == 0) {
130 super.start();
131 }
132 }
133
134 @Override
135 public void stop() {
136 if (!isStarted())
137 return;
138
139 super.stop();
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public void openFile(String file_name) throws IOException {
157 streamWriteLock.lock();
158 try {
159 File file = new File(file_name);
160 boolean result = FileUtil.createMissingParentDirectories(file);
161 if (!result) {
162 addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]");
163 }
164
165 ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize());
166 resilientFos.setContext(context);
167 setOutputStream(resilientFos);
168 } finally {
169 streamWriteLock.unlock();
170 }
171 }
172
173
174
175
176
177
178 public boolean isPrudent() {
179 return prudent;
180 }
181
182
183
184
185
186
187
188 public void setPrudent(boolean prudent) {
189 this.prudent = prudent;
190 }
191
192 public void setAppend(boolean append) {
193 this.append = append;
194 }
195
196 public void setBufferSize(FileSize bufferSize) {
197 addInfo("Setting bufferSize to [" + bufferSize.toString() + "]");
198 this.bufferSize = bufferSize;
199 }
200
201 @Override
202 protected void writeOut(E event) throws IOException {
203 if (prudent) {
204 safeWriteOut(event);
205 } else {
206 super.writeOut(event);
207 }
208 }
209
210 private void safeWriteOut(E event) {
211 byte[] byteArray = this.encoder.encode(event);
212 if (byteArray == null || byteArray.length == 0)
213 return;
214
215 streamWriteLock.lock();
216 try {
217 safeWriteBytes(byteArray);
218 } finally {
219 streamWriteLock.unlock();
220 }
221 }
222
223 private void safeWriteBytes(byte[] byteArray) {
224 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
225 FileChannel fileChannel = resilientFOS.getChannel();
226 if (fileChannel == null) {
227 return;
228 }
229
230
231 boolean interrupted = Thread.interrupted();
232
233 FileLock fileLock = null;
234 try {
235 fileLock = fileChannel.lock();
236 long position = fileChannel.position();
237 long size = fileChannel.size();
238 if (size != position) {
239 fileChannel.position(size);
240 }
241 writeByteArrayToOutputStreamWithPossibleFlush(byteArray);
242 } catch (IOException e) {
243
244 resilientFOS.postIOFailure(e);
245 } finally {
246 releaseFileLock(fileLock);
247
248
249 if (interrupted) {
250 Thread.currentThread().interrupt();
251 }
252 }
253 }
254
255 private void releaseFileLock(FileLock fileLock) {
256 if (fileLock != null && fileLock.isValid()) {
257 try {
258 fileLock.release();
259 } catch (IOException e) {
260 addError("failed to release lock", e);
261 }
262 }
263 }
264 }