Skip to Content

Purpose

  • A mechanism which helps to identify flow and erroneous code.
  • Traceability of the entire code base to re-engineer any execution path.
  • Identify threats in design from the trace.

Problem Statement

To achieve the purpose, following are design problems.

  • Provision of sharing a singular instance of Log Class across all Mutually Exclusive instances.
  • Provide a solution for unique instance per singular instance of Log class per execution thread.
  • Prepare an XML based Trace provisioning of the entire execution path.

Proposed Solution

Artifact 1. Provision of sharing a singular instance of Log Class across all Mutually Exclusive instances

Solution: Singleton design pattern can provide singular instance of a class across the entire execution per process.

Artifact 2. Provide a solution for unique instance per singular instance of Log class per execution thread

Solution: A thread specific reentrant mechanism is excogitated to fetch singular instance of the Log class for a specific thread. Thus, each concurrent thread under execution will consume unique instance of the singular (Singleton) class –  Hack the Singleton.

Artifact 3. Prepare an XML based Trace provisioning of the entire execution path per thread

Solution: All the entries and exits along with errors, warnings and exception etc. will be logged in hierarchical structure per execution per thread.

Design Objectives

  1. Prepare a singleton class that serves as a singular interface for trace mechanism for all the ME classes – Logger Class.
  2. Provide single wrapper for all Tracing activities, one method to set the trace level and one method to get the Logger class instance.
  3. Multiple threads are invoked for the entire execution and hence traditional Singleton Design won’t suffice the design problem (Artifact 2) at hand. Thread specific Singularity is achieved for the logger class using “ThreadClass”
  4. Tree structure is leveraged out of the entire execution path using XML concept.

Concept Diagrams

 

Design Challenge

Given a code scenario where code flow is sequential rather than hierarchical which involves many independent classes and each class is ME; introducing Trace mechanism into it will generally indicate Singleton Design pattern for the Logger class. But what if the execution of the code happens in a multithreaded environment which is Asynchronous.

Now the problem statement completely changes from “Singular instance per execution process for all the class instances to Singular instance per execution per thread for all the class instances”.

To tackle this design challenge, it is required to hack the singleton design pattern such that per threaded execution singleton class releases one instance (of course when requested) per thread and not for the entire process and make it believe that the subsequent requested instances are from a different process.

In this way, 2 problems are tackled – 1. Only a single instance of the Logger class is available across all the execution classes involved and this helps to avoid passing the Logger object along with each method call (not an ideal code design) and 2. when a different thread request for Logger singleton instance it does not return the same instance that is already in use (otherwise the data corruption takes place because asynchronous thread will over write the Logger instance containing information of another parallel thread).

Singleton – The Normal

The usual implementation of a singleton class is:

class SingletonClass
{
	private static volatile SingletonClass sInstance;
	//private constructor.
	private SingletonClass()
	{
		//Prevent hack by reflection
		if (sInstance != null)
		{
			throw new RuntimeException("Use createInstance() method to get the single instance of this class.");
		}
	}
	public static SingletonClass createInstance()
	{
		//Double locking mechanism to prevent race condition
		if (sInstance == null)
		{
			//Check for the first time
			synchronized (SingletonClass.class)
			{
				//Check for the second time to prevent race condition.
				//if there is no instance available... create a new one
				if (sInstance == null)
					sInstance = new SingletonClass();
			}
		}
		return sInstance;
	}
}

But this implementation only helps us address “Artifact 1. Provision of sharing a singular instance of Log Class across all Mutually Exclusive instances”.

But the solution to “Artifact 2. Provide a solution for unique instance per singular instance of Log class per execution thread” needs a tweaking in the singleton class by which it not only retains Singleton property but also serves the purpose of thread specific Singleton. The below mentioned code helps achieve this purpose.

Singleton version 2 – The Asynchronous new

The new implementation of a singleton class for Asynchronous multi threaded scenario to tackle Artifact 2.

