package detective.common.trace;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import detective.common.DateUtil;
import detective.common.json.JacksonMsgConverter;
import detective.core.services.DetectiveFactory;
public class TraceRecordBuilder {
private static final Logger logger = LoggerFactory.getLogger(TraceRecordBuilder.class);
private static final JacksonMsgConverter msgConverter = new JacksonMsgConverter();
private final TraceRecord record;
private static String instanceId;
private TraceRecordBuilder(TraceRecord record) {
this.record = record;
}
/**
* Setup the time stamp to now and create a new TraceRecordBuilder and Thread to current thread
* name
*
* @return the new record
*/
public static TraceRecordBuilder newRecord() {
TraceRecord record = new TraceRecord();
return build(record);
}
/**
* Setup the time stamp to now and create a new TraceRecordBuilder and Thread to current thread
* name
*/
public static TraceRecordBuilder build(TraceRecord record) {
if (instanceId == null)
instanceId = DetectiveFactory.INSTANCE.getMachineName();
record.setTimestamp(new Date()).setThreadName(Thread.currentThread().getName());
return new TraceRecordBuilder(record).withObject("_RunningInstance", instanceId);
}
public static TraceRecordBuilder buildUnhandledException(Throwable exception, String type, String hashKey) {
logger.error(exception.getMessage(), exception);
TraceRecord record = new TraceRecord();
record.setType(type);
record.setHashKey(hashKey);
record.getExtendDatas().put("_Exception", "UnhandledException");
return build(record).withException(exception);
}
public TraceRecord getRecord() {
return record;
}
/**
* Setup HashKey as yyyy-MM-dd as of now
* @return the builder
*/
public TraceRecordBuilder withSimpleDateAsHashKey(){
record.setHashKey(DateUtil.formatSimpleDate(new Date()));
return this;
}
/**
* Always add below information into TraceRecord
* <ul>
* <li>request.path</li>
* <li>request.parameters</li>
* <li>request.cookies</li>
* <li>request.content</li>
* </ul>
*
*/
public TraceRecordBuilder withHttpRequest(HttpServletRequest request) {
Map<String, Object> map = record.getExtendDatas();
map.put("request.path", request.getRequestURL().toString());
Map<String, String[]> paramMap = request.getParameterMap();
if (paramMap != null && paramMap.size() > 0) {
map.put("request.parameters", this.msgConverter.toJson(request.getParameterMap()));
}
if (request.getCookies() != null && request.getCookies().length > 0){
StringBuilder sb = new StringBuilder("[");
for (Cookie c : request.getCookies()) {
sb.append("{\"").append(c.getName()).append("\":\"").append(c.getValue()).append("\"},");
}
sb.append("]");
map.put("request.cookies", sb.toString());
}
map.put("request.content", getRequestInputStream(request));
return this;
}
/**
* Always add below exception information into TraceRecord
* <ul>
* <li>exception.msg</li>
* <li>exception.callstack</li>
* <li>exception.class</li>
* </ul>
*/
public TraceRecordBuilder withException(Throwable exception) {
Map<String, Object> map = record.getExtendDatas();
if (null != exception.getMessage())
map.put("exception.msg", exception.getMessage());
map.put("exception.class", exception.getClass().getName());
map.put("exception.callstack", getStackTrace(exception));
return this;
}
/**
* Add extend data into trace record, this basically doing: record.getExtendDatas().put(fieldName,
* toJson(obj));
*
* @param obj, will convert to json before put into extend data, will use toString() if convert to
* json fails
*/
public TraceRecordBuilder withObject(String fieldName, Object obj) {
String json = toJsonSafe(obj);
record.getExtendDatas().put(fieldName, json);
return this;
}
/**
* Add extend data into trace record, this basically doing: record.getExtendDatas().put(fieldName,
* obj);
*/
public TraceRecordBuilder withObject(String fieldName, Number obj) {
record.getExtendDatas().put(fieldName, obj);
return this;
}
/**
* Add extend data into trace record, this basically doing: record.getExtendDatas().put(fieldName,
* obj);
*/
public TraceRecordBuilder withObject(String fieldName, String obj) {
record.getExtendDatas().put(fieldName, obj);
return this;
}
public TraceRecordBuilder withPayLoad(Object obj){
record.getExtendDatas().put("payload", toJsonSafe(obj));
return this;
}
private static String toJsonSafe(Object obj) {
try {
if (obj == null)
return null;
if (obj instanceof String)
return (String) obj;
String result = msgConverter.toJson(obj);
return result;
} catch (Throwable e) {
return "Convert to JSON error:" + e.getMessage() + " the object:" + obj.toString();
}
}
private static String getRequestInputStream(HttpServletRequest request) {
try {
return IOUtils.toString(request.getInputStream(), "UTF-8");
} catch (IOException e) {
logger.error(e.getMessage(), e);
return "ERROR:" + e.getMessage();
}
}
private static String getStackTrace(Throwable aThrowable) {
Writer result = new StringWriter();
PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
}