File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\message\TraceSystem.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.message;
8
9 import java.io.IOException;
10 import java.io.PrintStream;
11 import java.io.PrintWriter;
12 import java.io.Writer;
13 import java.sql.DriverManager;
14 import java.text.SimpleDateFormat;
15 import java.util.Date;
16 import java.util.HashMap;
17 import org.h2.constant.ErrorCode;
18 import org.h2.engine.Constants;
19 import org.h2.jdbc.JdbcSQLException;
20 import org.h2.store.fs.FileUtils;
21 import org.h2.util.IOUtils;
22 import org.h2.util.New;
23
24 /**
25 * The trace mechanism is the logging facility of this database. There is
26 * usually one trace system per database. It is called 'trace' because the term
27 * 'log' is already used in the database domain and means 'transaction log'. It
28 * is possible to write after close was called, but that means for each write
29 * the file will be opened and closed again (which is slower).
30 */
31 public class TraceSystem implements TraceWriter {
32
33 /**
34 * The parent trace level should be used.
35 */
36 public static final int PARENT = -1;
37
38 /**
39 * This trace level means nothing should be written.
40 */
41 public static final int OFF = 0;
42
43 /**
44 * This trace level means only errors should be written.
45 */
46 public static final int ERROR = 1;
47
48 /**
49 * This trace level means errors and informational messages should be
50 * written.
51 */
52 public static final int INFO = 2;
53
54 /**
55 * This trace level means all type of messages should be written.
56 */
57 public static final int DEBUG = 3;
58
59 /**
60 * This trace level means all type of messages should be written, but
61 * instead of using the trace file the messages should be written to SLF4J.
62 */
63 public static final int ADAPTER = 4;
64
65 /**
66 * The default level for system out trace messages.
67 */
68 public static final int DEFAULT_TRACE_LEVEL_SYSTEM_OUT = OFF;
69
70 /**
71 * The default level for file trace messages.
72 */
73 public static final int DEFAULT_TRACE_LEVEL_FILE = ERROR;
74
75 /**
76 * The default maximum trace file size. It is currently 64 MB. Additionally,
77 * there could be a .old file of the same size.
78 */
79 private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024;
80
81 private static final int CHECK_SIZE_EACH_WRITES = 128;
82
83 private int levelSystemOut = DEFAULT_TRACE_LEVEL_SYSTEM_OUT;
84 private int levelFile = DEFAULT_TRACE_LEVEL_FILE;
85 private int levelMax;
86 private int maxFileSize = DEFAULT_MAX_FILE_SIZE;
87 private String fileName;
88 private HashMap<String, Trace> traces;
89 private SimpleDateFormat dateFormat;
90 private Writer fileWriter;
91 private PrintWriter printWriter;
92 private int checkSize;
93 private boolean closed;
94 private boolean writingErrorLogged;
95 private TraceWriter writer = this;
96 private PrintStream sysOut = System.out;
97
98 /**
99 * Create a new trace system object.
100 *
101 * @param fileName the file name
102 */
103 public TraceSystem(String fileName) {
104 this.fileName = fileName;
105 updateLevel();
106 }
107
108 private void updateLevel() {
109 levelMax = Math.max(levelSystemOut, levelFile);
110 }
111
112 /**
113 * Set the print stream to use instead of System.out.
114 *
115 * @param out the new print stream
116 */
117 public void setSysOut(PrintStream out) {
118 this.sysOut = out;
119 }
120
121 /**
122 * Write the exception to the driver manager log writer if configured.
123 *
124 * @param e the exception
125 */
126 public static void traceThrowable(Throwable e) {
127 PrintWriter writer = DriverManager.getLogWriter();
128 if (writer != null) {
129 e.printStackTrace(writer);
130 }
131 }
132
133 /**
134 * Get or create a trace object for this module. Trace modules with names
135 * such as "JDBC[1]" are not cached (modules where the name ends with "]").
136 * All others are cached.
137 *
138 * @param module the module name
139 * @return the trace object
140 */
141 public synchronized Trace getTrace(String module) {
142 if (module.endsWith("]")) {
143 return new Trace(writer, module);
144 }
145 if (traces == null) {
146 traces = New.hashMap(16);
147 }
148 Trace t = traces.get(module);
149 if (t == null) {
150 t = new Trace(writer, module);
151 traces.put(module, t);
152 }
153 return t;
154 }
155
156 public boolean isEnabled(int level) {
157 return level <= this.levelMax;
158 }
159
160 /**
161 * Set the trace file name.
162 *
163 * @param name the file name
164 */
165 public void setFileName(String name) {
166 this.fileName = name;
167 }
168
169 /**
170 * Set the maximum trace file size in bytes.
171 *
172 * @param max the maximum size
173 */
174 public void setMaxFileSize(int max) {
175 this.maxFileSize = max;
176 }
177
178 /**
179 * Set the trace level to use for System.out
180 *
181 * @param level the new level
182 */
183 public void setLevelSystemOut(int level) {
184 levelSystemOut = level;
185 updateLevel();
186 }
187
188 /**
189 * Set the file trace level.
190 *
191 * @param level the new level
192 */
193 public void setLevelFile(int level) {
194 if (level == ADAPTER) {
195 String adapterClass = "org.h2.message.TraceWriterAdapter";
196 try {
197 writer = (TraceWriter) Class.forName(adapterClass).newInstance();
198 } catch (Throwable e) {
199 e = DbException.get(ErrorCode.CLASS_NOT_FOUND_1, e, adapterClass);
200 write(ERROR, Trace.DATABASE, adapterClass, e);
201 return;
202 }
203 String name = fileName;
204 if (name != null) {
205 if (name.endsWith(Constants.SUFFIX_TRACE_FILE)) {
206 name = name.substring(0, name.length() - Constants.SUFFIX_TRACE_FILE.length());
207 }
208 int idx = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'));
209 if (idx >= 0) {
210 name = name.substring(idx + 1);
211 }
212 writer.setName(name);
213 }
214 }
215 levelFile = level;
216 updateLevel();
217 }
218
219 public int getLevelFile() {
220 return levelFile;
221 }
222
223 private synchronized String format(String module, String s) {
224 if (dateFormat == null) {
225 dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss ");
226 }
227 return dateFormat.format(new Date()) + module + ": " + s;
228 }
229
230 public void write(int level, String module, String s, Throwable t) {
231 if (level <= levelSystemOut || level > this.levelMax) {
232 // level <= levelSystemOut: the system out level is set higher
233 // level > this.level: the level for this module is set higher
234 sysOut.println(format(module, s));
235 if (t != null && levelSystemOut == DEBUG) {
236 t.printStackTrace(sysOut);
237 }
238 }
239 if (fileName != null) {
240 if (level <= levelFile) {
241 writeFile(format(module, s), t);
242 }
243 }
244 }
245
246 private synchronized void writeFile(String s, Throwable t) {
247 try {
248 if (checkSize++ >= CHECK_SIZE_EACH_WRITES) {
249 checkSize = 0;
250 closeWriter();
251 if (maxFileSize > 0 && FileUtils.size(fileName) > maxFileSize) {
252 String old = fileName + ".old";
253 FileUtils.delete(old);
254 FileUtils.moveTo(fileName, old);
255 }
256 }
257 if (!openWriter()) {
258 return;
259 }
260 printWriter.println(s);
261 if (t != null) {
262 if (levelFile == ERROR && t instanceof JdbcSQLException) {
263 JdbcSQLException se = (JdbcSQLException) t;
264 int code = se.getErrorCode();
265 if (ErrorCode.isCommon(code)) {
266 printWriter.println(t.toString());
267 } else {
268 t.printStackTrace(printWriter);
269 }
270 } else {
271 t.printStackTrace(printWriter);
272 }
273 }
274 printWriter.flush();
275 if (closed) {
276 closeWriter();
277 }
278 } catch (Exception e) {
279 logWritingError(e);
280 }
281 }
282
283 private void logWritingError(Exception e) {
284 if (writingErrorLogged) {
285 return;
286 }
287 writingErrorLogged = true;
288 Exception se = DbException.get(ErrorCode.TRACE_FILE_ERROR_2, e, fileName, e.toString());
289 // print this error only once
290 fileName = null;
291 sysOut.println(se);
292 se.printStackTrace();
293 }
294
295 private boolean openWriter() {
296 if (printWriter == null) {
297 try {
298 FileUtils.createDirectories(FileUtils.getParent(fileName));
299 if (FileUtils.exists(fileName) && !FileUtils.canWrite(fileName)) {
300 // read only database: don't log error if the trace file
301 // can't be opened
302 return false;
303 }
304 fileWriter = IOUtils.getBufferedWriter(FileUtils.newOutputStream(fileName, true));
305 printWriter = new PrintWriter(fileWriter, true);
306 } catch (Exception e) {
307 logWritingError(e);
308 return false;
309 }
310 }
311 return true;
312 }
313
314 private synchronized void closeWriter() {
315 if (printWriter != null) {
316 printWriter.flush();
317 printWriter.close();
318 printWriter = null;
319 }
320 if (fileWriter != null) {
321 try {
322 fileWriter.close();
323 } catch (IOException e) {
324 // ignore
325 }
326 fileWriter = null;
327 }
328 }
329
330 /**
331 * Close the writers, and the files if required. It is still possible to
332 * write after closing, however after each write the file is closed again
333 * (slowing down tracing).
334 */
335 public void close() {
336 closeWriter();
337 closed = true;
338 }
339
340 public void setName(String name) {
341 // nothing to do (the file name is already set)
342 }
343
344 }
345