class SingletonClass
{
	//private static volatile SingletonClass sInstance;
	//private constructor.
	private SingletonClass()
	{
		//Prevent hack by reflection
		if (sInstance != null)
		{
			throw new RuntimeException("Use createInstance() method to get the single instance of this class.");
		}
	}
	public static SingletonClass createInstance()
	{
		return sInstance.get();
	}
	private static final ThreadLocal<SingletonClass> sInstance = new ThreadLocal<SingletonClass>()
	{
		@Override
		protected SingletonClass initialValue()
		{
			return new SingletonClass();
		}
	};
}

Implementation of Logger class using Singleton version 2

     1. Logger.java: This class provides a singular instance of the Logger class per thread.

          Exposed methods:

          a.

public static synchronized Logger createLoggerInstance()

         This method gets an instance (singular) of the Logger class per thread.

          Example code:

private Logger logObject = Logger.createLoggerInstance();

           b.

public void setTraceLevel(int iLevel)

          This method sets the level of tracing required to get clarity on execution flow. 4 levels are currently being exposed viz. 1, 2 and 3. 0 to turn off tracing.

           Example code:

logObject.setTraceLevel(3);

           c.

public void traceLog(int uTraceMethod, String sBufferArg1, Object... oVal)

           This single method helps introducing trace in between the code to be put under the scanner.

           Example code:

String ClassName = this.getClass().getSimpleName();
String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
logObject.traceLog(Logger.TRACEINFO, MethodName,"num1", num1, "num2", num2);
logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);

           d.

public String getTraceBuffer()

          This method helps to get the buffer which would reflect the entire flow of code execution.This should be requested just before the point where the execution of code ends.

          Example code:

System.out.println(logObject.getTraceBuffer()); //Print the stream Buffer to post back to DB

     2. Calculate.java: This is just a dummy class to demonstrate the usage of Logger class.

     3. Action.java is again a dummy code which is ME class but Calculate.java uses this to carry out the actual calculation. The methods in this class is again invoked using an asynchronous mechanism.

     4. ThreadClass.java is a dummy class which is used to invoke an object of Calculate.java on different methods exposed by that class across multiple threads.

Code Snippet:

ThreadClass.java

import mathOperation.Calculate;

public class ThreadClass extends Thread
{
	private int inputParam1;
	private int inputParam2;
	private String threadName;
	private int operationName;
	private static final int ADD = Calculate.ADD; 
	private static final int SUBTRACT = Calculate.SUBTRACT;
	private static final int DIVIDE = Calculate.DIVIDE;
	private static final int MULTIPLY = Calculate.MULTIPLY;
 
    public ThreadClass(int iArg1,int iArg2,int iOperation, String sThread)
    {
    	System.out.println("CTOR ThreadClass");
    	
    	inputParam1 = iArg1;
    	inputParam2 = iArg2;
        threadName = sThread;
        operationName = iOperation;
    }
    
    @Override
    public void run()
    {
        try
        {
        	System.out.println("Entering Thread "+ threadName);
        	Calculate calcObj = new Calculate();
        	int result = calcObj.operation(inputParam1, inputParam2, operationName);
        	System.out.println("Exiting Thread " + threadName );
        	System.out.println(threadName + ": Result = " + result);
    	}
        catch(Exception e)
        {
        	System.out.println("Thread interrupted " + threadName);
        }
    }	
	
	public static void main(String [] arg)
	{
		try
		{
			ThreadClass T1 = new ThreadClass(1, 2, ADD, "Thread0");
		    T1.start();
			ThreadClass T2 = new ThreadClass(4, 3, SUBTRACT, "Thread1");
		    T2.start();
			ThreadClass T3 = new ThreadClass(5, 6, MULTIPLY, "Thread2");
		    T3.start();
			ThreadClass T4 = new ThreadClass(56, 0, DIVIDE, "Thread3");
		    T4.start();
		}
		catch(Exception e)
		{
			
		}
	}
}

Calculate.java

