package org.intrace.output.trace;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import org.intrace.output.AgentHelper;
import org.intrace.output.IInstrumentationHandler;
/**
* Implements Standard Output Tracing
*/
public class TraceHandler implements IInstrumentationHandler
{
/**
* Including the period at the end enables code in "org.intracetest" to be included in the trace
*/
private static final String INTRACE_PACKAGE = "org.intrace.";
private static final String THREAD = "java.lang.Thread";
private static final String GET_STACK_TRACE = "getStackTrace";
public static final TraceHandler INSTANCE = new TraceHandler();
private TraceHandler()
{
// Private constructor
}
private boolean entryExitTrace = true;
private boolean branchTrace = false;
private boolean argTrace = true;
private boolean truncateArrays = true;
private boolean exitStackTrace = false;
private static final TraceSettings traceSettings = new TraceSettings("");
@Override
public String getResponse(String args)
{
// TraceSettings oldSettings = new TraceSettings(traceSettings);
traceSettings.parseArgs(args);
// if ((oldSettings.isEntryExitTraceEnabled() != traceSettings
// .isEntryExitTraceEnabled())
// || (oldSettings.isBranchTraceEnabled() != traceSettings
// .isBranchTraceEnabled())
// || (oldSettings.isArgTraceEnabled() != traceSettings
// .isArgTraceEnabled())
// || (oldSettings.isTruncateArraysEnabled() != traceSettings
// .isTruncateArraysEnabled()))
// {
//// System.out.println("## Trace Settings Changed");
// }
entryExitTrace = traceSettings.isEntryExitTraceEnabled();
branchTrace = traceSettings.isBranchTraceEnabled();
argTrace = traceSettings.isArgTraceEnabled();
truncateArrays = traceSettings.isTruncateArraysEnabled();
exitStackTrace = traceSettings.isExitStackTraceEnabled();
return null;
}
@Override
public Map<String, String> getSettingsMap()
{
return traceSettings.getSettingsMap();
}
private String getArrayLenStr(Object array)
{
String lRet = "";
if (array != null)
{
lRet = "Len:" + Array.getLength(array) + " ";
}
return lRet;
}
private String arrayStr(String xiArrStr)
{
String ret = xiArrStr;
if (truncateArrays &&
xiArrStr.length() > 100)
{
ret = xiArrStr.substring(0, 100) + "...";
}
return ret;
}
@Override
public void val(String desc, String className, String methodName, byte byteArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ byteArg);
}
}
@Override
public void val(String desc, String className, String methodName,
byte[] byteArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(byteArrayArg) + arrayStr(Arrays.toString(byteArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName,
short shortArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ shortArg);
}
}
@Override
public void val(String desc, String className, String methodName,
short[] shortArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(shortArrayArg) + arrayStr(Arrays.toString(shortArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName, int intArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ intArg);
}
}
@Override
public void val(String desc, String className, String methodName,
int[] intArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(intArrayArg) + arrayStr(Arrays.toString(intArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName, long longArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ longArg);
}
}
@Override
public void val(String desc, String className, String methodName,
long[] longArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(longArrayArg) + arrayStr(Arrays.toString(longArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName,
float floatArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ floatArg);
}
}
@Override
public void val(String desc, String className, String methodName,
float[] floatArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(floatArrayArg) + arrayStr(Arrays.toString(floatArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName,
double doubleArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ doubleArg);
}
}
@Override
public void val(String desc, String className, String methodName,
double[] doubleArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(doubleArrayArg) + arrayStr(Arrays.toString(doubleArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName,
boolean boolArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ boolArg);
}
}
@Override
public void val(String desc, String className, String methodName,
boolean[] boolArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(boolArrayArg) + arrayStr(Arrays.toString(boolArrayArg)));
}
}
@Override
public void val(String desc, String className, String methodName, char charArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ charArg);
}
}
@Override
public void val(String desc, String className, String methodName,
char[] charArrayArg)
{
if (argTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(charArrayArg) + arrayStr(Arrays.toString(charArrayArg)));
}
}
private static final char ESCAPE_REPLACEMENT = '\u25A1';
/**
* If user has requested to see a stack trace for each 'exit' event (with the parameter [exit-stack-trace-true) , then
* this delimiter will follow the regular event text, which will be followed by the text of the stack trace.
*/
private static final String STACK_TRACE_DELIM = "~";
/**
* Just like Arrays.toString(Object), place a comma between each element of the stack trace
*/
private static final Object STACK_ELE_DELIM = ",";
private String replaceChars(String xiArg)
{
String ret = xiArg;
StringBuilder str = null;
for (int ii = 0; ii < xiArg.length(); ii++)
{
char c = xiArg.charAt(ii);
// Detect special char
if ((0x00 <= c) &&
(c <= 0x20) &&
(c != '\r') &&
(c != '\n'))
{
// Replace char
c = ESCAPE_REPLACEMENT;
// Setup stringbuilder
if (str == null)
{
str = new StringBuilder();
// Append any previous non special chars
if (ii > 0)
{
str.append(xiArg.substring(0, ii));
}
}
}
// If we are storing chars we better write
// this one now
if (str != null)
{
str.append(c);
}
}
if (str != null)
{
ret = str.toString();
}
return ret;
}
@Override
public void val(String desc, String className, String methodName,
Object objArg)
{
if (argTrace)
{
String objStr;
if ((objArg != null) && objArg.getClass().isArray())
{
// Array return values pass through this arm so we must do something
// a bit special - use Arrays.deepToString and discard the surrounding
// [] that we add.
objStr = Arrays.deepToString(new Object[] { objArg });
objStr = objStr.substring(1, objStr.length() - 1);
objStr = arrayStr(objStr);
objStr = replaceChars(objStr);
}
else
{
objStr = (objArg != null ? objArg.toString() : "null");
}
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ objStr);
}
}
@Override
public void val(String desc, String className, String methodName,
Object[] objArrayArg)
{
if (argTrace)
{
String objStr = Arrays.deepToString(objArrayArg);
objStr = replaceChars(objStr);
objStr = arrayStr(objStr);
writeTraceOutput(className + ":" + methodName + ": " + desc + ": "
+ getArrayLenStr(objArrayArg) + objStr);
}
}
@Override
public void branch(String className, String methodName, int lineNo)
{
if (branchTrace)
{
writeTraceOutput(className + ":" + methodName + ": /" +
(lineNo >= 0 ? ":" + lineNo : ""));
}
}
@Override
public void val(String desc, String className, String methodName, int lineNo,
Throwable throwable)
{
if (branchTrace)
{
writeTraceOutput(className + ":" + methodName + ": " + desc + ":"
+ lineNo + ": " + throwableToString(throwable));
}
}
private String throwableToString(Throwable throwable)
{
StringBuilder throwToStr = new StringBuilder();
if (throwable == null)
{
throwToStr.append("null");
}
else
{
StringWriter strWriter = new StringWriter();
PrintWriter writer = new PrintWriter(strWriter);
throwable.printStackTrace(writer);
throwToStr.append(strWriter.toString());
}
return throwToStr.toString();
}
@Override
public void enter(String className, String methodName, int lineNo)
{
if (entryExitTrace)
{
writeTraceOutput(className + ":" + methodName + ": {" +
(lineNo >= 0 ? ":" + lineNo : ""));
}
}
/**
* Remove all "org.intrace" elements from the current stack trace and return it as string.
* @return
*/
public String getStackTrace() {
StringBuilder sb = new StringBuilder();
int counter = 0;
for(StackTraceElement ste : Thread.currentThread().getStackTrace() ) {
boolean disqualify = false;
if (ste != null) {
if ( (ste.getClassName() !=null) &&
( (ste.getClassName().indexOf(INTRACE_PACKAGE) >= 0)
|| (ste.getClassName().indexOf(THREAD) >= 0) )
)
disqualify = true;
if (ste.getMethodName()!=null
&& ste.getMethodName().indexOf(GET_STACK_TRACE) >=0)
disqualify = true;
}
if ( !disqualify) {
if (counter++>0) sb.append(STACK_ELE_DELIM); //Just like Arrays.toString(), place a comma between each stack trace ele.
sb.append(ste.toString());
}
}
return sb.toString();
}
@Override
public void exit(String className, String methodName, int lineNo)
{
if (entryExitTrace)
{
if (exitStackTrace) {
writeTraceOutput(className + ":" + methodName + ": }" +
(lineNo >= 0 ? ":" + lineNo : "") +
STACK_TRACE_DELIM + getStackTrace() );
} else {
writeTraceOutput(className + ":" + methodName + ": }" +
(lineNo >= 0 ? ":" + lineNo : "") );
}
}
}
/**
* Write output to zero or more of the following.
* <ul>
* <li>StdOut
* <li>FileOut
* <li>NetworkOut
* </ul>
*
* @param xiOutput
*/
public void writeTraceOutput(String xiOutput)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
long threadID = Thread.currentThread().getId();
String traceString = "[" + dateFormat.format(new Date()) + "]:[" + threadID
+ "]:" + xiOutput;
// if (AgentHelper.getOutputSettings().isStdoutOutputEnabled())
// {
//// System.out.println(traceString);
// }
if (AgentHelper.getOutputSettings().isFileOutputEnabled())
{
AgentHelper.getOutputSettings().writeFileOutput(traceString);
}
if (AgentHelper.getOutputSettings().isNetOutputEnabled())
{
AgentHelper.writeDataOutput(traceString);
}
}
}