summaryrefslogtreecommitdiffstats
path: root/pki/base/common/src/com/netscape/cmscore/util/Debug.java
blob: 417f3159be4fd4ffe5714f5fe39c4b60291fe454 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.cmscore.util;


import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.StringTokenizer;

import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.ISubsystem;
import com.netscape.cmsutil.util.Utils;


public class Debug
    implements ISubsystem {

    private static Debug mInstance = new Debug();
    private static boolean mShowCaller = false;


	/* This dateformatter is used to put the date on each
	   debug line. But the DateFormatter is not thread safe,
	   so I create a thread-local DateFormatter for each thread
	 */
    private static String DATE_PATTERN = "dd/MMM/yyyy:HH:mm:ss";
    private static ThreadLocal mFormatObject = new ThreadLocal() {
			protected synchronized Object initialValue() {
				return new SimpleDateFormat(DATE_PATTERN);
			}
	}; 

	/* the dateformatter should be accessed with this function */
	private static SimpleDateFormat getDateFormatter() {
		return ((SimpleDateFormat)(mFormatObject.get()));
	}

    public static final boolean ON = false;
    public static final int OBNOXIOUS = 10;
    public static final int VERBOSE = 5;
    public static final int INFORM = 1;

    // the difference between this and 'ON' is that this is always
    // guaranteed to log to 'mOut', whereas other parts of the server
    // may do:
    //  if (Debug.ON) {
    //     System.out.println("..");
    //	}
    // I want to make sure that any Debug.trace() is not logged to 
    // System.out if the server is running under watchdog

    private static boolean TRACE_ON = false;

    private static int mDebugLevel = VERBOSE;

    private static PrintStream mOut = null;
	private static Hashtable mHK = null;

    static {
        if (TRACE_ON == true) {
            mOut = System.out;
        }
    }

    public static void trace(int level, String t) {
        trace(level, t, null, true);
    }

    /**
     * Output a debug message at the output stream sepcified in the init()
     * method. This method is very lightweight if debugging is turned off, since
     *   it will return immediately. However, the caller should be aware that
     *   if the argument to Debug.trace() is an object whose toString() is
     *   expensive, that this toString() will still be called in any case.
     *   In such a case, it is wise to wrap the Debug.trace like this: <pre>
     *   if (Debug.on()) { Debug.trace("obj is: "+obj); }
     *   </pre>
     * @param level the message level. If this is >= than the currently set
     *    level (set with setLevel() ), the message is printed
     * @param t the message to print
     * @param ignoreStack when walking the stack to determine the
     *   location of the method that called the trace() method,
     *   ignore any classes with this string in. Can be null
     * @param printCaller if true, (and if static mShowCaller is true)
     *    dump caller information in this format:
     *    (source-file:line) methodname():
     */
    public static void trace(int level, String t, String ignoreStack, boolean printCaller) {
		String callerinfo = "";
        if (!TRACE_ON) return;
        if (level >= mDebugLevel) {
            if (mShowCaller && printCaller) {
                String method = "";
                String fileAndLine = "";

                try {
					Throwable tr = new Throwable();
					StackTraceElement ste[] = tr.getStackTrace();
					int i=0;
					while ((i < ste.length) && 
						(ste[i].getMethodName().toLowerCase().indexOf("debug") >-1) ||
						(ste[i].getMethodName().toLowerCase().indexOf("hashkey") >-1) ||
						(ste[i].getClassName().toLowerCase().indexOf("propconfigstore") >-1) ||
						(ste[i].getClassName().toLowerCase().indexOf("argblock") >-1) ||
						(ste[i].getClassName().toLowerCase().indexOf("debug") >-1) ||
						(ste[i].getMethodName().toLowerCase().indexOf("trace") >-1)) i++;

					if (i < ste.length) {
						fileAndLine = ste[i].getFileName()+":"+
							ste[i].getLineNumber();
						method = ste[i].getMethodName()+"()";
					}

					callerinfo = fileAndLine +":"+ method + " ";
                } catch (Exception f) {
                }
            }
		
			outputTraceMessage(callerinfo + t);
        }
    }
	
	private static void outputTraceMessage(String t)
	{
        if (!TRACE_ON) return;
		SimpleDateFormat d = getDateFormatter();
        if (mOut != null && d != null) {
            mOut.println("[" + d.format(new Date()) + "][" + Thread.currentThread().getName() + "]: " + t);
            mOut.flush();
            }
	}

	private static boolean hkdotype(String type)
	{
		if (mHK!= null && mHK.get(type) != null) {
			return true;
		} else {
			return false;
		}
	}

    public static void traceHashKey(String type, String key) {
		if (hkdotype(type)) {
           	trace("GET r=" + type+ ",k=" + key);
        }
    }

    public static void traceHashKey(String type, String key, String val) {
		if (hkdotype(type)) {
            trace("GET r=" + type+ ",k=" + key + ",v=" + val);
        }
    }

    public static void traceHashKey(String type, String key, String val, String def) {
		if (hkdotype(type)) {
            trace("GET r=" + type+ ",k=" +
					 key + ",v=" + val +",d="+def);
        }
	}

    public static void putHashKey(String type, String key, String value) {
		if (hkdotype(type)) {
            outputTraceMessage("PUT r=" + type+ ",k=" + key + ",v=" + value);
        }
    }

    public static void trace(String t) {
        trace(VERBOSE, t);
    }

    public static void print(int level, String t) {
        if (!TRACE_ON) return;
        if (mOut != null) {
            if (level >= mDebugLevel)
                mOut.print(t);
        }
    }

    public static void print(String t) {
        print(VERBOSE, t);
    }

    private static void printNybble(byte b) {
        if (mOut == null) return;
        if (b < 10) mOut.write('0' + b);
        else mOut.write('a' + b - 10);
    }

    /**
     * If tracing enabled, dump a byte array to debugging printstream
     * as hex, colon-seperated bytes, 16 bytes to a line
     */
    public static void print(byte[] b) {
        if (!TRACE_ON) return;
        if (mOut == null) return;

        for (int i = 0; i < b.length; i++) {
            printNybble((byte) ((b[i] & 0xf0) >> 4));
            printNybble((byte) (b[i] & 0x0f));
            mOut.print(" ");
            if (((i % 16) == 15) && i != b.length) mOut.println("");
        }
        mOut.println("");
        mOut.flush();
    }

    /**
     * Print the current stack trace to the debug printstream
     */
    public static void printStackTrace() {
        if (!TRACE_ON) return;
        Exception e = new Exception("Debug");

        printStackTrace(e);
    }

    /**
     * Print the stack trace of the named exception 
     * to the debug printstream
     */
    public static void printStackTrace(Throwable e) {
        if (!TRACE_ON) return;
        if (mOut == null) return;

        e.printStackTrace(mOut);
    }

    /**
     * Set the current debugging level. You can use: <pre>
     * OBNOXIOUS = 10
     * VERBOSE   = 5
     * INFORM    = 1
     * </pre> Or another value
     */

    public static void setLevel(int level) {
        mDebugLevel = level;
    }

    public static int getLevel(int level) {
        return mDebugLevel;
    }

    /**
     * Test if debugging is on. Do NOT write to System.out in your debug code
     */
    public static boolean on() { 
        return TRACE_ON;
    }

    /*  ISubsystem methods: */

    public static String ID = "debug";
    private static IConfigStore mConfig = null;
	
    public String getId() {
        return ID;
    }

    public void setId(String id) {
        ID = id;
    }

    private static final String PROP_ENABLED = "enabled";
    private static final String PROP_FILENAME = "filename";
    private static final String PROP_HASHKEYS = "hashkeytypes";
    private static final String PROP_SHOWCALLER = "showcaller";
    private static final String PROP_LEVEL = "level";
    private static final String PROP_APPEND = "append";

    /**
     * Debug subsystem initialization. This subsystem is usually
     * given the following parameters: <pre>
     * debug.enabled   : (true|false) default false
     * debug.filename  : can be a pathname, or STDOUT
     * debug.hashkeytypes: comma-separated list of hashkey types
     *    possible values:  "CS.cfg"
     * debug.showcaller: (true|false) default false  [show caller method name for Debug.trace()]
     * </pre>
     */
    public void init(ISubsystem owner, IConfigStore config) {
        mConfig = config;
        String filename = null;
        String hashkeytypes = null;
		boolean append=true;

        try {
            TRACE_ON = mConfig.getBoolean(PROP_ENABLED, false);
            if (TRACE_ON) {
                filename = mConfig.getString(PROP_FILENAME, null);
                if (filename == null) {
                    TRACE_ON = false;
                }
                hashkeytypes = mConfig.getString(PROP_HASHKEYS, null);
                mShowCaller = mConfig.getBoolean(PROP_SHOWCALLER, false);
                append = mConfig.getBoolean(PROP_APPEND, true);
            }
            if (TRACE_ON) {
                if (filename.equals("STDOUT")) {
                    mOut = System.out;
                } else {
                    if( !Utils.isNT() ) {
                        // Always insure that a physical file exists!
                        Utils.exec( "touch " + filename );
                        Utils.exec( "chmod 00640 " + filename );
                    }
                    OutputStream os = new FileOutputStream(filename, append);
                    mOut = new PrintStream(os, true);  /* true == autoflush */
                }
                if (hashkeytypes != null) {
					StringTokenizer st = new StringTokenizer(hashkeytypes,
							",", false);
					mHK = new Hashtable();
					while (st.hasMoreElements()) {
						String hkr = st.nextToken();
						mHK.put(hkr, "true");
					}
                }
            }
			outputTraceMessage("============================================");
			outputTraceMessage("=====  DEBUG SUBSYSTEM INITIALIZED   =======");
			outputTraceMessage("============================================");
            int level = mConfig.getInteger(PROP_LEVEL, VERBOSE);
            setLevel(level);
        } catch (Exception e) {
            // Don't do anything. Logging is not set up yet, and
            // we can't write to STDOUT. 
        }
    }

    public void startup() {
    }

    public void shutdown() {
    }

    public IConfigStore getConfigStore() {
        return mConfig;
    }

    // for singleton

    public static Debug getInstance() {
        return mInstance;
    }

}