package mathOperation;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import logPackage.*;
import mathOperation.Action;

public class Calculate 
{
	private Logger logObject = Logger.createLoggerInstance();
	public static final int ADD = 0; 
	public static final int SUBTRACT = 1;
	public static final int DIVIDE = 2;
	public static final int MULTIPLY = 3;
	
	public Calculate()
	{
		System.out.println("CTOR Consumer Class");
	}
	
	private void startLog()
	{
		logObject.setTraceLevel(3);
	}
	
	public int operation(int num1, int num2, int iOperation) throws InterruptedException, ExecutionException
	{
		startLog();
		String ClassName = this.getClass().getSimpleName();
		String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
		logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
		logObject.traceLog(Logger.TRACEINFO, MethodName,"num1", num1, "num2", num2);
		ExecutorService es = Executors.newSingleThreadExecutor();
		Future<Integer> fAction = es.submit(new Action(num1, num2, iOperation));
		int iResult = 0;
		switch(iOperation)
		{
			case ADD:
				iResult = fAction.get();
				break;
			case SUBTRACT:
				iResult = fAction.get();
				break;
			case DIVIDE:
				iResult = fAction.get();
				break;
			case MULTIPLY:
				iResult = fAction.get();
				break;	
		}
		logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
		System.out.println(logObject.getTraceBuffer()); //Print the stream Buffer to post back to DB
		return iResult;
	}
}

Action.java

package mathOperation;

import java.util.concurrent.Callable;
import logPackage.*;

public class Action implements Callable<Integer>
{
	private Logger logObject = Logger.createLoggerInstance();
	private int number1;
	private int number2;
	private int iActionType;
	
	public Action(int iArg1, int iArg2, int iAction)
	{
		number1 = iArg1;
		number2 = iArg2;
		iActionType = iAction;
	}
	
	@Override
    public Integer call()
    {
		switch(iActionType)
		{
			case 0:
				return add(number1, number2);
			case 1:
				return subtract(number1, number2);
			case 2:
				return divide(number1, number2);
			case 3:
				return multiply(number1, number2);
			default:
				return -1;
		}
    }
	
	private int add(int num1, int num2)
	{
		String ClassName = this.getClass().getSimpleName();
		String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
		logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
		logObject.traceLog(Logger.TRACEINFO, MethodName, "Result", (num1 + num2));
		logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
		return (num1 + num2);
	}
	
	private int subtract(int num1, int num2)
	{
		String ClassName = this.getClass().getSimpleName();
		String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
		logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
		logObject.traceLog(Logger.TRACEINFO, MethodName, "Result", (num1 - num2));
		logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
		return (num1 - num2);
	}
	
	private int divide(int num1, int num2)
	{
		String ClassName = this.getClass().getSimpleName();
		String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
		logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
		if(num2 != 0)
		{
			logObject.traceLog(Logger.TRACEINFO, MethodName, "Result", num1 / num2 );
			logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
			return (num1 / num2);
		}
		else
		{
			logObject.traceLog(Logger.TRACEERROR, MethodName, "Divided by ZERO error, num2 (Denominator)", num2);
			logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
			return 0;
		}
	}
	
	private int multiply(int num1, int num2)
	{
		String ClassName = this.getClass().getSimpleName();
		String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
		logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
		logObject.traceLog(Logger.TRACEINFO, MethodName, "Result", (num1 * num2));
		logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
		return (num1 * num2);
	}
}

Logger.java

// File: Logger.java
package logPackage;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import java.text.MessageFormat;
import java.util.Stack;

