提交 3138ce98 编写于 作者: F Frankie Wu

draft waterfall logview supported

上级 9c260b41
......@@ -9,6 +9,7 @@ import com.dianping.cat.message.spi.codec.EscapingBufferWriter;
import com.dianping.cat.message.spi.codec.HtmlEncodingBufferWriter;
import com.dianping.cat.message.spi.codec.HtmlMessageCodec;
import com.dianping.cat.message.spi.codec.PlainTextMessageCodec;
import com.dianping.cat.message.spi.codec.WaterfallMessageCodec;
import com.site.lookup.configuration.AbstractResourceConfigurator;
import com.site.lookup.configuration.Component;
......@@ -24,6 +25,8 @@ class CodecComponentConfigurator extends AbstractResourceConfigurator {
.req(BufferWriter.class, EscapingBufferWriter.ID));
all.add(C(MessageCodec.class, HtmlMessageCodec.ID, HtmlMessageCodec.class) //
.req(BufferWriter.class, HtmlEncodingBufferWriter.ID));
all.add(C(MessageCodec.class, WaterfallMessageCodec.ID, WaterfallMessageCodec.class) //
.req(BufferWriter.class, HtmlEncodingBufferWriter.ID));
return all;
}
......
package com.dianping.cat.message.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import com.dianping.cat.message.Event;
import com.dianping.cat.message.Heartbeat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
public abstract class MockMessageBuilder {
private Stack<TransactionHolder> m_stack = new Stack<TransactionHolder>();
public final Message build() {
try {
return define().build();
} finally {
m_stack.clear();
}
}
public abstract MessageHolder define();
protected EventHolder e(String type, String name) {
EventHolder e = new EventHolder(type, name);
TransactionHolder parent = m_stack.isEmpty() ? null : m_stack.peek();
if (parent != null) {
e.setTimestampInMicros(parent.getCurrentTimestampInMicros());
}
return e;
}
protected HeartbeatHolder h(String type, String name) {
HeartbeatHolder h = new HeartbeatHolder(type, name);
TransactionHolder parent = m_stack.isEmpty() ? null : m_stack.peek();
if (parent != null) {
h.setTimestampInMicros(parent.getCurrentTimestampInMicros());
}
return h;
}
protected TransactionHolder t(String type, String name, long durationInMillis) {
TransactionHolder t = new TransactionHolder(type, name, durationInMillis);
TransactionHolder parent = m_stack.isEmpty() ? null : m_stack.peek();
if (parent != null) {
t.setTimestampInMicros(parent.getCurrentTimestampInMicros());
}
m_stack.push(t);
return t;
}
protected static abstract class AbstractMessageHolder implements MessageHolder {
private String m_type;
private String m_name;
private long m_timestampInMicros;
private String m_status = "0";
public AbstractMessageHolder(String type, String name) {
m_type = type;
m_name = name;
}
public String getName() {
return m_name;
}
public String getStatus() {
return m_status;
}
@Override
public long getTimestampInMicros() {
return m_timestampInMicros;
}
public long getTimestampInMillis() {
return m_timestampInMicros / 1000;
}
public String getType() {
return m_type;
}
public void setStatus(String status) {
m_status = status;
}
@Override
public void setTimestampInMicros(long timestampInMicros) {
m_timestampInMicros = timestampInMicros;
}
}
protected static class EventHolder extends AbstractMessageHolder {
private DefaultEvent m_event;
public EventHolder(String type, String name) {
super(type, name);
}
@Override
public Event build() {
m_event = new DefaultEvent(getType(), getName());
m_event.setTimestamp(getTimestampInMillis());
m_event.setStatus(getStatus());
m_event.complete();
return m_event;
}
public EventHolder status(String status) {
setStatus(status);
return this;
}
}
protected static class HeartbeatHolder extends AbstractMessageHolder {
private DefaultHeartbeat m_heartbeat;
public HeartbeatHolder(String type, String name) {
super(type, name);
}
@Override
public Heartbeat build() {
m_heartbeat = new DefaultHeartbeat(getType(), getName());
m_heartbeat.setTimestamp(getTimestampInMillis());
m_heartbeat.setStatus(getStatus());
m_heartbeat.complete();
return m_heartbeat;
}
public HeartbeatHolder status(String status) {
setStatus(status);
return this;
}
}
protected static interface MessageHolder {
public Message build();
public long getTimestampInMicros();
public void setTimestampInMicros(long timestampInMicros);
}
protected class TransactionHolder extends AbstractMessageHolder {
private long m_durationInMicros;
private long m_currentTimestampInMicros;
private List<MessageHolder> m_children = new ArrayList<MessageHolder>();
private DefaultTransaction m_transaction;
private long m_markTimestampInMicros;
public TransactionHolder(String type, String name, long durationInMicros) {
super(type, name);
m_durationInMicros = durationInMicros;
}
public TransactionHolder after(long periodInMicros) {
m_currentTimestampInMicros += periodInMicros;
return this;
}
public TransactionHolder at(long timestampInMillis) {
m_currentTimestampInMicros = timestampInMillis * 1000;
setTimestampInMicros(m_currentTimestampInMicros);
return this;
}
@Override
public Transaction build() {
m_transaction = new DefaultTransaction(getType(), getName(), null);
m_transaction.setTimestamp(getTimestampInMillis());
for (MessageHolder child : m_children) {
m_transaction.addChild(child.build());
}
m_transaction.setStatus(getStatus());
m_transaction.complete();
m_transaction.setDurationInMicros(m_durationInMicros);
return m_transaction;
}
public TransactionHolder child(MessageHolder child) {
if (child instanceof TransactionHolder) {
m_currentTimestampInMicros += ((TransactionHolder) child).getDurationInMicros();
m_stack.pop();
}
m_children.add(child);
return this;
}
public long getCurrentTimestampInMicros() {
return m_currentTimestampInMicros;
}
public long getDurationInMicros() {
return m_durationInMicros;
}
public TransactionHolder mark() {
m_markTimestampInMicros = m_currentTimestampInMicros;
return this;
}
public TransactionHolder reset() {
m_currentTimestampInMicros = m_markTimestampInMicros;
return this;
}
@Override
public void setTimestampInMicros(long timestampInMicros) {
super.setTimestampInMicros(timestampInMicros);
m_currentTimestampInMicros = timestampInMicros;
}
public TransactionHolder status(String status) {
setStatus(status);
return this;
}
}
}
......@@ -34,9 +34,6 @@ public class HtmlMessageCodec implements MessageCodec, Initializable {
@Inject
private String m_logViewPrefix = "/cat/r/m/";
@Inject
private boolean m_showNav = true;
private BufferHelper m_bufferHelper;
private DateHelper m_dateHelper = new DateHelper();
......@@ -65,9 +62,6 @@ public class HtmlMessageCodec implements MessageCodec, Initializable {
count += helper.table1(buf);
count += helper.crlf(buf);
if (m_showNav) {
count += encodeFooter(tree, buf);
}
count += encodeHeader(tree, buf);
if (tree.getMessage() != null) {
......@@ -84,7 +78,7 @@ public class HtmlMessageCodec implements MessageCodec, Initializable {
protected int encodeHeader(MessageTree tree, ChannelBuffer buf) {
BufferHelper helper = m_bufferHelper;
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder(1024);
sb.append("<tr class=\"header\"><td colspan=5>");
sb.append(VERSION).append(" ").append(tree.getDomain()).append(" ");
......@@ -252,10 +246,6 @@ public class HtmlMessageCodec implements MessageCodec, Initializable {
m_logViewPrefix = logViewPrefix;
}
public void setShowNav(boolean showNav) {
m_showNav = showNav;
}
protected static class BufferHelper {
private static byte[] TABLE1 = "<table class=\"logview\">".getBytes();
......
package com.dianping.cat.message.spi.codec;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
......@@ -9,6 +11,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.internal.MockMessageBuilder;
import com.dianping.cat.message.spi.MessageCodec;
import com.dianping.cat.message.spi.MessageTree;
import com.site.lookup.annotation.Inject;
......@@ -24,11 +27,22 @@ public class WaterfallMessageCodec implements MessageCodec, Initializable {
@Inject
private BufferWriter m_writer;
@Inject
private boolean m_showNav = true;
private BufferHelper m_bufferHelper;
private boolean m_mockMode = false;
protected int countTransactions(Transaction t) {
int count = 1;
for (Message child : t.getChildren()) {
if (child instanceof Transaction) {
count += countTransactions((Transaction) child);
}
}
return count;
}
@Override
public MessageTree decode(ChannelBuffer buf) {
throw new UnsupportedOperationException("HtmlMessageCodec only supports one-way encoding!");
......@@ -41,58 +55,60 @@ public class WaterfallMessageCodec implements MessageCodec, Initializable {
@Override
public void encode(MessageTree tree, ChannelBuffer buf) {
int count = 0;
int index = buf.writerIndex();
BufferHelper helper = m_bufferHelper;
Message message = tree.getMessage();
buf.writeInt(0); // place-holder
count += helper.table1(buf);
count += helper.crlf(buf);
if (m_showNav) {
count += encodeFooter(tree, buf);
if (m_mockMode) {
message = mockTransaction();
tree.setMessage(message);
}
count += encodeHeader(tree, buf);
if (tree.getMessage() != null) {
count += encodeMessage(tree, tree.getMessage(), buf, 0);
}
if (message instanceof Transaction) {
int count = 0;
int index = buf.writerIndex();
BufferHelper helper = m_bufferHelper;
Transaction t = (Transaction) message;
Locator locator = new Locator();
Ruler ruler = new Ruler((int) t.getDurationInMicros());
ruler.setWidth(1400);
ruler.setHeight(18 * countTransactions(t) + 10);
ruler.setOffsetX(200);
ruler.setOffsetY(10);
buf.writeInt(0); // place-holder
count += helper.table2(buf);
buf.setInt(index, count);
count += helper.table1(buf);
count += helper.crlf(buf);
count += encodeHeader(tree, buf, ruler);
count += encodeRuler(buf, locator, ruler);
count += encodeTransaction(tree, t, buf, locator, ruler);
count += encodeFooter(tree, buf);
count += helper.table2(buf);
buf.setInt(index, count);
}
}
protected int encodeFooter(MessageTree tree, ChannelBuffer buf) {
BufferHelper helper = m_bufferHelper;
int count = 0;
String uri = "/cat/r/m/" + tree.getMessageId();
count += helper.tr1(buf, "nav");
count += helper.td1(buf, "colspan=\"4\" align=\"left\"");
count += helper.nbsp(buf, 3);
count += helper.write(buf, "<a href=\"");
count += helper.write(buf, uri);
count += helper.write(buf, "?tag1=t:");
count += helper.write(buf, tree.getThreadId());
count += helper.write(buf, "\">&lt;&lt;&lt; Thread &nbsp;&nbsp;</a>");
count += helper.write(buf, "<a href=\"");
count += helper.write(buf, uri);
count += helper.write(buf, "?tag2=t:");
count += helper.write(buf, tree.getThreadId());
count += helper.write(buf, "\"> &nbsp;&nbsp;Thread &gt;&gt;&gt;</a>");
count += helper.nbsp(buf, 3);
count += helper.td2(buf);
count += helper.tr2(buf);
count += helper.crlf(buf);
XmlBuilder b = new XmlBuilder();
StringBuilder sb = b.getResult();
b.tag2("g");
b.tag2("svg");
sb.append("</td></tr>");
int count = helper.write(buf, sb.toString());
return count;
}
protected int encodeHeader(MessageTree tree, ChannelBuffer buf) {
protected int encodeHeader(MessageTree tree, ChannelBuffer buf, Ruler ruler) {
BufferHelper helper = m_bufferHelper;
StringBuilder sb = new StringBuilder();
XmlBuilder b = new XmlBuilder();
StringBuilder sb = b.getResult();
sb.append("<tr class=\"header\"><td colspan=5>");
sb.append("<tr class=\"header\"><td>");
sb.append(VERSION).append(" ").append(tree.getDomain()).append(" ");
sb.append(tree.getHostName()).append(" ").append(tree.getIpAddress()).append(" ");
sb.append(tree.getThreadGroupName()).append(" ").append(tree.getThreadId()).append(" ");
......@@ -100,31 +116,122 @@ public class WaterfallMessageCodec implements MessageCodec, Initializable {
sb.append(tree.getParentMessageId()).append(" ").append(tree.getRootMessageId()).append(" ");
sb.append(tree.getSessionToken()).append(" ");
sb.append("</td></tr>");
sb.append("<tr><td>");
int height = ruler.getHeight();
int width = ruler.getWidth();
b.add("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\r\n");
b.tag1("svg", "x", 0, "y", 0, "width", width, "height", height, "viewBox", "0,0," + width + "," + height, "xmlns",
"http://www.w3.org/2000/svg");
b.tag1("g", "font-size", "12", "stroke", "gray");
int count = helper.write(buf, sb.toString());
return count;
}
protected int encodeLine(MessageTree tree, Message message, ChannelBuffer buf, int level) {
return 0;
protected int encodeLine(MessageTree tree, Transaction t, ChannelBuffer buf, Locator locator, Ruler ruler) {
BufferHelper helper = m_bufferHelper;
XmlBuilder b = new XmlBuilder();
StringBuilder sb = b.getResult();
int width = 6;
int height = 18;
int x = 0;
int y = locator.getLine() * height + ruler.getOffsetY();
b.branch(locator, x, y, width, height);
x += locator.getLevel() * width;
if (t.getStatus().equals("0")) {
b.tagWithText("text", t.getType(), "x", x, "y", y - 5, "font-weight", "bold", "stroke-width", "0");
} else {
b.tagWithText("text", t.getType(), "x", x, "y", y - 5, "font-weight", "bold", "stroke-width", "0", "fill", "red");
}
long t0 = tree.getMessage().getTimestamp();
long t1 = t.getTimestamp();
long d = t.getDurationInMicros();
int rx = ruler.calcX((t1 - t0) * 1000);
int rw = ruler.calcX(d) - ruler.getOffsetX();
b.tag("rect", "x", rx, "y", y - 15, "width", rw, "height", height - 2, "fill", "#0066ff", "opacity", "0.5");
b.tagWithText("text", String.format("%.3f %s", t.getDurationInMicros() / 1000.0, t.getName()), "x", rx + 5, "y", y - 3,
"font-size", "11", "stroke-width", "0");
int count = helper.write(buf, sb.toString());
return count;
}
protected int encodeMessage(MessageTree tree, Message message, ChannelBuffer buf, int level) {
if (message instanceof Transaction) {
Transaction transaction = (Transaction) message;
List<Message> children = transaction.getChildren();
int count = 0;
protected int encodeRuler(ChannelBuffer buf, Locator locator, Ruler ruler) {
BufferHelper helper = m_bufferHelper;
XmlBuilder b = new XmlBuilder();
StringBuilder sb = b.getResult();
PathBuilder p = new PathBuilder();
int height = 600;
count += encodeLine(tree, transaction, buf, level);
b.tag1("g", "id", "ruler", "font-size", "12", "text-anchor", "middle", "stroke", "black", "stroke-width", "1");
for (Message child : children) {
count += encodeMessage(tree, child, buf, level + 1);
int unitNum = ruler.getUnitNum();
int unitStep = ruler.getUnitStep();
int unit = (int) ruler.getUnit();
int x = ruler.getOffsetX();
int y = 10;
for (int i = 0; i <= unitNum; i++) {
String text;
if (unitStep >= 1000) {
text = (i * unitStep / 1000) + "ms";
} else {
text = (i * unitStep) + "us";
}
return count;
} else {
return 0;
b.tagWithText("text", text, "x", x + i * unit, "y", y, "stroke-width", "0");
}
for (int i = 0; i <= unitNum; i++) {
b.tag("path", "d", p.moveTo(x + i * unit, y + 6).v(height).build(), "stroke-dasharray", "3,4");
}
b.tag2("g");
int count = helper.write(buf, sb.toString());
return count;
}
protected int encodeTransaction(MessageTree tree, Transaction transaction, ChannelBuffer buf, Locator locator, Ruler ruler) {
List<Transaction> children = getChildTransactions(transaction);
int count = 0;
locator.downLevel(children.isEmpty());
locator.nextLine();
count += encodeLine(tree, transaction, buf, locator, ruler);
int len = children.size();
for (int i = 0; i < len; i++) {
Transaction child = children.get(i);
locator.setLast(i == len - 1);
count += encodeTransaction(tree, child, buf, locator, ruler);
}
locator.upLevel();
return count;
}
protected List<Transaction> getChildTransactions(Transaction transaction) {
List<Transaction> children = new ArrayList<Transaction>();
for (Message child : transaction.getChildren()) {
if (child instanceof Transaction) {
children.add((Transaction) child);
}
}
return children;
}
@Override
......@@ -132,15 +239,46 @@ public class WaterfallMessageCodec implements MessageCodec, Initializable {
m_bufferHelper = new BufferHelper(m_writer);
}
protected Transaction mockTransaction() { // for test/debug purpose
return (Transaction) new MockMessageBuilder() {
@Override
public MessageHolder define() {
TransactionHolder t = t("WEB CLUSTER", "GET", 112819) //
.at(1348374838231L) //
.after(1300).child(t("QUICKIE SERVICE", "gimme_stuff", 1571)) //
.after(100).child(e("SERVICE", "event1")) //
.after(100).child(h("SERVICE", "heartbeat1")) //
.after(100).child(t("WEB SERVER", "GET", 109358).status("1") //
.after(1000).child(t("SOME SERVICE", "get", 4345) //
.after(4000).child(t("MEMCACHED", "Get", 279))) //
.mark().after(200).child(t("MEMCACHED", "Inc", 319)) //
.reset().after(500).child(t("BIG ASS SERVICE", "getThemDatar", 97155) //
.after(1000).mark().child(t("SERVICE", "getStuff", 3760)) //
.reset().child(t("DATAR", "findThings", 94537)) //
.after(200).child(t("THINGIE", "getMoar", 1435)) //
) //
.after(100).mark().child(t("OTHER DATA SERVICE", "get", 4394) //
.after(800).mark().child(t("MEMCACHED", "Get", 378)) //
.reset().child(t("MEMCACHED", "Get", 3496)) //
) //
.reset().child(t("FINAL DATA SERVICE", "get", 1902) //
.after(1000).mark().child(t("MEMCACHED", "Get", 386)) //
.reset().child(t("MEMCACHED", "Get", 322)) //
.reset().child(t("MEMCACHED", "Get", 322)) //
) //
) //
;
return t;
}
}.build();
}
public void setBufferWriter(BufferWriter writer) {
m_writer = writer;
m_bufferHelper = new BufferHelper(m_writer);
}
public void setShowNav(boolean showNav) {
m_showNav = showNav;
}
protected static class BufferHelper {
private static byte[] TABLE1 = "<table class=\"logview\">".getBytes();
......@@ -292,4 +430,398 @@ public class WaterfallMessageCodec implements MessageCodec, Initializable {
return m_writer.writeTo(buf, data);
}
}
protected static class Locator {
private int m_level;
private int m_line;
private Stack<Boolean> m_last = new Stack<Boolean>();
private Stack<Integer> m_flags = new Stack<Integer>();
public void downLevel(boolean atomic) {
if (m_level > 0) {
boolean last = m_last.peek();
m_flags.pop();
if (last) {
m_flags.push(6); // 00110
} else {
m_flags.push(22); // 10110
}
for (int i = 0; i < m_level - 1; i++) {
Integer flag = m_flags.get(i);
int f = flag;
if (flag == 6) { // 00110
f = 0; // 00000
} else if (flag == 22) { // 10110
f = 20; // 10100
}
m_flags.set(i, f);
}
}
boolean root = m_level == 0;
if (atomic) {
if (root) {
m_flags.push(1); // 00001
} else {
m_flags.push(9); // 01001
}
} else {
if (root) {
m_flags.push(17); // 10001
} else {
m_flags.push(25); // 11001
}
}
m_last.push(root ? true : false);
m_level++;
}
public Stack<Integer> getFlags() {
return m_flags;
}
public boolean getLast(int level) {
return m_last.get(level);
}
public int getLevel() {
return m_level;
}
public int getLine() {
return m_line;
}
public boolean isFirst() {
return m_level == 1;
}
public boolean isLast() {
return m_last.peek();
}
public void nextLine() {
m_line++;
}
public void setLast(boolean last) {
m_last.pop();
m_last.push(last);
}
@Override
public String toString() {
return String.format("Locator[level=%s, line=%s, first=%s, last=%s]", m_level, m_line, isFirst(), isLast());
}
public void upLevel() {
m_level--;
m_last.pop();
m_flags.pop();
}
}
protected static class PathBuilder {
private int m_marker;
private StringBuilder m_sb = new StringBuilder(64);
public String build() {
String result = m_sb.toString();
m_sb.setLength(0);
return result;
}
public PathBuilder h(int deltaX) {
m_sb.append(" h").append(deltaX);
return this;
}
public PathBuilder m(int deltaX, int deltaY) {
m_sb.append(" m").append(deltaX).append(',').append(deltaY);
return this;
}
public PathBuilder mark() {
m_marker = m_sb.length();
return this;
}
public PathBuilder moveTo(int x, int y) {
m_sb.append('M').append(x).append(',').append(y);
return this;
}
public PathBuilder repeat(int count) {
int pos = m_sb.length();
for (int i = 0; i < count; i++) {
m_sb.append(m_sb.subSequence(m_marker, pos));
}
return this;
}
public PathBuilder v(int deltaY) {
m_sb.append(" v").append(deltaY);
return this;
}
}
protected static class Ruler {
private static final int[] UNITS = { 1, 2, 3, 5 };
private int m_maxValue;
private int m_unitNum;
private int m_unitStep;
public int m_width;
private int m_height;
private int m_offsetX;
private int m_offsetY;
public Ruler(int maxValue) {
m_maxValue = maxValue;
int e = 1;
int value = maxValue;
while (true) {
if (value > 50) {
value = (value + 9) / 10;
e *= 10;
} else {
if (value < 6) {
m_unitNum = value;
m_unitStep = e;
} else {
for (int unit : UNITS) {
int num = (value + unit - 1) / unit;
if (num >= 6 && num <= 10) {
m_unitNum = num;
m_unitStep = unit * e;
break;
}
}
}
break;
}
}
}
public int calcX(long value) {
int w = (int) (value * getUnit() / m_unitStep);
if (w == 0 && value > 0) {
w = 1;
}
return w + m_offsetX;
}
public int getHeight() {
return m_height;
}
public int getMaxValue() {
return m_maxValue;
}
public int getOffsetX() {
return m_offsetX;
}
public int getOffsetY() {
return m_offsetY;
}
public double getUnit() {
return (m_width - m_offsetX - 20) * 1.0 / m_unitNum;
}
public int getUnitNum() {
return m_unitNum;
}
public int getUnitStep() {
return m_unitStep;
}
public int getWidth() {
return m_width;
}
public void setHeight(int height) {
m_height = height;
}
public void setOffsetX(int offsetX) {
m_offsetX = offsetX;
}
public void setOffsetY(int offsetY) {
m_offsetY = offsetY;
}
public void setWidth(int width) {
m_width = width;
}
@Override
public String toString() {
return String.format("[%s, %s, %s]", m_maxValue, m_unitNum, m_unitStep);
}
}
protected static class XmlBuilder {
private boolean m_compact;
private int m_level;
private StringBuilder m_sb = new StringBuilder(8192);
public XmlBuilder add(String text) {
m_sb.append(text);
return this;
}
public void branch(Locator locator, int x, int y, int width, int height) {
PathBuilder p = new PathBuilder();
int w = width / 2;
int h = height / 2;
int r = 2;
for (Integer flag : locator.getFlags()) {
int cx = x + w;
int cy = y - h;
if ((flag & 2) != 0) { // 00010
tag("path", "d", p.moveTo(cx, cy).h(w).build());
}
if ((flag & 4) != 0) { // 00100
tag("path", "d", p.moveTo(cx, cy).v(-h).build());
}
if ((flag & 8) != 0) { // 01000
tag("path", "d", p.moveTo(cx, cy).h(-w).build());
}
if ((flag & 16) != 0) { // 10000
tag("path", "d", p.moveTo(cx, cy).v(h).build());
}
if ((flag & 1) != 0) { // 00001
m_sb.append("<circle cx=\"").append(cx).append("\" cy=\"").append(cy).append("\" r=\"").append(r)
.append("\" stroke=\"red\" fill=\"white\"/>");
}
x += width;
}
}
public XmlBuilder element(String name, String value) {
indent();
m_sb.append('<').append(name).append('>');
m_sb.append(value);
m_sb.append("</").append(name).append(">");
newLine();
return this;
}
public StringBuilder getResult() {
return m_sb;
}
public XmlBuilder indent() {
if (!m_compact) {
for (int i = m_level - 1; i >= 0; i--) {
m_sb.append(" ");
}
}
return this;
}
public XmlBuilder newLine() {
m_sb.append("\r\n");
return this;
}
public XmlBuilder tag(String name, Object... attributes) {
return tagWithText(name, null, attributes);
}
public XmlBuilder tag1(String name, Object... attributes) {
indent();
m_sb.append('<').append(name);
int len = attributes.length;
for (int i = 0; i < len; i += 2) {
Object key = attributes[i];
Object val = attributes[i + 1];
if (val != null) {
m_sb.append(' ').append(key).append("=\"").append(val).append('"');
}
}
m_sb.append(">");
newLine();
m_level++;
return this;
}
public XmlBuilder tag2(String name) {
m_level--;
indent();
m_sb.append("</").append(name).append(">");
newLine();
return this;
}
public XmlBuilder tagWithText(String name, Object text, Object... attributes) {
indent();
m_sb.append('<').append(name);
int len = attributes.length;
for (int i = 0; i < len; i += 2) {
Object key = attributes[i];
Object val = attributes[i + 1];
if (val != null) {
m_sb.append(' ').append(key).append("=\"").append(val).append('"');
}
}
if (text == null) {
m_sb.append("/>");
} else {
m_sb.append('>').append(text).append("</").append(name).append('>');
}
newLine();
return this;
}
}
}
package com.site.initialization;
public abstract class AbstractModule implements Module {
private boolean m_initialized;
protected abstract void execute(ModuleContext ctx) throws Exception;
@Override
public void initialize(ModuleContext ctx) throws Exception {
execute(ctx);
}
@Override
public boolean isInitialized() {
return m_initialized;
}
@Override
public void setInitialized(boolean initialized) {
m_initialized = initialized;
}
protected void setup(ModuleContext ctx) throws Exception {
// no nothing by default
}
}
package com.site.initialization;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.LoggerManager;
public class DefaultModuleContext implements ModuleContext {
private PlexusContainer m_container;
private Map<String, Object> m_attributes;
private Logger m_logger;
public DefaultModuleContext(PlexusContainer container) {
m_container = container;
m_attributes = new HashMap<String, Object>();
try {
LoggerManager loggerManager = container.lookup(LoggerManager.class);
m_logger = loggerManager.getLoggerForComponent(PlexusContainer.class.getName());
} catch (Exception e) {
throw new RuntimeException("Unable to get instance of Logger, "
+ "please make sure the environment was setup correctly!", e);
}
}
@Override
public void error(String message) {
m_logger.error(message);
}
@Override
public void error(String message, Throwable e) {
m_logger.error(message, e);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAttribute(String name) {
return (T) m_attributes.get(name);
}
@Override
public void info(String message) {
m_logger.info(message);
}
@Override
public <T> T lookup(Class<T> role) {
return lookup(role, null);
}
@Override
public <T> T lookup(Class<T> role, String roleHint) {
try {
return m_container.lookup(role, roleHint);
} catch (ComponentLookupException e) {
throw new RuntimeException("Unable to get component: " + role + ".", e);
}
}
@Override
public void release(Object component) {
try {
m_container.release(component);
} catch (ComponentLifecycleException e) {
throw new RuntimeException("Unable to release component: " + component + ".", e);
}
}
@Override
public void setAttribute(String name, Object value) {
m_attributes.put(name, value);
}
@Override
public void warn(String message) {
m_logger.warn(message);
}
@Override
public Module[] getModules(String... names) {
Module[] modules = new Module[names.length];
int index = 0;
for (String name : names) {
modules[index++] = lookup(Module.class, name);
}
return modules;
}
public PlexusContainer getContainer() {
return m_container;
}
}
package com.site.initialization;
import java.util.LinkedHashSet;
import java.util.Set;
import com.site.lookup.annotation.Inject;
public class DefaultModuleInitializer implements ModuleInitializer {
@Inject
private ModuleManager m_manager;
@Inject
private boolean m_verbose;
private int m_index = 1;
@Override
public void execute(ModuleContext ctx) {
Module[] modules = m_manager.getTopLevelModules();
execute(ctx, modules);
}
@Override
public void execute(ModuleContext ctx, Module... modules) {
Set<Module> all = new LinkedHashSet<Module>();
info(ctx, "Initializing top level modules:");
for (Module module : modules) {
info(ctx, " " + module.getClass().getName());
}
try {
expandAll(ctx, modules, all);
for (Module module : all) {
if (!module.isInitialized()) {
executeModule(ctx, module, m_index++);
}
}
} catch (Exception e) {
throw new RuntimeException("Error when initializing modules!", e);
}
}
private void info(ModuleContext ctx, String message) {
if (m_verbose) {
ctx.info(message);
}
}
private synchronized void executeModule(ModuleContext ctx, Module module, int index) throws Exception {
long start = System.currentTimeMillis();
// set flat to avoid re-entrance
module.setInitialized(true);
info(ctx, index + " ------ " + module.getClass().getName());
// execute itself after its dependencies
module.initialize(ctx);
long end = System.currentTimeMillis();
info(ctx, index + " ------ " + module.getClass().getName() + " DONE in " + (end - start) + " ms.");
}
private void expandAll(ModuleContext ctx, Module[] modules, Set<Module> all) throws Exception {
if (modules != null) {
for (Module module : modules) {
expandAll(ctx, module.getDependencies(ctx), all);
if (!all.contains(module)) {
if (module instanceof AbstractModule) {
((AbstractModule) module).setup(ctx);
}
all.add(module);
}
}
}
}
public void setVerbose(boolean verbose) {
m_verbose = verbose;
}
}
package com.site.initialization;
import java.util.List;
import com.site.helper.Splitters;
import com.site.lookup.ContainerHolder;
import com.site.lookup.annotation.Inject;
public class DefaultModuleManager extends ContainerHolder implements ModuleManager {
@Inject
private String m_topLevelModules;
@Override
public Module[] getTopLevelModules() {
if (m_topLevelModules != null) {
List<String> hints = Splitters.by(',').trim().noEmptyItem().split(m_topLevelModules);
Module[] topLevelModules = new Module[hints.size()];
int index = 0;
for (String hint : hints) {
topLevelModules[index++] = lookup(Module.class, hint);
}
return topLevelModules;
} else {
return new Module[0];
}
}
public void setTopLevelModules(String topLevelModules) {
m_topLevelModules = topLevelModules;
}
}
package com.site.initialization;
public interface Module {
public Module[] getDependencies(ModuleContext ctx);
public void initialize(ModuleContext ctx) throws Exception;
public boolean isInitialized();
public void setInitialized(boolean initialized);
}
package com.site.initialization;
public interface ModuleContext {
public <T> T getAttribute(String name);
public void info(String message);
public void warn(String message);
public void error(String message);
public void error(String message, Throwable t);
public <T> T lookup(Class<T> role);
public <T> T lookup(Class<T> role, String roleHint);
public void release(Object component);
public void setAttribute(String name, Object value);
public Module[] getModules(String... modules);
}
package com.site.initialization;
public interface ModuleInitializer {
public void execute(ModuleContext ctx);
public void execute(ModuleContext ctx, Module... modules);
}
package com.site.initialization;
public interface ModuleManager {
public Module[] getTopLevelModules();
}
......@@ -259,6 +259,17 @@
</requirement>
</requirements>
</component>
<component>
<role>com.dianping.cat.message.spi.MessageCodec</role>
<role-hint>waterfall</role-hint>
<implementation>com.dianping.cat.message.spi.codec.WaterfallMessageCodec</implementation>
<requirements>
<requirement>
<role>com.dianping.cat.message.spi.codec.BufferWriter</role>
<role-hint>html</role-hint>
</requirement>
</requirements>
</component>
<component>
<role>com.dianping.cat.storage.BucketManager</role>
<implementation>com.dianping.cat.storage.DefaultBucketManager</implementation>
......
......@@ -11,6 +11,7 @@ import com.dianping.cat.message.TransactionTest;
import com.dianping.cat.message.configuration.ClientConfigTest;
import com.dianping.cat.message.internal.MessageIdFactoryTest;
import com.dianping.cat.message.internal.MillisSecondTimerTest;
import com.dianping.cat.message.internal.MockMessageBuilderTest;
import com.dianping.cat.message.io.TcpSocketTest;
import com.dianping.cat.message.spi.codec.HtmlMessageCodecTest;
import com.dianping.cat.message.spi.codec.PlainTextMessageCodecTest;
......@@ -48,6 +49,8 @@ MillisSecondTimerTest.class,
DefaultMessagePathBuilderTest.class,
MockMessageBuilderTest.class,
/* .io */
TcpSocketTest.class,
......
package com.dianping.cat.message.internal;
import junit.framework.Assert;
import org.junit.Test;
import com.dianping.cat.message.Message;
public class MockMessageBuilderTest {
@Test
public void test() {
Message message = new MockMessageBuilder() {
@Override
public MessageHolder define() {
TransactionHolder t = t("WEB CLUSTER", "GET", 112819) //
.at(1348374838231L) //
.after(1300).child(t("QUICKIE SERVICE", "gimme_stuff", 1571)) //
.after(100).child(e("SERVICE", "event1")) //
.after(100).child(h("SERVICE", "heartbeat1")) //
.after(100).child(t("WEB SERVER", "GET", 109358) //
.after(1000).child(t("SOME SERVICE", "get", 4345) //
.after(4000).child(t("MEMCACHED", "Get", 279))) //
.mark().after(200).child(t("MEMCACHED", "Inc", 319)) //
.reset().after(500).child(t("BIG ASS SERVICE", "getThemDatar", 97155) //
.after(1000).mark().child(t("SERVICE", "getStuff", 3760)) //
.reset().child(t("DATAR", "findThings", 94537)) //
.after(200).child(t("THINGIE", "getMoar", 1435)) //
) //
.after(100).mark().child(t("OTHER DATA SERVICE", "get", 4394) //
.after(1000).mark().child(t("MEMCACHED", "Get", 378)) //
.reset().child(t("MEMCACHED", "Get", 3496)) //
) //
.reset().child(t("FINAL DATA SERVICE", "get", 4394) //
.after(1000).mark().child(t("MEMCACHED", "Get", 386)) //
.reset().child(t("MEMCACHED", "Get", 322)) //
.reset().child(t("MEMCACHED", "Get", 322)) //
) //
) //
;
return t;
}
}.build();
Assert.assertEquals("t2012-09-23 12:33:58.231 WEB CLUSTER GET \n" + //
"A2012-09-23 12:33:58.232 QUICKIE SERVICE gimme_stuff 0 1571us \n" + //
"E2012-09-23 12:33:58.233 SERVICE event1 0 \n" + //
"H2012-09-23 12:33:58.234 SERVICE heartbeat1 0 \n" + //
"t2012-09-23 12:33:58.234 WEB SERVER GET \n" + //
"t2012-09-23 12:33:58.235 SOME SERVICE get \n" + //
"A2012-09-23 12:33:58.239 MEMCACHED Get 0 279us \n" + //
"T2012-09-23 12:33:58.239 SOME SERVICE get 0 4345us \n" + //
"A2012-09-23 12:33:58.239 MEMCACHED Inc 0 319us \n" + //
"t2012-09-23 12:33:58.240 BIG ASS SERVICE getThemDatar \n" + //
"A2012-09-23 12:33:58.241 SERVICE getStuff 0 3760us \n" + //
"A2012-09-23 12:33:58.241 DATAR findThings 0 94537us \n" + //
"A2012-09-23 12:33:58.335 THINGIE getMoar 0 1435us \n" + //
"T2012-09-23 12:33:58.337 BIG ASS SERVICE getThemDatar 0 97155us \n" + //
"t2012-09-23 12:33:58.337 OTHER DATA SERVICE get \n" + //
"A2012-09-23 12:33:58.338 MEMCACHED Get 0 378us \n" + //
"A2012-09-23 12:33:58.338 MEMCACHED Get 0 3496us \n" + //
"T2012-09-23 12:33:58.341 OTHER DATA SERVICE get 0 4394us \n" + //
"t2012-09-23 12:33:58.337 FINAL DATA SERVICE get \n" + //
"A2012-09-23 12:33:58.338 MEMCACHED Get 0 386us \n" + //
"A2012-09-23 12:33:58.338 MEMCACHED Get 0 322us \n" + //
"A2012-09-23 12:33:58.338 MEMCACHED Get 0 322us \n" + //
"T2012-09-23 12:33:58.341 FINAL DATA SERVICE get 0 4394us \n" + //
"T2012-09-23 12:33:58.343 WEB SERVER GET 0 109358us \n" + //
"T2012-09-23 12:33:58.343 WEB CLUSTER GET 0 112819us \n" + //
"", message.toString().replace("\r", ""));
}
}
......@@ -30,7 +30,6 @@ public class HtmlMessageCodecTest extends ComponentTestCase {
HtmlMessageCodec codec = (HtmlMessageCodec) lookup(MessageCodec.class, "html");
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
codec.setShowNav(false);
codec.encodeMessage(tree, message, buf, 0, null);
String actual = buf.toString(Charset.forName("utf-8"));
......@@ -41,7 +40,6 @@ public class HtmlMessageCodecTest extends ComponentTestCase {
HtmlMessageCodec codec = (HtmlMessageCodec) lookup(MessageCodec.class, "html");
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
codec.setShowNav(false);
codec.encode(tree, buf);
buf.readInt(); // get rid of length
String actual = buf.toString(Charset.forName("utf-8"));
......
package com.dianping.cat.message.spi.codec;
import junit.framework.Assert;
import org.junit.Test;
import com.dianping.cat.message.spi.codec.WaterfallMessageCodec.Ruler;
public class WaterfallMessageCodecTest {
@Test
public void testRuler() {
checkRuler(0, 0, 1);
checkRuler(3, 3, 1);
checkRuler(6, 6, 1);
checkRuler(10, 10, 1);
checkRuler(11, 6, 2);
checkRuler(20, 10, 2);
checkRuler(21, 7, 3);
checkRuler(34, 7, 5);
checkRuler(51, 6, 10);
checkRuler(100, 10, 10);
checkRuler(1001, 6, 200);
checkRuler(3476, 7, 500);
checkRuler(112819, 6, 20000);
}
private void checkRuler(int maxValue, int expectedUnitNum, int expectedUnitStep) {
Ruler ruler = new Ruler(maxValue);
Assert.assertEquals(String.format("[%s, %s, %s]", maxValue, expectedUnitNum, expectedUnitStep), ruler.toString());
}
}
......@@ -52,6 +52,8 @@ public class LocalMessageBucketManagerTest extends ComponentTestCase {
long now = 1343532130488L;
int num = 100;
Thread.sleep(100);
factory.setIpAddress("7f000001");
factory.initialize("source");
......@@ -59,6 +61,8 @@ public class LocalMessageBucketManagerTest extends ComponentTestCase {
manager.storeMessage(newMessageTree(factory.getNextId(), i, now + i * 10L));
}
Thread.yield();
for (int i = 0; i < num; i++) {
String messageId = "source-7f000001-373203-" + i;
MessageTree tree = manager.loadMessage(messageId);
......@@ -66,6 +70,8 @@ public class LocalMessageBucketManagerTest extends ComponentTestCase {
Assert.assertNotNull("Message " + i + " not found.", tree);
Assert.assertEquals(messageId, tree.getMessageId());
}
manager.close();
}
static class MockMessageIdFactory extends MessageIdFactory {
......
......@@ -45,22 +45,21 @@ public class DomainManager implements Initializable {
@Override
public void initialize() throws InitializationException {
try {
List<Hostinfo> infos = m_hostInfoDao.findAllIp(HostinfoEntity.READSET_FULL);
for (Hostinfo info : infos) {
m_ipDomains.put(info.getIp(), info.getDomain());
if (!m_manager.isLocalMode()) {
try {
List<Hostinfo> infos = m_hostInfoDao.findAllIp(HostinfoEntity.READSET_FULL);
for (Hostinfo info : infos) {
m_ipDomains.put(info.getIp(), info.getDomain());
}
} catch (DalException e) {
Cat.logError(e);
}
} catch (DalException e) {
Cat.logError(e);
}
if (!m_manager.isLocalMode()) {
Threads.forGroup("Cat").start(new Reload());
}
}
class Reload implements Task {
@Override
public void run() {
while (true) {
......@@ -74,7 +73,7 @@ public class DomainManager implements Initializable {
addIps.add(hostinfo.getIp());
m_ipDomains.put(hostinfo.getIp(), hostinfo.getDomain());
} catch (Exception e) {
//ignore
// ignore
}
}
for (String ip : addIps) {
......
......@@ -24,18 +24,14 @@ public class Handler implements PageHandler<Context> {
@Inject(type = ModelService.class, value = "logview")
private ModelService<String> m_service;
private String getLogView(String messageId, String direction, String tag) {
private String getLogView(String messageId, boolean waterfall) {
try {
if (messageId != null) {
MessageId id = MessageId.parse(messageId);
ModelPeriod period = ModelPeriod.getByTime(id.getTimestamp());
ModelRequest request = new ModelRequest(id.getDomain(), period) //
.setProperty("messageId", messageId);
if (direction != null && tag != null) {
request.setProperty("direction", String.valueOf(direction));
request.setProperty("tag", tag);
}
.setProperty("messageId", messageId) //
.setProperty("waterfall", String.valueOf(waterfall));
if (m_service.isEligable(request)) {
ModelResponse<String> response = m_service.invoke(request);
......@@ -83,7 +79,7 @@ public class Handler implements PageHandler<Context> {
model.setLongDate(payload.getDate());
String messageId = getMessageId(payload);
String logView = getLogView(messageId, payload.getDirection(), payload.getTag());
String logView = getLogView(messageId, payload.isWaterfall());
switch (payload.getAction()) {
case VIEW:
......
......@@ -10,19 +10,16 @@ import com.site.web.mvc.payload.annotation.PathMeta;
public class Payload extends AbstractReportPayload<Action> {
@FieldMeta("op")
private Action m_action;
private Action m_action = Action.VIEW;
@PathMeta("path")
private String[] m_path;
@FieldMeta("header")
private boolean m_showHeader = true;
@FieldMeta("tag1")
private String m_tag1;
@FieldMeta("tag2")
private String m_tag2;
@FieldMeta("waterfall")
private boolean m_waterfall;
public Payload() {
super(ReportPage.LOGVIEW);
......@@ -30,42 +27,23 @@ public class Payload extends AbstractReportPayload<Action> {
@Override
public Action getAction() {
if (m_action == null) {
return Action.VIEW;
}
return m_action;
}
public String getDirection() {
if (m_tag1 != null) {
return "false";
} else if (m_tag2 != null) {
return "true";
} else {
return null;
}
}
public String[] getPath() {
return m_path;
}
public String getTag() {
if (m_tag1 != null) {
return m_tag1;
} else if (m_tag2 != null) {
return m_tag2;
} else {
return null;
}
}
public boolean isShowHeader() {
return m_showHeader;
}
public boolean isWaterfall() {
return m_waterfall;
}
public void setAction(String action) {
m_action = Action.getByName(action, Action.VIEW);
m_action = Action.getByName(action, m_action);
}
public void setPath(String[] path) {
......@@ -80,13 +58,9 @@ public class Payload extends AbstractReportPayload<Action> {
m_showHeader = !"no".equals(showHeader);
}
public void setTag1(String tag1) {
m_tag1 = tag1;
}
public void setTag2(String tag2) {
m_tag2 = tag2;
}
public void setWaterfall(boolean waterfall) {
m_waterfall = waterfall;
}
@Override
public void validate(ActionContext<?> ctx) {
......
......@@ -168,13 +168,7 @@ public class Handler extends ContainerHolder implements PageHandler<Context> {
MessageId id = MessageId.parse(messageId);
request.setProperty("messageId", messageId);
if (!StringUtils.isEmpty(payload.getDirection())) {
request.setProperty("direction", payload.getDirection());
}
if (!StringUtils.isEmpty(payload.getTag())) {
request.setProperty("tag", payload.getTag());
}
request.setProperty("waterfall", String.valueOf(payload.isWaterfall()));
if (id.getVersion() == 1) {
response = m_logviewService.invoke(request);
......
......@@ -13,15 +13,15 @@ public class Payload implements ActionPayload<ReportPage, Action> {
@FieldMeta("op")
private Action m_action;
@FieldMeta("direction")
private String m_direction;
@FieldMeta("ip")
private String m_ipAddress;
@FieldMeta("messageId")
private String m_messageId;
@FieldMeta("waterfall")
private boolean m_waterfall;
@FieldMeta("name")
private String m_name;
......@@ -31,9 +31,6 @@ public class Payload implements ActionPayload<ReportPage, Action> {
@PathMeta("path")
private String[] m_path;
@FieldMeta("tag")
private String m_tag;
@FieldMeta("thread")
private String m_threadId;
......@@ -48,8 +45,8 @@ public class Payload implements ActionPayload<ReportPage, Action> {
return m_action;
}
public String getDirection() {
return m_direction;
public String getDatabase() {
return m_database;
}
public String getDomain() {
......@@ -93,10 +90,6 @@ public class Payload implements ActionPayload<ReportPage, Action> {
}
}
public String getTag() {
return m_tag;
}
public String getThreadId() {
return m_threadId;
}
......@@ -105,12 +98,16 @@ public class Payload implements ActionPayload<ReportPage, Action> {
return m_type;
}
public boolean isWaterfall() {
return m_waterfall;
}
public void setAction(String action) {
m_action = Action.getByName(action, Action.XML);
}
public void setDirection(String direction) {
m_direction = direction;
public void setDatabase(String database) {
m_database = database;
}
public void setIpAddress(String ipAddress) {
......@@ -138,10 +135,6 @@ public class Payload implements ActionPayload<ReportPage, Action> {
}
}
public void setTag(String tag) {
m_tag = tag;
}
public void setThreadId(String threadId) {
m_threadId = threadId;
}
......@@ -150,12 +143,8 @@ public class Payload implements ActionPayload<ReportPage, Action> {
m_type = type;
}
public String getDatabase() {
return m_database;
}
public void setDatabase(String database) {
m_database = database;
public void setWaterfall(boolean waterfall) {
m_waterfall = waterfall;
}
@Override
......
......@@ -42,13 +42,13 @@ public class HistoricalMessageService extends BaseLocalModelService<String> {
MessageTree tree = m_localBucketManager.loadMessage(messageId);
if (tree != null) {
return toString(tree);
return toString(request, tree);
}
tree = m_hdfsBucketManager.loadMessage(messageId);
if (tree != null) {
return toString(tree);
return toString(request, tree);
} else {
return null;
}
......@@ -68,10 +68,18 @@ public class HistoricalMessageService extends BaseLocalModelService<String> {
return eligibale;
}
private String toString(MessageTree tree) {
protected String toString(ModelRequest request, MessageTree tree) {
ChannelBuffer buf = ChannelBuffers.dynamicBuffer(8192);
m_codec.encode(tree, buf);
if (request.getProperty("waterfall", "false").equals("true")) {
// to work around a plexus injection bug
MessageCodec codec = lookup(MessageCodec.class, "waterfall");
codec.encode(tree, buf);
} else {
m_codec.encode(tree, buf);
}
buf.readInt(); // get rid of length
return buf.toString(Charset.forName("utf-8"));
}
......
......@@ -39,7 +39,15 @@ public class LocalMessageService extends BaseLocalModelService<String> {
if (tree != null) {
ChannelBuffer buf = ChannelBuffers.dynamicBuffer(8192);
m_codec.encode(tree, buf);
if (request.getProperty("waterfall", "false").equals("true")) {
// to work around a plexus injection bug
MessageCodec codec = lookup(MessageCodec.class, "waterfall");
codec.encode(tree, buf);
} else {
m_codec.encode(tree, buf);
}
buf.readInt(); // get rid of length
return buf.toString(Charset.forName("utf-8"));
}
......
......@@ -85,6 +85,7 @@ public abstract class BaseLocalModelService<T> extends ModelServiceWithCalSuppor
t.setStatus("NoReportFound");
}
} catch (Exception e) {
e.printStackTrace();
logError(e);
t.setStatus(e);
response.setException(e);
......
......@@ -124,7 +124,7 @@
</c:otherwise>
</c:choose>
</table>
<font color="white">${lastIndex+1}</font>
<font color="white">${lastIndex}</font>
<c:choose>
<c:when test="${not empty payload.type}">
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册