I am using ThreadLocal StringBuffer for storing entire log to move it to kibana at the end of each test case execution. But when I am running cases in parallel, the log is loosing some entries. I am writing the log into file before adding it to StringBuffer and I see all entires in the log file but not in the StringBuffer.
Any ideas are much appreciated.
I tried String Builder but no luck. Tried to make synchronized still no luck. I tried writing the log entry into file before moving into string buffer and I see all entire there but not getting all entries in string buffer
public abstract class Messages {
static ThreadLocal<StringBuffer> msg;
public static void init() {
msg = new ThreadLocal<StringBuffer>() {
@Override
protected StringBuffer initialValue() {
return new StringBuffer();
}
};
public static void addMsg(String msg) {
msg.get().append(msg + "\r\n");
System.out.println(msg);
}
}
public class CallingClass(){
public void callingMethod(String threadName){
Messages.init();
Messages.addMsg("Hi");
Messages.addMsg("This");
Messages.addMsg("Is");
Messages.addMsg("Testing");
Messages.addMsg("For");
Messages.addMsg("Multi");
Messages.addMsg("Thread");
Messages.addMsg("UI");
Messages.addMsg(threadName + "!!");
}
}
From my cucumber tests, we call the above method callingMethod from each thread.
I am running 10 parallel threads and the result is different when I print the msg at the end from all 10 threads, I see for some threads it is missing the first few entries.
I tried making addMsg synchronized but still no luck. In a single thread execution, the log is proper and also when I am using debug from eclipse, the log is coming properly as expected.
Multiple threads call Messages.init()
.
The first time we call init
the previous value of null
for msg
is replaced with a new ThreadLocal
.
The next time we call init
the previous value is replaced with a new copy of ThreadLocal
, effectively discarding all previously used/created StringBuffer
objects.
And the same the next time ...
There's no point in that init
method and you should get rid of it (and all calls to it) and just define msg
like this:
static final ThreadLocal<StringBuffer> msg = new ThreadLocal<StringBuffer>() {
@Override
protected StringBuffer initialValue() {
return new StringBuffer();
}
};
The whole point of ThreadLocal
is that you only need one instance of it to give each thread their own StringBuffer
.
Also, if all access to the content is via those methods, then the synchronization/thread safety of StringBuffer
isn't needed and you should use a StringBuilder
instead.