/**
* <br><b> This Logger class is used to generate trace information</b>
* <br><br><b> Exposed methods : </b>
* 
* <br> 1. Logger logPackage.Logger.<b>createLoggerInstance()</b>
* <br> 2. void logPackage.Logger.<b> setTraceLevel(int iLevel)</b>
* <br> 3. void logPackage.Logger.<b>traceLog(int uTraceMethod, String sBufferArg1, Object... oVal)</b>
* <br> 4. Exposed static constants to be used along with traceLog to be passed for <b>uTraceMethod</b> parameter:
* <br><b> TRACEENTER
* <br> TRACELEAVE
* <br> TRACEINFO
* <br> TRACEERROR
* <br> TRACEDEBUG
* <br> TRACEWARNING</b>
* <br> 5. String logPackage.Logger.<b>getTraceBuffer()</b>
* 
* @author Gaurav Mazumdar
* @see <a href=https://blogs.sap.com/2018/03/21/asynchronous-singularity-for-mutual-exclusion/>SAP BLOG</a>
*/
public class Logger
{
	private StringBuffer sBuffer;
	private Stack<String> sStoreClassMethod = null;
	/**
	* <br><b> TRACEENTER:</b>
	* <br> Type: static final int
	* <br> To be used along with traceLog method for ENTER trace in methods. 
	*/
	public static final int TRACEENTER = 0;
	/**
	* <br><b> TRACELEAVE:</b> 
	* <br> Type: static final int
	* <br> To be used along with traceLog method for LEAVE trace in methods. 
	*/
	public static final int TRACELEAVE = 1;
	/**
	* <br><b> TRACEINFO:</b> 
	* <br> Type: static final int
	* <br> To be used along with traceLog method for INFO trace in methods. 
	*/
	public static final int TRACEINFO = 2;
	/**
	* <br><b> TRACEERROR:</b> 
	* <br> Type: static final int
	* <br> To be used along with traceLog method for ERROR trace in methods. 
	*/
	public static final int TRACEERROR = 3;
	/**
	* <br><b> TRACEDEBUG:</b>
	* <br> Type: static final int 
	* <br> To be used along with traceLog method for DEBUG trace in methods. 
	*/
	public static final int TRACEDEBUG = 4;
	/**
	* <br><b> TRACEWARNING:</b> 
	* <br> Type: static final int
	* <br> To be used along with traceLog method for WARNING trace in methods. 
	*/
	public static final int TRACEWARNING = 5;
	private enum traceLevel
	{
		LEVEL0,
		LEVEL1,
		LEVEL2,
		LEVEL3;
	}
	private traceLevel eLevel;
	
	/*
	* Logger(): No-argument constructor initializes instance variables. Set to private for singleton Logger class
	*/
	private Logger()
	{
		/*System.out.println("CTOR of Logger Class");*/
		sStoreClassMethod = new Stack<String>();
		StringBuffer sBufferedStream = new StringBuffer("<DEVLOG>") ;
		sBuffer = sBufferedStream;
	}
	
	/*
	* ThreadLocal<Logger>: Thread Local concept used for singleton Logger class so that Logger class returns one instance per thread
	*/
	private static final ThreadLocal<Logger> m_LogInstance = new ThreadLocal<Logger>()
    {
        @Override
        protected Logger initialValue()
        {
        	/*System.out.println("Thread specific singular instance: " + "HashCode= " + Thread.currentThread().hashCode()+ " " + Thread.currentThread().getName());*/
        	return new Logger();
        }
    };
	
	/**
	* <br><b> createLoggerInstance :</b> returns singleton instance for Logger class per execution per thread.
	* <br> Parameter : none 
	* <br> return type : Logger
	*/
	public static synchronized Logger createLoggerInstance()
	{
		return m_LogInstance.get();
	}
	
	/**
	* <br><b> setTraceLevel :</b> set Trace Level.
	* <br> Parameter : int
	* <br><b> 0 : </b> NO Trace.
	* <br><b> 1 : </b> ENTER, LEAVE and INFO trace.
	* <br><b> 2 : </b> ERROR Trace plus LEVEL1 traces.
	* <br><b> 3 : </b> WARNING, DEBUG plus LEVEL2 traces.
	* <br> return type : void
	*/
	public void setTraceLevel(int iLevel)
	{
		switch(iLevel) 
		{
			case 0:
				eLevel = traceLevel.LEVEL0;
				break;
			case 1:
				eLevel = traceLevel.LEVEL1;
				break;
			case 2:
				eLevel = traceLevel.LEVEL2;
				break;
			case 3:
				eLevel = traceLevel.LEVEL3;
				break;
			default:
				eLevel = traceLevel.LEVEL0;
				break;
		}
	}
	
