001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.message;
007:
008: import java.io.IOException;
009: import java.io.PrintWriter;
010: import java.io.Writer;
011: import java.sql.DriverManager;
012: import java.sql.SQLException;
013: import java.text.SimpleDateFormat;
014: import java.util.Date;
015:
016: import org.h2.constant.ErrorCode;
017: import org.h2.constant.SysProperties;
018: import org.h2.engine.Constants;
019: import org.h2.util.FileUtils;
020: import org.h2.util.SmallLRUCache;
021:
022: /**
023: * The trace mechanism is the logging facility of this database. There is
024: * usually one trace system per database. It is called 'trace' because the term
025: * 'log' is already used in the database domain and means 'transaction log'. It
026: * is possible to write after close was called, but that means for each write
027: * the log file will be opened and closed again (which is slower).
028: */
029: public class TraceSystem {
030: public static final int OFF = 0, ERROR = 1, INFO = 2, DEBUG = 3;
031:
032: // TODO log total and free memory from time to time
033:
034: // max file size is currently 64 MB,
035: // and then there could be a .old file of the same size
036: private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024;
037: public static final int DEFAULT_TRACE_LEVEL_SYSTEM_OUT = OFF;
038: public static final int DEFAULT_TRACE_LEVEL_FILE = ERROR;
039: private static final int CHECK_FILE_TIME = 4000;
040: private int levelSystemOut = DEFAULT_TRACE_LEVEL_SYSTEM_OUT;
041: private int levelFile = DEFAULT_TRACE_LEVEL_FILE;
042: private int maxFileSize = DEFAULT_MAX_FILE_SIZE;
043: private String fileName;
044: private long lastCheck;
045: private SmallLRUCache traces;
046: private SimpleDateFormat dateFormat;
047: private Writer fileWriter;
048: private PrintWriter printWriter;
049: private static final int CHECK_SIZE_EACH_WRITES = 128;
050: private int checkSize;
051: private boolean closed;
052: private boolean manualEnabling = true;
053: private boolean writingErrorLogged;
054:
055: public static void traceThrowable(Throwable e) {
056: PrintWriter writer = DriverManager.getLogWriter();
057: if (writer != null) {
058: e.printStackTrace(writer);
059: }
060: }
061:
062: public void setManualEnabling(boolean value) {
063: this .manualEnabling = value;
064: }
065:
066: public TraceSystem(String fileName, boolean init) {
067: this .fileName = fileName;
068: traces = new SmallLRUCache(100);
069: dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss ");
070: if (fileName != null && init) {
071: try {
072: openWriter();
073: } catch (Exception e) {
074: logWritingError(e);
075: }
076: }
077: }
078:
079: public Trace getTrace(String module) {
080: Trace t = (Trace) traces.get(module);
081: if (t == null) {
082: t = new Trace(this , module);
083: traces.put(module, t);
084: }
085: return t;
086: }
087:
088: public boolean isEnabled(int level) {
089: int max = Math.max(levelSystemOut, levelFile);
090: return level <= max;
091: }
092:
093: public void setFileName(String name) {
094: this .fileName = name;
095: }
096:
097: public void setMaxFileSize(int max) {
098: this .maxFileSize = max;
099: }
100:
101: public int getMaxFileSize() {
102: return maxFileSize;
103: }
104:
105: public void setLevelSystemOut(int l) {
106: levelSystemOut = l;
107: }
108:
109: public int getLevelFile() {
110: return levelFile;
111: }
112:
113: public int getLevelSystemOut() {
114: return levelSystemOut;
115: }
116:
117: public void setLevelFile(int l) {
118: levelFile = l;
119: }
120:
121: private String format(String module, String s) {
122: synchronized (dateFormat) {
123: return dateFormat.format(new Date()) + module + ": " + s;
124: }
125: }
126:
127: void write(int l, String module, String s, Throwable t) {
128: if (l <= levelSystemOut) {
129: System.out.println(format(module, s));
130: if (t != null && levelSystemOut == DEBUG) {
131: t.printStackTrace();
132: }
133: }
134: if (fileName != null) {
135: if (l > levelFile) {
136: enableIfRequired();
137: }
138: if (l <= levelFile) {
139: writeFile(format(module, s), t);
140: }
141: }
142: }
143:
144: private void enableIfRequired() {
145: if (!manualEnabling) {
146: return;
147: }
148: long time = System.currentTimeMillis();
149: if (time > lastCheck + CHECK_FILE_TIME) {
150: String checkFile = fileName
151: + Constants.SUFFIX_TRACE_START_FILE;
152: lastCheck = time;
153: if (FileUtils.exists(checkFile)) {
154: levelFile = DEBUG;
155: try {
156: FileUtils.delete(checkFile);
157: } catch (Exception e) {
158: // the file may be read only
159: }
160: }
161: }
162: }
163:
164: private synchronized void writeFile(String s, Throwable t) {
165: try {
166: if (checkSize++ >= CHECK_SIZE_EACH_WRITES) {
167: checkSize = 0;
168: closeWriter();
169: if (maxFileSize > 0
170: && FileUtils.length(fileName) > maxFileSize) {
171: String old = fileName + ".old";
172: if (FileUtils.exists(old)) {
173: FileUtils.delete(old);
174: }
175: FileUtils.rename(fileName, old);
176: }
177: }
178: if (!openWriter()) {
179: return;
180: }
181: printWriter.println(s);
182: if (t != null) {
183: t.printStackTrace(printWriter);
184: }
185: printWriter.flush();
186: if (closed) {
187: closeWriter();
188: }
189: } catch (Exception e) {
190: logWritingError(e);
191: }
192: }
193:
194: private void logWritingError(Exception e) {
195: if (writingErrorLogged) {
196: return;
197: }
198: writingErrorLogged = true;
199: // TODO translate trace messages
200: SQLException se = Message.getSQLException(
201: ErrorCode.TRACE_FILE_ERROR_2, new String[] { fileName,
202: e.toString() }, e);
203: // print this error only once
204: fileName = null;
205: System.out.println(se);
206: se.printStackTrace();
207: }
208:
209: private boolean openWriter() {
210: if (printWriter == null) {
211: try {
212: FileUtils.createDirs(fileName);
213: if (FileUtils.exists(fileName)
214: && FileUtils.isReadOnly(fileName)) {
215: // read only database: don't log error if the trace file
216: // can't be opened
217: return false;
218: }
219: fileWriter = FileUtils.openFileWriter(fileName, true);
220: printWriter = new PrintWriter(fileWriter, true);
221: } catch (Exception e) {
222: logWritingError(e);
223: return false;
224: }
225: }
226: return true;
227: }
228:
229: private synchronized void closeWriter() {
230: if (printWriter != null) {
231: printWriter.flush();
232: printWriter.close();
233: printWriter = null;
234: }
235: if (fileWriter != null) {
236: try {
237: fileWriter.close();
238: } catch (IOException e) {
239: // ignore
240: }
241: fileWriter = null;
242: }
243: try {
244: if (fileName != null && FileUtils.length(fileName) == 0) {
245: FileUtils.delete(fileName);
246: }
247: } catch (SQLException e) {
248: // ignore
249: }
250: }
251:
252: public void close() {
253: closeWriter();
254: closed = true;
255: }
256:
257: protected void finalize() {
258: if (!SysProperties.runFinalize) {
259: return;
260: }
261: close();
262: }
263:
264: }
|