	/*
	* getTraceLevel: returns trace level set by external source using setTraceLevel().
	*/
	private traceLevel getTraceLevel()
	{
		return eLevel;
	}

	/*
	* traceEnter: returns string buffer with class name and method ENTERED.
	*/
	private StringBuffer traceEnter(String sBufferArg1, Object sBufferArg2 )
	{
		StringBuffer sTempBuffer = new StringBuffer(MessageFormat.format("<ENTERING CLASSNAME=\"{0}\" METHODNAME=\"{1}\">", sBufferArg1, sBufferArg2));
		return sTempBuffer;
	}
	
	/*
	* traceLeave: returns string buffer with closing XML tag when EXITING a method.
	*/
	private StringBuffer traceLeave()
	{
		StringBuffer sTempBuffer = new StringBuffer("</ENTERING>");
		return sTempBuffer;
	}
	
	/*
	* traceInfo: returns string buffer with INFO details inside the methods.
	*/
	private <T> StringBuffer traceInfo(String sBufferArg1, Object sBufferArg2, T tValue )
	{
		T element = tValue;
		StringBuffer sTempBuffer = new StringBuffer(MessageFormat.format("<INFO METHODNAME=\"{0}\" INFORMATION=\"{1}\" VALUE=\"{2}\"></INFO>", sBufferArg1, sBufferArg2, element));
		return sTempBuffer;
	}
	
	/*
	* traceError: returns string buffer with ERROR details inside the methods.
	*/
	private <T> StringBuffer traceError(String sBufferArg1, Object sBufferArg2, T tValue )
	{
		T element = tValue;
		StringBuffer sTempBuffer = new StringBuffer(MessageFormat.format("<ERROR METHODNAME=\"{0}\" ERRORINFO=\"{1}\" VALUE=\"{2}\"></ERROR>", sBufferArg1, sBufferArg2, element));
		return sTempBuffer;
	}
	
	/*
	* traceDebug: returns string buffer with DEBUG details inside the methods.
	*/
	private <T> StringBuffer traceDebug(String sBufferArg1, Object sBufferArg2, T tValue )
	{
		T element = tValue;
		StringBuffer sTempBuffer = new StringBuffer(MessageFormat.format("<DEBUG METHODNAME=\"{0}\" DEBUGINFO=\"{1}\" VALUE=\"{2}\"></DEBUG>", sBufferArg1, sBufferArg2, element));
		return sTempBuffer;
	}
	
	/*
	* traceWarning: returns string buffer with WARNING details inside the methods.
	*/
	private <T> StringBuffer traceWarning(String sBufferArg1, Object sBufferArg2 , T tValue )
	{
		T element = tValue;
		StringBuffer sTempBuffer = new StringBuffer(MessageFormat.format("<WARNING METHODNAME=\"{0}\" WARNINFO=\"{1}\" VALUE=\"{2}\"></WARNING>", sBufferArg1, sBufferArg2, element));
		return sTempBuffer;
	}
	
	/**
	* <br><b> traceLog :</b> This method to be used to set the trace lines inside methods.
	* <br> Parameter :
	* <br> <b>uTraceMethod :</b> int type. Denotes the trace methods (ENTER, LEAVE, INFO, ERROR, DEBUG, WARNING) user wants to invoke.
	* <br> <b>sBufferArg1 :</b> String type. Denotes the Class name or Method name.
	* <br> <b>oVal :</b> Object type. Denotes the Method name or variable name or variable value. 
	* <br> return type : void
	* <br> <b>Example :</b>
	* <br><b> For ENTER and LEAVE (visible only with Trace level 1,2 and 3):</b>
	* <br> String ClassName = this.getClass().getSimpleName();
	* <br> String MethodName = new Object(){}.getClass().getEnclosingMethod().getName();
	* <br> logObject.traceLog(Logger.TRACEENTER, ClassName, MethodName);
	* <br> logObject.traceLog(Logger.TRACELEAVE, ClassName, MethodName);
	* <br><b> For INFO (visible only with Trace level 1,2 and 3):</b>
	* <br> logObject.traceLog(Logger.TRACEINFO, MethodName,"VARIABLE NAME1", VARIABLEDATA, "VARIABLE NAME2", VARIABLEDATA2,...so on);
	* <br><b> For ERROR (visible only with Trace level 2 and 3):</b>
	* <br> logObject.traceLog(Logger.TRACEERROR, MethodName,"ERROR CUSTOM TEXT 1 or ERROR DESCRIPTION 1", ERRORCODE1, "ERROR CUSTOM TEXT 2 or ERROR DESCRIPTION 2", ERRORCODE2,...so on);
	* <br><b> For DEBUG (visible only with Trace level  3):</b>
	* <br> logObject.traceLog(Logger.TRACEDEBUG, MethodName,"DEBUG TEXT 1 or DEBUG VARIABLE NAME 1", DEBUG_VARIABLE_VALUE1, "DEBUG TEXT 2 or DEBUG VARIABLE NAME 2", DEBUG_VARIABLE_VALUE2,...so on);
	* <br><b> For WARNING (visible only with Trace level  3):</b>
	* <br> logObject.traceLog(Logger.TRACEWARNING, MethodName,"WARNING TEXT 1 or WARNING VARIABLE NAME 1", WARNING_VARIABLE_VALUE2, "WARNING TEXT 2 or WARNING VARIABLE NAME 2", WARNING_VARIABLE_VALUE2,...so on);
	*/
	public void traceLog(int uTraceMethod, String sBufferArg1, Object... oVal)
	{
		Object[] element = oVal;
		int elementLength = element.length;
		String tempString = null;
		try
		{
			traceLevel iCurrentTraceLevel = getTraceLevel();
			StringBuffer sBufferedStream = new StringBuffer();
			switch (iCurrentTraceLevel)
			{
			case LEVEL3:
				if(uTraceMethod == TRACEWARNING)
				{
					if(elementLength == 0)
						sBufferedStream = sBufferedStream.append(traceWarning(sBufferArg1,"Missing WarningInfo", "Missing Data"));
					else if (elementLength % 2 == 0 )
					{
						for(int i = 0; i < elementLength; i = i+2)
						{
							sBufferedStream = sBufferedStream.append(traceWarning(sBufferArg1, element[i], element[i+1]));
						}
					}
					else
					{
						for(int i = 0; i < elementLength; i++)
						{
							sBufferedStream = sBufferedStream.append(traceWarning(sBufferArg1, element[i], "Missing Data"));
						}
					}
				}
				if(uTraceMethod == TRACEDEBUG)
				{
					if(elementLength == 0)
						sBufferedStream = sBufferedStream.append(traceDebug(sBufferArg1,"Missing DebugInfo", "Missing Data"));
					else if (elementLength % 2 == 0 )
					{
						for(int i = 0; i < elementLength; i = i+2)
						{
							sBufferedStream = sBufferedStream.append(traceDebug(sBufferArg1, element[i], element[i+1]));
						}
					}
					else
					{
						for(int i = 0; i < elementLength; i++)
						{
							sBufferedStream = sBufferedStream.append(traceDebug(sBufferArg1, element[i], "Missing Data"));
						}
					}
				}
			case LEVEL2:
				if(uTraceMethod == TRACEERROR)
				{
					if(elementLength == 0)
						sBufferedStream = sBufferedStream.append(traceError(sBufferArg1,"Missing ErrorInfo", "Missing Data"));
					else if (elementLength % 2 == 0 )
					{
						for(int i = 0; i < elementLength; i = i+2)
						{
							sBufferedStream = sBufferedStream.append(traceError(sBufferArg1, element[i], element[i+1]));
						}
					}
					else
					{
						for(int i = 0; i < elementLength; i++)
						{
							sBufferedStream = sBufferedStream.append(traceError(sBufferArg1, element[i], "Missing Data"));
						}
					}
				}
			case LEVEL1:
				if(uTraceMethod == TRACEENTER)
				{
					if(elementLength == 0)
					{
						sBufferedStream = traceEnter(sBufferArg1,"Missing MethodName");
						sStoreClassMethod.push(sBufferArg1 + " " + "Missing MethodName");
					}
					else if (elementLength != 0 )
					{
						sBufferedStream = traceEnter(sBufferArg1, element[0]);
						sStoreClassMethod.push(sBufferArg1 + " " + element[0]);
					}			
				}
				if(uTraceMethod == TRACELEAVE)
				{
					sBufferedStream = traceLeave();
					if(elementLength != 0)
						tempString = MessageFormat.format("{0} {1}",sBufferArg1, element[0]);
					else
						tempString = MessageFormat.format("{0} {1}",sBufferArg1, "Missing MethodName");
				}
				if(uTraceMethod == TRACEINFO)
				{
					if(elementLength == 0)
						sBufferedStream = sBufferedStream.append(traceInfo(sBufferArg1,"Missing Information", "Missing Data"));
					else if (elementLength % 2 == 0 )
					{
						for(int i = 0; i < elementLength; i = i+2)
						{
							sBufferedStream = sBufferedStream.append(traceInfo(sBufferArg1, element[i], element[i+1]));
						}
					}
					else
					{
						for(int i = 0; i < elementLength; i++)
						{
							sBufferedStream = sBufferedStream.append(traceInfo(sBufferArg1, element[i], "Missing Data"));
						}
					}
				}
				break;
			case LEVEL0:
				break;
			}
			if(sBufferedStream != null)
			{
				sBuffer.append(sBufferedStream);
				if(sStoreClassMethod.get(0).equals(tempString))
				{
					traceEnd();
					sStoreClassMethod.clear();
				}
			}
		}
		catch (Exception e) 
		{
			System.err.println(e);
		}

	}
	
	/**
	* <br><b> getTraceBuffer:</b> returns string buffer which contains the execution path per execution per thread
	* <br> Parameter : none 
	* <br> return type : String
	*/
	public String getTraceBuffer()
	{
		return sBuffer.toString();
	}
	
	/*
	* traceEnd: Closing the string buffer and do clean up activities at the end of the execution
	*/
	private void traceEnd()
	{
		try
		{
			String sBufferedStream = "</DEVLOG>" ;
			String tempBuffer = sBuffer.toString();
			tempBuffer = tempBuffer.replaceAll("</DEVLOG>", "");
			sBuffer = new StringBuffer(tempBuffer);
			sBuffer.append(sBufferedStream);
			String l_FilePath = System.getProperty("java.io.tmpdir")+ Thread.currentThread().getName() + new SimpleDateFormat("_yyyy_dd_MM_hhmmss").format(new Date()) +".xml";
			File file = new File(l_FilePath);
			BufferedWriter bw = null;
			FileWriter fw = null;
			fw = new FileWriter(file.getAbsolutePath(),true);
			bw = new BufferedWriter(fw);
			bw.write(sBuffer.toString());
			bw.close();
			fw.close();
			m_LogInstance.remove();
		}
		catch(Exception e)
		{
			System.err.println(e);
		}
	}
}

Conclusion

With Asynchronous Singleton implementation, Artifact 1 and Artifact 2 got addressed.

Artifact 3. (Prepare an XML based Trace provisioning of the entire execution path per thread) is addressed leveraging the call stack mechanism.

With all the artifacts being addressed, the Tracing mechanism for a multi-threaded environment is successfully implemented.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply