提交 191a6f61 编写于 作者: F Frankie Wu

transaction report draft

上级 0442671c
package com.dianping.cat.consumer.failure;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import com.dianping.cat.configuration.model.entity.Config;
import com.dianping.cat.configuration.model.entity.Property;
import com.dianping.cat.consumer.failure.model.entity.FailureReport;
import com.dianping.cat.consumer.failure.model.transform.DefaultJsonBuilder;
import com.dianping.cat.message.spi.AbstractMessageAnalyzer;
import com.dianping.cat.message.spi.MessageManager;
import com.dianping.cat.message.spi.MessageStorage;
import com.dianping.cat.message.spi.MessageTree;
import com.site.helper.Files;
import com.site.lookup.annotation.Inject;
/**
* @author sean.wang
* @since Jan 5, 2012
*/
public class FailureAnalyzer extends AbstractMessageAnalyzer<FailureReport> implements Initializable, LogEnabled {
private static final SimpleDateFormat FILE_SDF = new SimpleDateFormat("yyyyMMddHHmm");
private static final long MINUTE = 60 * 1000;
@Inject
private MessageManager m_messageManager;
@Inject
private MessageStorage m_messageStorage;
private Map<String, FailureReport> m_reports = new HashMap<String, FailureReport>();
private long m_extraTime;
private String m_reportPath;
private Logger m_logger;
private long m_startTime;
private long m_duration;
@Override
public void enableLogging(Logger logger) {
m_logger = logger;
}
@Override
protected List<FailureReport> generate() {
List<FailureReport> reports = new ArrayList<FailureReport>(m_reports.size());
for (String domain : m_reports.keySet()) {
FailureReport report = generate(domain);
reports.add(report);
}
return reports;
}
FailureReport generate(String domain) {
if (domain == null) {
List<String> domains = getDomains();
domain = domains.size() > 0 ? domains.get(0) : null;
}
FailureReport report = m_reports.get(domain);
return report;
}
public List<String> getDomains() {
List<String> domains = new ArrayList<String>(m_reports.keySet());
Collections.sort(domains, new Comparator<String>() {
@Override
public int compare(String d1, String d2) {
if (d1.equals("Cat")) {
return 1;
}
return d1.compareTo(d2);
}
});
return domains;
}
public FailureReport getReport(String domain) {
return m_reports.get(domain);
}
public Map<String, FailureReport> getReports() {
return m_reports;
}
private String getFailureFileName(FailureReport report) {
StringBuffer result = new StringBuffer();
String start = FILE_SDF.format(report.getStartTime());
String end = FILE_SDF.format(report.getEndTime());
result.append(report.getDomain()).append("-").append(start).append("-").append(end);
return result.toString();
}
@Override
public void initialize() throws InitializationException {
Config config = m_messageManager.getClientConfig();
if (config != null) {
Property property = config.findProperty("failure-base-dir");
if (property != null) {
m_reportPath = property.getValue();
}
}
}
@Override
protected boolean isTimeout() {
long currentTime = System.currentTimeMillis();
long endTime = m_startTime + m_duration + m_extraTime;
return currentTime > endTime;
}
@Override
protected void process(MessageTree tree) {
}
public void setAnalyzerInfo(long startTime, long duration, String domain, long extraTime) {
m_extraTime = extraTime;
m_startTime = startTime;
m_duration = duration;
}
public void setMessageStorage(MessageStorage messageStorage) {
m_messageStorage = messageStorage;
}
public void setReportPath(String reportPath) {
m_reportPath = reportPath;
}
@Override
protected void store(List<FailureReport> reports) {
if (reports == null || reports.size() == 0) {
return;
}
for (FailureReport report : reports) {
String failureFileName = getFailureFileName(report);
String htmlPath = new StringBuilder().append(m_reportPath).append(failureFileName).append(".html").toString();
File file = new File(htmlPath);
file.getParentFile().mkdirs();
try {
Files.forIO().writeTo(file, new DefaultJsonBuilder().buildJson(report));
} catch (IOException e) {
m_logger.error(String.format("Error when writing to file(%s)!", file), e);
}
}
}
}
package com.dianping.cat.consumer.transaction;
import com.dianping.cat.consumer.transaction.model.entity.TransactionName;
import com.dianping.cat.consumer.transaction.model.entity.TransactionType;
import com.dianping.cat.consumer.transaction.model.transform.BaseVisitor;
public class MeanSquareDeviationComputer extends BaseVisitor {
@Override
public void visitName(TransactionName name) {
long count = name.getTotalCount();
if (count > 0) {
long failCount = name.getFailCount();
double avg = name.getSum() / count;
double std = std(count, avg, name.getSum2());
double failPercent = 100.0 * failCount / count;
name.setFailPercent(failPercent);
name.setAvg(avg);
name.setStd(std);
}
}
@Override
public void visitType(TransactionType type) {
super.visitType(type);
long count = type.getTotalCount();
if (count > 0) {
long failCount = type.getFailCount();
double avg = type.getSum() / count;
double std = std(count, avg, type.getSum2());
double failPercent = 100.0 * failCount / count;
type.setFailPercent(failPercent);
type.setAvg(avg);
type.setStd(std);
}
}
double std(long count, double avg, double sum2) {
return Math.sqrt(sum2 / count - avg * avg);
}
}
\ No newline at end of file
package com.dianping.cat.consumer.transaction;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import com.dianping.cat.configuration.model.entity.Config;
import com.dianping.cat.configuration.model.entity.Property;
import com.dianping.cat.consumer.transaction.model.entity.TransactionName;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.consumer.transaction.model.entity.TransactionType;
import com.dianping.cat.consumer.transaction.model.transform.DefaultJsonBuilder;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.spi.AbstractMessageAnalyzer;
import com.dianping.cat.message.spi.MessageManager;
import com.dianping.cat.message.spi.MessageStorage;
import com.dianping.cat.message.spi.MessageTree;
import com.site.helper.Files;
import com.site.lookup.annotation.Inject;
/**
* @author sean.wang
* @since Jan 5, 2012
*/
public class TransactionAnalyzer extends AbstractMessageAnalyzer<TransactionReport> implements Initializable,
LogEnabled {
private static final SimpleDateFormat FILE_SDF = new SimpleDateFormat("yyyyMMddHHmm");
private static final long MINUTE = 60 * 1000;
@Inject
private MessageManager m_messageManager;
@Inject
private MessageStorage m_messageStorage;
private Map<String, TransactionReport> m_reports = new HashMap<String, TransactionReport>();
private long m_extraTime;
private String m_reportPath;
private Logger m_logger;
private long m_startTime;
private long m_duration;
@Override
public void enableLogging(Logger logger) {
m_logger = logger;
}
@Override
protected List<TransactionReport> generate() {
List<TransactionReport> reports = new ArrayList<TransactionReport>(m_reports.size());
MeanSquareDeviationComputer computer = new MeanSquareDeviationComputer();
for (String domain : m_reports.keySet()) {
TransactionReport report = generate(domain);
report.accept(computer);
reports.add(report);
}
return reports;
}
TransactionReport generate(String domain) {
if (domain == null) {
List<String> domains = getDomains();
domain = domains.size() > 0 ? domains.get(0) : null;
}
TransactionReport report = m_reports.get(domain);
return report;
}
public List<String> getDomains() {
List<String> domains = new ArrayList<String>(m_reports.keySet());
Collections.sort(domains, new Comparator<String>() {
@Override
public int compare(String d1, String d2) {
if (d1.equals("Cat")) {
return 1;
}
return d1.compareTo(d2);
}
});
return domains;
}
public TransactionReport getReport(String domain) {
return m_reports.get(domain);
}
public Map<String, TransactionReport> getReports() {
return m_reports;
}
private String getTransactionFileName(TransactionReport report) {
StringBuffer result = new StringBuffer();
String start = FILE_SDF.format(report.getStartTime());
String end = FILE_SDF.format(report.getEndTime());
result.append(report.getDomain()).append("-").append(start).append("-").append(end);
return result.toString();
}
@Override
public void initialize() throws InitializationException {
Config config = m_messageManager.getClientConfig();
if (config != null) {
Property property = config.findProperty("transaction-base-dir");
if (property != null) {
m_reportPath = property.getValue();
}
}
}
@Override
protected boolean isTimeout() {
long currentTime = System.currentTimeMillis();
long endTime = m_startTime + m_duration + m_extraTime;
return currentTime > endTime;
}
@Override
protected void process(MessageTree tree) {
String domain = tree.getDomain();
TransactionReport report = m_reports.get(domain);
if (report == null) {
report = new TransactionReport(domain);
report.setStartTime(new Date(m_startTime));
report.setEndTime(new Date(m_startTime + MINUTE * 60 - 1));
m_reports.put(domain, report);
}
Message message = tree.getMessage();
if (message instanceof Transaction) {
int count = processTransaction(report, tree, (Transaction) message);
// the message is required by some transactions
if (count > 0) {
m_messageStorage.store(tree);
}
}
}
int processTransaction(TransactionReport report, MessageTree tree, Transaction t) {
TransactionType type = report.findOrCreateType(t.getType());
TransactionName name = type.findOrCreateName(t.getName());
String url = m_messageStorage.getPath(tree);
int count = 0;
type.incTotalCount();
name.incTotalCount();
if (t.isSuccess()) {
if (type.getSuccessMessageUrl() == null) {
type.setSuccessMessageUrl(url);
count++;
}
if (name.getSuccessMessageUrl() == null) {
name.setSuccessMessageUrl(url);
count++;
}
} else {
type.incFailCount();
name.incFailCount();
if (type.getFailMessageUrl() == null) {
type.setFailMessageUrl(url);
count++;
}
if (name.getFailMessageUrl() == null) {
name.setFailMessageUrl(url);
count++;
}
}
// update statistics
long duration = t.getDuration();
name.setMax(Math.max(name.getMax(), duration));
name.setMin(Math.min(name.getMin(), duration));
name.setSum(name.getSum() + duration);
name.setSum2(name.getSum2() + duration * duration);
type.setMax(Math.max(type.getMax(), duration));
type.setMin(Math.min(type.getMin(), duration));
type.setSum(type.getSum() + duration);
type.setSum2(type.getSum2() + duration * duration);
List<Message> children = t.getChildren();
for (Message child : children) {
if (child instanceof Transaction) {
count += processTransaction(report, tree, (Transaction) child);
}
}
return count;
}
public void setAnalyzerInfo(long startTime, long duration, String domain, long extraTime) {
m_extraTime = extraTime;
m_startTime = startTime;
m_duration = duration;
}
public void setMessageStorage(MessageStorage messageStorage) {
m_messageStorage = messageStorage;
}
public void setReportPath(String reportPath) {
m_reportPath = reportPath;
}
@Override
protected void store(List<TransactionReport> reports) {
if (reports == null || reports.size() == 0) {
return;
}
for (TransactionReport report : reports) {
String failureFileName = getTransactionFileName(report);
String htmlPath = new StringBuilder().append(m_reportPath).append(failureFileName).append(".html").toString();
File file = new File(htmlPath);
file.getParentFile().mkdirs();
try {
Files.forIO().writeTo(file, new DefaultJsonBuilder().buildJson(report));
} catch (IOException e) {
m_logger.error(String.format("Error when writing to file(%s)!", file), e);
}
}
}
}
package com.dianping.cat.consumer;
import junit.framework.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class MyTest {
public static void main(String[] args) {
System.out.println("second");
System.out.println("second");
System.out.println("second");
System.out.println("second");
System.out.println("second");
// second comment
System.out.println("second");
System.out.println("second");
System.out.println("second");
System.out.println("second");
System.out.println("second");
System.out.println("second");
}
@Test
public void firstCase() {
Assert.assertEquals("First", "Fir" + "st");
}
@Test
@Ignore("need database connection")
public void secondCase() {
System.out.println("Second");
}
}
package com.dianping.cat.consumer.transaction;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import junit.framework.Assert;
import org.junit.Test;
public class FormatTest {
private void checkFormat(Number number, String format, String expected) {
String actual = new DecimalFormat(format).format(number);
Assert.assertEquals(expected, actual);
}
private void checkFranceFormat(Number number, String format, String expected) {
DecimalFormat df = new DecimalFormat(format);
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.FRANCE));
String actual = df.format(number);
Assert.assertEquals(expected, actual);
}
@Test
public void testFormat() {
checkFormat(12, "0", "12");
checkFormat(12.34, "0", "12");
checkFormat(12, "0.#", "12");
checkFormat(12.34, "0.#", "12.3");
checkFormat(12.35, "0.#", "12.4");
checkFormat(12.34, "0.##", "12.34");
checkFormat(12.346, "0.##", "12.35");
checkFormat(0.3467, "0.#%", "34.7%");
}
@Test
public void testFranceFormat() {
checkFranceFormat(12, "0", "12");
checkFranceFormat(12.34, "0", "12");
checkFranceFormat(12, "0.#", "12");
checkFranceFormat(12.34, "0.#", "12,3");
checkFranceFormat(12.35, "0.#", "12,4");
checkFranceFormat(12.34, "0.##", "12,34");
checkFranceFormat(12.346, "0.##", "12,35");
checkFranceFormat(0.3467, "0.#%", "34,7%");
}
}
package com.dianping.cat.consumer.transaction;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.dianping.cat.consumer.transaction.model.transform.DefaultJsonBuilder;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.internal.DefaultTransaction;
import com.dianping.cat.message.spi.MessageStorage;
import com.dianping.cat.message.spi.MessageTree;
import com.dianping.cat.message.spi.internal.DefaultMessageTree;
import com.site.helper.Files;
import com.site.lookup.ComponentTestCase;
@RunWith(JUnit4.class)
public class TransactionAnalyzerTest extends ComponentTestCase {
@Test
public void testProcessTransaction() throws Exception {
TransactionAnalyzer analyzer = new TransactionAnalyzer();
TransactionReport report = new TransactionReport("Test");
analyzer.setMessageStorage(lookup(MessageStorage.class, "html"));
for (int i = 1; i <= 1000; i++) {
MessageTree tree = newMessageTree(i);
DefaultTransaction t = new DefaultTransaction("A", "n" + i % 2, null);
DefaultTransaction t2 = new DefaultTransaction("A-1", "n" + i % 3, null);
if (i % 2 == 0) {
t2.setStatus("ERROR");
} else {
t2.setStatus(Message.SUCCESS);
}
t2.complete();
t2.setDuration(i);
t.addChild(t2);
if (i % 2 == 0) {
t.setStatus("ERROR");
} else {
t.setStatus(Message.SUCCESS);
}
t.complete();
t.setDuration(i * 2);
tree.setMessage(t);
analyzer.processTransaction(report, tree, t);
}
report.accept(new MeanSquareDeviationComputer());
String json = new DefaultJsonBuilder().buildJson(report);
String expected = Files.forIO().readFrom(getClass().getResourceAsStream("TransactionAnalyzerTest.json"), "utf-8");
Assert.assertEquals(expected.replace("\r", ""), json.replace("\r", ""));
}
protected MessageTree newMessageTree(int i) {
MessageTree tree = new DefaultMessageTree();
tree.setMessageId("" + i);
tree.setDomain("group");
tree.setHostName("group001");
tree.setIpAddress("192.168.1.1");
return tree;
}
}
{
"domain": "Test",
"types": {
"A": {
"id": "A",
"successMessageUrl": "20120228/21/group/1.html",
"failMessageUrl": "20120228/21/group/2.html",
"totalCount": 1000,
"failCount": 500,
"failPercent": "50.00",
"min": 2.0,
"max": 2000.0,
"avg": "1001.0",
"sum": "1001000.0",
"sum2": "1335334000.0",
"std": "577.3",
"names": {
"n1": {
"id": "n1",
"totalCount": 500,
"failCount": 0,
"failPercent": "0.00",
"min": 2.0,
"max": 1998.0,
"avg": "1000.0",
"sum": "500000.0",
"sum2": "666666000.0",
"std": "577.3",
"successMessageUrl": "20120228/21/group/1.html"
},
"n0": {
"id": "n0",
"totalCount": 500,
"failCount": 500,
"failPercent": "100.00",
"min": 4.0,
"max": 2000.0,
"avg": "1002.0",
"sum": "501000.0",
"sum2": "668668000.0",
"std": "577.3",
"failMessageUrl": "20120228/21/group/2.html"
}
}
},
"A-1": {
"id": "A-1",
"successMessageUrl": "20120228/21/group/1.html",
"failMessageUrl": "20120228/21/group/2.html",
"totalCount": 1000,
"failCount": 500,
"failPercent": "50.00",
"min": 1.0,
"max": 1000.0,
"avg": "500.5",
"sum": "500500.0",
"sum2": "333833500.0",
"std": "288.7",
"names": {
"n1": {
"id": "n1",
"totalCount": 334,
"failCount": 167,
"failPercent": "50.00",
"min": 1.0,
"max": 1000.0,
"avg": "500.5",
"sum": "167167.0",
"sum2": "111611611.0",
"std": "289.3",
"successMessageUrl": "20120228/21/group/1.html",
"failMessageUrl": "20120228/21/group/4.html"
},
"n2": {
"id": "n2",
"totalCount": 333,
"failCount": 167,
"failPercent": "50.15",
"min": 2.0,
"max": 998.0,
"avg": "500.0",
"sum": "166500.0",
"sum2": "110944278.0",
"std": "288.4",
"successMessageUrl": "20120228/21/group/5.html",
"failMessageUrl": "20120228/21/group/2.html"
},
"n0": {
"id": "n0",
"totalCount": 333,
"failCount": 166,
"failPercent": "49.85",
"min": 3.0,
"max": 999.0,
"avg": "501.0",
"sum": "166833.0",
"sum2": "111277611.0",
"std": "288.4",
"successMessageUrl": "20120228/21/group/3.html",
"failMessageUrl": "20120228/21/group/6.html"
}
}
}
}
}
<transaction-report domain="Cat" startTime="2012-02-16 23:00:00" endTime="2012-02-16 23:59:00">
<domain>Cat</domain>
<type id="URL" totalCount="11" failCount="0" failPercent="0.00" min="0.0" max="194.0" avg="47.1" sum="518.0" sum2="73942.0" std="67.1">
<name id="home" totalCount="1" failCount="0" failPercent="0.00" min="175.0" max="175.0" avg="175.0" sum="175.0" sum2="30625.0" std="0.0">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
<range minute="5" count="123" sum="123456" avg="22.2" transactions="3"/>
<range minute="10" count="123" sum="12457" avg="222" transactions="3"/>
<duration value="128" count="34"/>
<duration value="256" count="12"/>
</name>
<name id="service" totalCount="8" failCount="0" failPercent="0.00" min="1.0" max="58.0" avg="13.0" sum="104.0" sum2="3952.0" std="18.0">
<successMessageUrl>20120216/23/Cat/b10bdefb-1eca-45e1-a9c3-52367078b5a2.html</successMessageUrl>
</name>
<name id="t" totalCount="1" failCount="0" failPercent="0.00" min="193.0" max="193.0" avg="193.0" sum="193.0" sum2="37249.0" std="0.0">
<successMessageUrl>20120216/23/Cat/8e7c91a0-7549-4b13-b43b-252b2a6ef4bb.html</successMessageUrl>
</name>
<name id="ip" totalCount="1" failCount="0" failPercent="0.00" min="46.0" max="46.0" avg="46.0" sum="46.0" sum2="2116.0" std="0.0">
<successMessageUrl>20120216/23/Cat/2029c32e-b692-4e43-8eaf-96b8d6c846a2.html</successMessageUrl>
</name>
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</type>
<type id="MVC" totalCount="33" failCount="0" failPercent="0.00" min="0.0" max="191.0" avg="15.1" sum="499.0" sum2="68377.0" std="42.9">
<name id="InboundPhase" totalCount="11" failCount="0" failPercent="0.00" min="0.0" max="17.0" avg="1.5" sum="17.0" sum2="289.0" std="4.9">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<name id="TransitionPhase" totalCount="11" failCount="0" failPercent="0.00" min="0.0" max="4.9E-324" avg="0.0" sum="0.0" sum2="0.0" std="0.0">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<name id="OutboundPhase" totalCount="11" failCount="0" failPercent="0.00" min="1.0" max="191.0" avg="43.8" sum="482.0" sum2="68088.0" std="65.3">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</type>
<type id="NEW1" totalCount="33" failCount="0" failPercent="0.00" min="0.0" max="191.0" avg="15.1" sum="499.0" sum2="68377.0" std="42.9">
<name id="InboundPhase" totalCount="11" failCount="0" failPercent="0.00" min="0.0" max="17.0" avg="1.5" sum="17.0" sum2="289.0" std="4.9">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<name id="TransitionPhase" totalCount="11" failCount="0" failPercent="0.00" min="0.0" max="4.9E-324" avg="0.0" sum="0.0" sum2="0.0" std="0.0">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<name id="OutboundPhase" totalCount="11" failCount="0" failPercent="0.00" min="1.0" max="191.0" avg="43.8" sum="482.0" sum2="68088.0" std="65.3">
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</name>
<successMessageUrl>20120216/23/Cat/1168a02c-664b-440c-9ef4-a87bac4d9cb1.html</successMessageUrl>
</type>
</transaction-report>
package com.dianping.cat.report.graph;
public abstract class AbstractGraphPayload implements GraphPayload {
private double[] m_values;
private String m_title;
private String m_axisXLabel;
private String m_axisYLabel;
public AbstractGraphPayload(String title, String axisXLabel, String axisYLabel) {
m_title = title;
m_axisXLabel = axisXLabel;
m_axisYLabel = axisYLabel;
m_values = getValues();
}
@Override
public String getAxisXLabel() {
return m_axisXLabel;
}
@Override
public String getAxisYLabel() {
return m_axisYLabel;
}
@Override
public int getColumns() {
return m_values.length;
}
@Override
public String getDescription() {
return null;
}
@Override
public int getDisplayHeight() {
return getHeight();
}
@Override
public int getDisplayWidth() {
return getWidth();
}
@Override
public int getHeight() {
return 280;
}
@Override
public int getMarginBottom() {
return 50;
}
@Override
public int getMarginLeft() {
return 90;
}
@Override
public int getMarginRight() {
return 10;
}
@Override
public int getMarginTop() {
return 50;
}
@Override
public int getRows() {
return 5;
}
@Override
public String getTitle() {
return m_title;
}
@Override
public int getWidth() {
return 580;
}
}
package com.dianping.cat.report.graph;
import java.text.DecimalFormat;
import com.site.lookup.annotation.Inject;
public class DefaultGraphBuilder implements GraphBuilder {
@Inject
private ValueTranslater m_translater;
@Override
public String build(GraphPayload payload) {
double[] values = payload.getValues();
int maxValue = m_translater.getMaxValue(values);
XmlBuilder b = new XmlBuilder();
buildHeader(payload, b);
buildCoordinate(payload, b);
buildYLabels(payload, b, maxValue);
buildXLabels(payload, b);
buildBars(payload, b, maxValue, values);
buildFooter(payload, b);
return b.getResult().toString();
}
protected void buildBars(GraphPayload payload, XmlBuilder b, int maxValue, double[] values) {
DecimalFormat format = new DecimalFormat("0.#");
int width = payload.getWidth();
int height = payload.getHeight();
int top = payload.getMarginTop();
int left = payload.getMarginLeft();
int bottom = payload.getMarginBottom();
int right = payload.getMarginRight();
int h = height - top - bottom;
int w = width - left - right;
int cols = payload.getColumns();
int xstep = w / cols;
int[] pixels = m_translater.translate(h, maxValue, values);
b.tag1("g", "id", "bar", "fill", "red");
for (int i = 0; i < cols && i < pixels.length; i++) {
int pixel = pixels[i];
int x = left + xstep * i;
int y = top + h - pixel;
b.tag("rect", "id", "b" + i, "x", x, "y", y, "width", xstep - 1, "height", pixel);
}
b.tag2("g");
b.tag1("g", "id", "label");
for (int i = 0; i < cols && i < pixels.length; i++) {
int pixel = pixels[i];
double value = values[i];
int x = left + xstep * i;
int y = top - 6 + h - pixel;
String tip = format.format(value);
// adjust
if (x + tip.length() * 7 > width - right) {
x = width - right - tip.length() * 7;
}
b.tag1("text", "x", x, "y", y, "display", "none");
b.indent().add(tip).newLine();
b.tag("set", "attributeName", "display", "from", "none", "to", "block", "begin", "b" + i + ".mouseover",
"end", "b" + i + ".mouseout");
b.tag2("text");
}
b.tag2("g");
}
protected void buildCoordinate(GraphPayload payload, XmlBuilder b) {
int width = payload.getWidth();
int height = payload.getHeight();
int top = payload.getMarginTop();
int left = payload.getMarginLeft();
int bottom = payload.getMarginBottom();
int right = payload.getMarginRight();
int h = height - top - bottom;
int w = width - left - right;
int rows = payload.getRows();
int cols = payload.getColumns();
int ystep = h / rows;
int xstep = w / cols;
PathBuilder p = new PathBuilder();
b.tag1("g", "id", "coordinate", "stroke", "#003f7f", "fill", "white");
b.tag("path", "id", "xy", "d", p.moveTo(left, top + h).h(w).m(-w, 0).v(-h).build());
b.tag("path", "id", "xy-2", "d", p.moveTo(left, top).m(w, 0).v(h).build(), "stroke-dasharray", "1,5");
b.tag("path", "id", "lines", "d", p.moveTo(left, top).mark().h(w).m(-w, ystep).repeat(rows - 1).build(),
"stroke-dasharray", "1,5");
if (rows >= 8) {
p.moveTo(left, top).mark().h(-9).m(9, ystep).h(-5).m(5, ystep).repeat(rows / 2 - 1);
if (rows % 2 == 0) {
p.h(-9).m(9, ystep);
}
b.tag("path", "id", "ys", "d", p.build());
} else {
p.moveTo(left, top).mark().h(-7).m(7, ystep).repeat(rows);
b.tag("path", "id", "ys", "d", p.build());
}
if (cols >= 16) {
p.moveTo(left, top + h).mark().v(9).m(xstep, -9).v(5).m(xstep, -5).repeat(cols / 2);
if (cols % 2 == 0) {
p.v(9).m(xstep, -9);
}
b.tag("path", "id", "xs", "d", p.build());
} else {
p.moveTo(left, top + h).mark().v(7).m(xstep, -7).repeat(cols);
b.tag("path", "id", "xs", "d", p.build());
}
b.tag2("g");
}
protected void buildFooter(GraphPayload payload, XmlBuilder b) {
b.tag2("svg");
}
protected void buildHeader(GraphPayload payload, XmlBuilder b) {
int height = payload.getHeight();
int width = payload.getWidth();
int top = payload.getMarginTop();
int left = payload.getMarginLeft();
int bottom = payload.getMarginBottom();
int right = payload.getMarginRight();
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", "width", payload.getDisplayWidth(), "height", payload.getDisplayHeight(), "viewBox", "0,0," + width
+ "," + height, "xmlns", "http://www.w3.org/2000/svg");
String title = payload.getTitle();
if (title != null) {
b.element("title", title);
}
if (payload.getDescription() != null) {
b.element("description", payload.getDescription());
}
b.tag1("g");
String axisXLabel = payload.getAxisXLabel();
if (axisXLabel != null) {
int x = (width - left - right - axisXLabel.length() * 9) / 2 + left;
int y = height - 4;
b.tagWithText("text", axisXLabel, "x", x, "y", y, "font-size", "18");
}
String axisYLabel = payload.getAxisYLabel();
if (axisYLabel != null) {
int x = 16;
int y = (height - top - bottom + axisYLabel.length() * 9) / 2 + top;
String transform = "rotate(-90," + x + "," + y + ")";
b.tagWithText("text", axisYLabel, "x", x, "y", y, "font-size", "18", "transform", transform);
}
if (title != null) {
int x = (width - left - right - title.length() * 12) / 2 + left;
int y = 24;
b.tagWithText("text", title, "x", x, "y", y, "font-size", "24");
}
b.tag2("g");
}
protected void buildXLabels(GraphPayload payload, XmlBuilder b) {
int height = payload.getHeight();
int width = payload.getWidth();
int left = payload.getMarginLeft();
int bottom = payload.getMarginBottom();
int right = payload.getMarginRight();
int cols = payload.getColumns();
int w = width - left - right;
int xstep = w / cols;
b.tag1("g", "id", "xt");
if (cols >= 16) {
for (int i = 0; i <= cols; i += 2) {
int x = left + xstep * i - 4;
int y = height - bottom + 22;
if (i >= 10) {
x -= 4;
}
b.tagWithText("text", i, "x", x, "y", y);
}
} else {
for (int i = 0; i <= cols; i++) {
int x = left + xstep * i - 4;
int y = height - bottom + 20;
if (i >= 10) {
x -= 4;
}
b.tagWithText("text", i, "x", x, "y", y);
}
}
b.tag2("g");
}
protected void buildYLabels(GraphPayload payload, XmlBuilder b, int maxValue) {
int height = payload.getHeight();
int top = payload.getMarginTop();
int left = payload.getMarginLeft();
int bottom = payload.getMarginBottom();
int h = height - top - bottom;
int rows = payload.getRows();
int ystep = h / rows;
b.tag1("g", "id", "yt", "direction", "rtl");
if (rows >= 8) {
for (int i = 0; i < rows; i += 2) {
int x = left - 12;
int y = top + 4 + ystep * i;
b.tagWithText("text", maxValue - maxValue / rows * i, "x", x, "y", y);
}
} else {
for (int i = 0; i < rows; i++) {
int x = left - 9;
int y = top + 4 + ystep * i;
b.tagWithText("text", maxValue - maxValue / rows * i, "x", x, "y", y);
}
}
b.tag2("g");
}
protected static class PathBuilder {
private StringBuilder m_sb = new StringBuilder(64);
private int m_marker;
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 XmlBuilder {
private StringBuilder m_sb = new StringBuilder(8192);
private boolean m_compact;
private int m_level;
public XmlBuilder add(String text) {
m_sb.append(text);
return this;
}
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.dianping.cat.report.graph;
public class DefaultValueTranslater implements ValueTranslater {
@Override
public int getMaxValue(double[] values) {
double min = Integer.MAX_VALUE;
double max = Integer.MIN_VALUE;
int len = values.length;
for (int i = 0; i < len; i++) {
double value = values[i];
if (value < min) {
min = value;
}
if (value > max) {
max = value;
}
}
double maxLog = Math.log10(max);
int maxValue = (int) Math.pow(10, Math.ceil(maxLog));
while (maxValue > max * 2) {
maxValue = maxValue / 2;
}
return maxValue;
}
@Override
public int[] translate(int height, int maxValue, double[] values) {
int len = values.length;
int[] result = new int[len];
for (int i = 0; i < len; i++) {
double value = values[i];
result[i] = (int) (value * height / maxValue);
}
return result;
}
}
package com.dianping.cat.report.graph;
public interface GraphBuilder {
public String build(GraphPayload payload);
}
package com.dianping.cat.report.graph;
public interface GraphPayload {
public String getAxisXLabel();
public String getAxisYLabel();
public int getColumns();
public String getDescription();
public int getHeight();
public int getMarginBottom();
public int getMarginLeft();
public int getMarginRight();
public int getMarginTop();
public int getRows();
public String getTitle();
public double[] getValues();
public int getWidth();
public int getDisplayHeight();
public int getDisplayWidth();
}
package com.dianping.cat.report.graph;
public interface ValueTranslater {
public int getMaxValue(double[] values);
public int[] translate(int height, int maxValue, double[] values);
}
package com.dianping.cat.report.page;
import java.util.Collection;
import java.util.Date;
import com.dianping.cat.report.ReportPage;
import com.dianping.cat.report.view.UrlNav;
import com.site.web.mvc.Action;
import com.site.web.mvc.ActionContext;
import com.site.web.mvc.ViewModel;
public abstract class AbstractReportModel<A extends Action, M extends ActionContext<?>> extends
ViewModel<ReportPage, A, M> {
private Throwable m_exception;
public AbstractReportModel(M ctx) {
super(ctx);
}
public String getBaseUri() {
return buildPageUri(getPage().getPath(), null);
}
// required by report tag
public Date getCurrentTime() {
return new Date();
}
// required by report tag
public abstract Collection<String> getDomains();
public Throwable getException() {
return m_exception;
}
public String getLogViewBaseUri() {
return buildPageUri(ReportPage.LOGVIEW.getPath(), null);
}
// required by report tag
public UrlNav[] getNavs() {
return UrlNav.values();
}
public void setException(Throwable exception) {
m_exception = exception;
}
}
package com.dianping.cat.report.page.model;
public enum Action implements com.site.web.mvc.Action {
XML("xml");
private String m_name;
private Action(String name) {
m_name = name;
}
public static Action getByName(String name, Action defaultAction) {
for (Action action : Action.values()) {
if (action.getName().equals(name)) {
return action;
}
}
return defaultAction;
}
@Override
public String getName() {
return m_name;
}
}
package com.dianping.cat.report.page.model;
import com.dianping.cat.report.ReportContext;
public class Context extends ReportContext<Payload> {
}
package com.dianping.cat.report.page.model;
import java.io.IOException;
import javax.servlet.ServletException;
import com.dianping.cat.report.ReportPage;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.dianping.cat.report.page.model.transaction.LocalTransactionModelService;
import com.site.lookup.ContainerHolder;
import com.site.lookup.annotation.Inject;
import com.site.web.mvc.PageHandler;
import com.site.web.mvc.annotation.InboundActionMeta;
import com.site.web.mvc.annotation.OutboundActionMeta;
import com.site.web.mvc.annotation.PayloadMeta;
public class Handler extends ContainerHolder implements PageHandler<Context> {
@Inject
private JspViewer m_jspViewer;
@Inject(type = ModelService.class, value = "transaction-local")
private LocalTransactionModelService m_transactionService;
@Override
@PayloadMeta(Payload.class)
@InboundActionMeta(name = "model")
public void handleInbound(Context ctx) throws ServletException, IOException {
// display only, no action here
}
@Override
@OutboundActionMeta(name = "model")
public void handleOutbound(Context ctx) throws ServletException, IOException {
Model model = new Model(ctx);
Payload payload = ctx.getPayload();
model.setAction(Action.XML);
model.setPage(ReportPage.MODEL);
try {
String report = payload.getReport();
String domain = payload.getDomain();
ModelRequest request = new ModelRequest(domain, payload.getPeriod());
ModelResponse<?> response = null;
if ("transaction".equals(report)) {
response = m_transactionService.invoke(request);
} else if ("failure".equals(report)) {
request.setProperty("ip", payload.getIp());
// response = m_failureService.invoke(request);
} else {
throw new RuntimeException("Unsupported report: " + report + "!");
}
Object dataModel = response.getModel();
model.setModel(dataModel);
model.setModelInXml(dataModel == null ? "" : String.valueOf(dataModel));
} catch (Throwable e) {
model.setException(e);
}
m_jspViewer.view(ctx, model);
}
}
package com.dianping.cat.report.page.model;
public enum JspFile {
VIEW("/jsp/report/model.jsp"),
;
private String m_path;
private JspFile(String path) {
m_path = path;
}
public String getPath() {
return m_path;
}
}
package com.dianping.cat.report.page.model;
import com.dianping.cat.report.ReportPage;
import com.site.web.mvc.view.BaseJspViewer;
public class JspViewer extends BaseJspViewer<ReportPage, Action, Context, Model> {
@Override
protected String getJspFilePath(Context ctx, Model model) {
Action action = model.getAction();
switch (action) {
case XML:
return JspFile.VIEW.getPath();
}
throw new RuntimeException("Unknown action: " + action);
}
}
package com.dianping.cat.report.page.model;
import com.dianping.cat.report.ReportPage;
import com.site.web.mvc.ViewModel;
public class Model extends ViewModel<ReportPage, Action, Context> {
private Throwable m_exception;
private Object m_model;
private String m_modelInXml;
public Model(Context ctx) {
super(ctx);
}
@Override
public Action getDefaultAction() {
return Action.XML;
}
public Throwable getException() {
return m_exception;
}
public Object getModel() {
return m_model;
}
public String getModelInXml() {
return m_modelInXml;
}
public void setException(Throwable exception) {
m_exception = exception;
}
public void setModel(Object model) {
m_model = model;
}
public void setModelInXml(String modelInXml) {
m_modelInXml = modelInXml;
}
}
package com.dianping.cat.report.page.model;
import com.dianping.cat.report.ReportPage;
import com.dianping.cat.report.page.model.spi.ModelPeriod;
import com.site.web.mvc.ActionContext;
import com.site.web.mvc.ActionPayload;
import com.site.web.mvc.payload.annotation.FieldMeta;
import com.site.web.mvc.payload.annotation.PathMeta;
public class Payload implements ActionPayload<ReportPage, Action> {
private ReportPage m_page;
@FieldMeta("op")
private Action m_action;
// /<report>/<domain>/<period>
@PathMeta("path")
private String[] m_path;
@FieldMeta("ip")
private String m_ip;
@Override
public Action getAction() {
return m_action;
}
public String getDomain() {
if (m_path.length > 1) {
return m_path[1];
} else {
return null;
}
}
public String getIp() {
return m_ip;
}
@Override
public ReportPage getPage() {
return m_page;
}
public ModelPeriod getPeriod() {
if (m_path.length > 2) {
return ModelPeriod.getByName(m_path[2], ModelPeriod.CURRENT);
} else {
return ModelPeriod.CURRENT;
}
}
public String getReport() {
if (m_path.length > 0) {
return m_path[0];
} else {
return null;
}
}
public void setAction(Action action) {
m_action = action;
}
public void setIp(String ip) {
m_ip = ip;
}
@Override
public void setPage(String page) {
m_page = ReportPage.getByName(page, ReportPage.MODEL);
}
public void setPath(String[] path) {
m_path = path;
}
@Override
public void validate(ActionContext<?> ctx) {
}
}
package com.dianping.cat.report.page.model.failure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import com.dianping.cat.consumer.failure.model.entity.FailureReport;
import com.dianping.cat.consumer.failure.model.transform.DefaultMerger;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.lookup.annotation.Inject;
public class CompositeFailureModelService implements ModelService<FailureReport>, Initializable {
@Inject
private List<ModelService<FailureReport>> m_services;
private ExecutorService m_threadPool;
@Override
public void initialize() throws InitializationException {
m_threadPool = Executors.newFixedThreadPool(10);
}
@Override
public ModelResponse<FailureReport> invoke(final ModelRequest request) {
int size = m_services.size();
final List<ModelResponse<FailureReport>> responses = new ArrayList<ModelResponse<FailureReport>>(size);
final CountDownLatch latch = new CountDownLatch(size);
for (final ModelService<FailureReport> service : m_services) {
m_threadPool.submit(new Runnable() {
@Override
public void run() {
try {
responses.add(service.invoke(request));
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
});
}
try {
latch.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// ignore it
}
ModelResponse<FailureReport> aggregated = new ModelResponse<FailureReport>();
DefaultMerger merger = null;
for (ModelResponse<FailureReport> response : responses) {
if (response != null) {
FailureReport model = response.getModel();
if (model != null) {
if (merger == null) {
merger = new DefaultMerger(model);
} else {
model.accept(merger);
}
}
}
}
aggregated.setModel(merger == null ? null : merger.getFailureReport());
return aggregated;
}
@Override
public boolean isEligable(ModelRequest request) {
for (ModelService<FailureReport> service : m_services) {
if (service.isEligable(request)) {
return true;
}
}
return false;
}
public void setSerivces(ModelService<FailureReport>... services) {
m_services = Arrays.asList(services);
}
}
package com.dianping.cat.report.page.model.failure;
import java.util.List;
import com.dianping.cat.consumer.RealtimeConsumer;
import com.dianping.cat.consumer.failure.FailureAnalyzer;
import com.dianping.cat.consumer.failure.model.entity.FailureReport;
import com.dianping.cat.message.spi.MessageConsumer;
import com.dianping.cat.report.page.model.spi.ModelPeriod;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.lookup.annotation.Inject;
public class LocalFailureModelService implements ModelService<FailureReport> {
@Inject(type = MessageConsumer.class, value = "realtime")
private RealtimeConsumer m_consumer;
@Override
public ModelResponse<FailureReport> invoke(ModelRequest request) {
FailureAnalyzer analyzer = getAnalyzer(request.getPeriod());
ModelResponse<FailureReport> response = new ModelResponse<FailureReport>();
if (analyzer != null) {
List<String> domains = analyzer.getDomains();
String d = request.getDomain();
FailureReport report = analyzer.getReport(d != null ? d : domains.isEmpty() ? null : domains.get(0));
if (report != null) {
for (String domain : domains) {
report.addDomain(domain);
}
}
response.setModel(report);
}
return response;
}
private FailureAnalyzer getAnalyzer(ModelPeriod period) {
if (period.isCurrent()) {
return (FailureAnalyzer) m_consumer.getCurrentAnalyzer("failure");
} else if (period.isLast()) {
return (FailureAnalyzer) m_consumer.getLastAnalyzer("failure");
} else {
return null;
}
}
@Override
public boolean isEligable(ModelRequest request) {
ModelPeriod period = request.getPeriod();
return period.isCurrent() || period.isLast();
}
}
package com.dianping.cat.report.page.model.failure;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import com.dianping.cat.consumer.failure.model.entity.FailureReport;
import com.dianping.cat.consumer.failure.model.transform.DefaultXmlParser;
import com.dianping.cat.report.page.model.spi.ModelPeriod;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.helper.Files;
import com.site.helper.Joiners;
import com.site.helper.Joiners.IBuilder;
import com.site.lookup.annotation.Inject;
public class RemoteFailureModelService implements ModelService<FailureReport> {
@Inject
private String m_host;
@Inject
private int m_port = 2281; // default admin port
@Inject
private String m_serviceUri = "/cat/r/model";
URL buildUrl(ModelRequest request) throws MalformedURLException {
String pairs = Joiners.by('&').prefixDelimiter()
.join(request.getProperties().entrySet(), new IBuilder<Map.Entry<String, String>>() {
@Override
public String asString(Entry<String, String> e) {
return e.getKey() + "=" + e.getValue();
}
});
String url = String.format("http://%s:%s%s/%s/%s/%s?op=xml%s", m_host, m_port, m_serviceUri, "failure",
request.getDomain(), request.getPeriod(), pairs);
return new URL(url);
}
@Override
public ModelResponse<FailureReport> invoke(ModelRequest request) {
ModelResponse<FailureReport> response = new ModelResponse<FailureReport>();
try {
URL url = buildUrl(request);
String xml = Files.forIO().readFrom(url.openStream(), "utf-8");
if (xml != null && xml.trim().length() > 0) {
FailureReport report = new DefaultXmlParser().parse(xml);
response.setModel(report);
}
} catch (Exception e) {
response.setException(e);
}
return response;
}
@Override
public boolean isEligable(ModelRequest request) {
ModelPeriod period = request.getPeriod();
return period.isCurrent() || period.isLast();
}
public void setHost(String host) {
m_host = host;
}
public void setPort(int port) {
m_port = port;
}
public void setServiceUri(String serviceUri) {
m_serviceUri = serviceUri;
}
}
package com.dianping.cat.report.page.model.spi;
public enum ModelPeriod {
HISTORICAL, LAST, CURRENT, FUTURE;
public static ModelPeriod getByName(String name, ModelPeriod defaultValue) {
for (ModelPeriod period : values()) {
if (period.name().equals(name)) {
return period;
}
}
return defaultValue;
}
public boolean isCurrent() {
return this == CURRENT;
}
public boolean isLast() {
return this == LAST;
}
public boolean isHistorical() {
return this == HISTORICAL;
}
public boolean isFuture() {
return this == FUTURE;
}
}
\ No newline at end of file
package com.dianping.cat.report.page.model.spi;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ModelRequest {
private String m_domain;
private ModelPeriod m_period;
private Map<String, String> m_properties;
public ModelRequest(String domain, ModelPeriod period) {
m_domain = domain;
m_period = period;
}
public String getDomain() {
return m_domain;
}
public ModelPeriod getPeriod() {
return m_period;
}
public Map<String, String> getProperties() {
if (m_properties == null) {
return Collections.emptyMap();
} else {
return m_properties;
}
}
public String getProperty(String name) {
if (m_properties == null) {
return null;
} else {
return m_properties.get(name);
}
}
public void setProperty(String name, String value) {
if (m_properties == null) {
m_properties = new HashMap<String, String>();
}
m_properties.put(name, value);
}
public static ModelRequest from(String domain, String period) {
ModelRequest request = new ModelRequest(domain, ModelPeriod.getByName(period, ModelPeriod.CURRENT));
return request;
}
}
package com.dianping.cat.report.page.model.spi;
public class ModelResponse<M> {
private Exception m_exception;
private M m_model;
public Exception getException() {
return m_exception;
}
public M getModel() {
return m_model;
}
public void setException(Exception exception) {
m_exception = exception;
}
public void setModel(M model) {
m_model = model;
}
}
package com.dianping.cat.report.page.model.spi;
public interface ModelService<M> {
public boolean isEligable(ModelRequest request);
public ModelResponse<M> invoke(ModelRequest request);
}
package com.dianping.cat.report.page.model.transaction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.lookup.annotation.Inject;
public class CompositeTransactionModelService implements ModelService<TransactionReport>, Initializable {
@Inject
private List<ModelService<TransactionReport>> m_services;
private ExecutorService m_threadPool;
@Override
public void initialize() throws InitializationException {
m_threadPool = Executors.newFixedThreadPool(10);
}
@Override
public ModelResponse<TransactionReport> invoke(final ModelRequest request) {
int size = m_services.size();
final List<ModelResponse<TransactionReport>> responses = new ArrayList<ModelResponse<TransactionReport>>(size);
final Semaphore semaphore = new Semaphore(0);
int count = 0;
for (final ModelService<TransactionReport> service : m_services) {
if (service.isEligable(request)) {
m_threadPool.submit(new Runnable() {
@Override
public void run() {
try {
responses.add(service.invoke(request));
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
});
count++;
}
}
try {
semaphore.tryAcquire(count, 5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// ignore it
}
ModelResponse<TransactionReport> aggregated = new ModelResponse<TransactionReport>();
TransactionReportMerger merger = null;
for (ModelResponse<TransactionReport> response : responses) {
if (response != null) {
TransactionReport model = response.getModel();
if (model != null) {
if (merger == null) {
merger = new TransactionReportMerger(model);
} else {
model.accept(merger);
}
}
}
}
aggregated.setModel(merger == null ? null : merger.getTransactionReport());
return aggregated;
}
@Override
public boolean isEligable(ModelRequest request) {
for (ModelService<TransactionReport> service : m_services) {
if (service.isEligable(request)) {
return true;
}
}
return false;
}
public void setSerivces(ModelService<TransactionReport>... services) {
m_services = Arrays.asList(services);
}
}
package com.dianping.cat.report.page.model.transaction;
import java.util.List;
import com.dianping.cat.consumer.RealtimeConsumer;
import com.dianping.cat.consumer.transaction.TransactionAnalyzer;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.message.spi.MessageConsumer;
import com.dianping.cat.report.page.model.spi.ModelPeriod;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.lookup.annotation.Inject;
public class LocalTransactionModelService implements ModelService<TransactionReport> {
@Inject(type = MessageConsumer.class, value = "realtime")
private RealtimeConsumer m_consumer;
@Override
public ModelResponse<TransactionReport> invoke(ModelRequest request) {
TransactionAnalyzer analyzer = getAnalyzer(request.getPeriod());
ModelResponse<TransactionReport> response = new ModelResponse<TransactionReport>();
if (analyzer != null) {
List<String> domains = analyzer.getDomains();
String d = request.getDomain();
TransactionReport report = analyzer.getReport(d != null ? d : domains.isEmpty() ? null : domains.get(0));
if (report != null) {
for (String domain : domains) {
report.addDomain(domain);
}
}
response.setModel(report);
}
return response;
}
private TransactionAnalyzer getAnalyzer(ModelPeriod period) {
if (period.isCurrent()) {
return (TransactionAnalyzer) m_consumer.getCurrentAnalyzer("transaction");
} else if (period.isLast()) {
return (TransactionAnalyzer) m_consumer.getLastAnalyzer("transaction");
} else {
return null;
}
}
@Override
public boolean isEligable(ModelRequest request) {
ModelPeriod period = request.getPeriod();
return period.isCurrent() || period.isLast();
}
}
package com.dianping.cat.report.page.model.transaction;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.consumer.transaction.model.transform.DefaultXmlParser;
import com.dianping.cat.report.page.model.spi.ModelPeriod;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.helper.Files;
import com.site.helper.Joiners;
import com.site.helper.Joiners.IBuilder;
import com.site.lookup.annotation.Inject;
public class RemoteTransactionModelService implements ModelService<TransactionReport> {
@Inject
private String m_host;
@Inject
private int m_port = 2281; // default admin port
@Inject
private String m_serviceUri = "/cat/r/model";
URL buildUrl(ModelRequest request) throws MalformedURLException {
String pairs = Joiners.by('&').prefixDelimiter()
.join(request.getProperties().entrySet(), new IBuilder<Map.Entry<String, String>>() {
@Override
public String asString(Entry<String, String> e) {
return e.getKey() + "=" + e.getValue();
}
});
String url = String.format("http://%s:%s%s/%s/%s/%s?op=xml%s", m_host, m_port, m_serviceUri, "transaction",
request.getDomain(), request.getPeriod(), pairs);
return new URL(url);
}
@Override
public ModelResponse<TransactionReport> invoke(ModelRequest request) {
ModelResponse<TransactionReport> response = new ModelResponse<TransactionReport>();
try {
URL url = buildUrl(request);
String xml = Files.forIO().readFrom(url.openStream(), "utf-8");
if (xml != null && xml.trim().length() > 0) {
TransactionReport report = new DefaultXmlParser().parse(xml);
response.setModel(report);
}
} catch (Exception e) {
response.setException(e);
}
return response;
}
@Override
public boolean isEligable(ModelRequest request) {
ModelPeriod period = request.getPeriod();
return period.isCurrent() || period.isLast();
}
public void setHost(String host) {
m_host = host;
}
public void setPort(int port) {
m_port = port;
}
public void setServiceUri(String serviceUri) {
m_serviceUri = serviceUri;
}
}
package com.dianping.cat.report.page.model.transaction;
import java.util.List;
import com.dianping.cat.consumer.transaction.model.entity.Duration;
import com.dianping.cat.consumer.transaction.model.entity.Range;
import com.dianping.cat.consumer.transaction.model.entity.TransactionName;
import com.dianping.cat.consumer.transaction.model.entity.TransactionReport;
import com.dianping.cat.consumer.transaction.model.entity.TransactionType;
import com.dianping.cat.consumer.transaction.model.transform.DefaultMerger;
public class TransactionReportMerger extends DefaultMerger {
public TransactionReportMerger(TransactionReport transactionReport) {
super(transactionReport);
}
public static TransactionReport merges(List<TransactionReport> reports) {
TransactionReportMerger merger = new TransactionReportMerger(new TransactionReport(""));
for (TransactionReport report : reports) {
report.accept(merger);
}
return merger.getTransactionReport();
}
@Override
protected void mergeDuration(Duration old, Duration duration) {
old.setCount(old.getCount() + duration.getCount());
}
@Override
protected void mergeName(TransactionName old, TransactionName other) {
old.setTotalCount(old.getTotalCount() + other.getTotalCount());
old.setFailCount(old.getFailCount() + other.getFailCount());
if (other.getMin() < old.getMin()) {
old.setMin(other.getMin());
}
if (other.getMax() > old.getMax()) {
old.setMax(other.getMax());
}
old.setSum(old.getSum() + other.getSum());
old.setSum2(old.getSum2() + other.getSum2());
if (old.getTotalCount() > 0) {
old.setFailPercent(old.getFailCount() * 100.0 / old.getTotalCount());
old.setAvg(old.getSum() / old.getTotalCount());
old.setStd(std(old.getTotalCount(), old.getAvg(), old.getSum2()));
}
if (old.getSuccessMessageUrl() == null) {
old.setSuccessMessageUrl(other.getSuccessMessageUrl());
}
if (old.getFailMessageUrl() == null) {
old.setFailMessageUrl(other.getFailMessageUrl());
}
}
@Override
protected void mergeRange(Range old, Range range) {
old.setCount(old.getCount() + range.getCount());
old.setFails(old.getFails() + range.getFails());
old.setSum(old.getSum() + range.getSum());
if (old.getCount() > 0) {
old.setAvg(old.getSum() / old.getCount());
}
}
public TransactionReport mergesFrom(TransactionReport report) {
report.accept(this);
return getTransactionReport();
}
@Override
protected void mergeTransactionReport(TransactionReport old, TransactionReport transactionReport) {
super.mergeTransactionReport(old, transactionReport);
old.getDomains().addAll(transactionReport.getDomains());
}
@Override
protected void mergeType(TransactionType old, TransactionType other) {
old.setTotalCount(old.getTotalCount() + other.getTotalCount());
old.setFailCount(old.getFailCount() + other.getFailCount());
if (other.getMin() < old.getMin()) {
old.setMin(other.getMin());
}
if (other.getMax() > old.getMax()) {
old.setMax(other.getMax());
}
old.setSum(old.getSum() + other.getSum());
old.setSum2(old.getSum2() + other.getSum2());
if (old.getTotalCount() > 0) {
old.setFailPercent(old.getFailCount() * 100.0 / old.getTotalCount());
old.setAvg(old.getSum() / old.getTotalCount());
old.setStd(std(old.getTotalCount(), old.getAvg(), old.getSum2()));
}
if (old.getSuccessMessageUrl() == null) {
old.setSuccessMessageUrl(other.getSuccessMessageUrl());
}
if (old.getFailMessageUrl() == null) {
old.setFailMessageUrl(other.getFailMessageUrl());
}
}
protected double std(long count, double ave, double sum2) {
return Math.sqrt(sum2 / count - 2 * ave * ave + ave * ave);
}
}
.transaction {
width: auto;
font-size: small;
}
.subtitle {
font-size: small;
}
.timestamp {
font-size: small;
}
.graph {
width: 400px;
height: 100px;
}
tr.odd td {
background-color: #eee;
font-size: small;
white-space: nowrap;
vertical-align: top;
}
tr.even td {
background-color: white;
font-size: small;
white-space: nowrap;
vertical-align: top;
}
tr.link td {
font-size: small;
white-space: nowrap;
vertical-align: top;
}
.current {
background-color: orange;
color: white;
}
.warn {
color: yellow;
}
.error {
color: red;
}
\ No newline at end of file
<%@ page contentType="text/xml; charset=utf-8" trimDirectiveWhitespaces="true" %>
<jsp:useBean id="ctx" type="com.dianping.cat.report.page.model.Context" scope="request"/>
<jsp:useBean id="payload" type="com.dianping.cat.report.page.model.Payload" scope="request"/>
<jsp:useBean id="model" type="com.dianping.cat.report.page.model.Model" scope="request"/>
${model.modelInXml}
\ No newline at end of file
<%@ page contentType="image/svg+xml; charset=utf-8"%>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="600" height="300" xmlns="http://www.w3.org/2000/svg">
<title>Duration</title>
<g id="coordinate" stroke="#003f7f" fill="white">
<path id="xy" d="M90,200 h480 m-480,0 v-150"/>
<path id="xy-2" d="M90,50 m480,0 v150" stroke-dasharray="1,5"/>
<path id="lines" d="M90,50 h480 m-480,30 h480 m-480,30 h480 m-480,30 h480 m-480,30 h480" stroke-dasharray="1,5"/>
<path id="ys" d="M90,50 h-6 m6,30 h-6 m6,30 h-6 m6,30 h-6 m6,30 h-6 m6,30 h-6"/>
<path id="xs" d="M90,200 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10 m20,-10 v6 m20,-6 v10"/>
</g>
<g id="yt" direction="rtl">
<text x="80" y="54">200</text>
<text x="80" y="84">160</text>
<text x="80" y="114">120</text>
<text x="80" y="144">80</text>
<text x="80" y="174">40</text>
<text x="80" y="204">0</text>
</g>
<g id="xt">
<text x="86" y="225">0</text>
<text x="126" y="225">2</text>
<text x="166" y="225">4</text>
<text x="206" y="225">6</text>
<text x="246" y="225">8</text>
<text x="282" y="225">10</text>
<text x="322" y="225">12</text>
<text x="362" y="225">14</text>
<text x="402" y="225">16</text>
<text x="442" y="225">18</text>
<text x="482" y="225">20</text>
<text x="522" y="225">22</text>
</g>
<g>
<text x="255" y="258" font-size="20" alignment-baseline="central">Bottom</text>
<text x="276" y="30" font-size="28">Title</text>
</g>
<g fill="#ff0000">
<rect x="90" y="140" width="19" height="60"/>
<text transform="rotate(-90,110,140) translate(4,-4)" x="110" y="140">60</text>
<rect x="110" y="170" width="19" height="30"/>
<text transform="rotate(-90,130,170) translate(4,-4)" x="130" y="170">30</text>
<rect x="130" y="150" width="19" height="50"/>
<text transform="rotate(-90,150,150) translate(4,-4)" x="150" y="150">50</text>
</g>
</svg>
\ No newline at end of file
<%@ page contentType="text/html; charset=utf-8"%>
<jsp:useBean id="ctx" type="com.dianping.cat.report.page.transaction.Context" scope="request" />
<jsp:useBean id="payload" type="com.dianping.cat.report.page.transaction.Payload" scope="request" />
<jsp:useBean id="model" type="com.dianping.cat.report.page.transaction.Model" scope="request" />
${model.graph}
<%-- <table class="graphs">
<tr>
<td><img src="?op=graph&domain=${payload.domain}&type=${payload.type}&name=${payload.name}&graph=1" class="graph"></td>
<td><img src="?op=graph&domain=${payload.domain}&type=${payload.type}&name=${payload.name}&graph=2" class="graph"></td>
</tr>
<tr>
<td><img src="?op=graph&domain=${payload.domain}&type=${payload.type}&name=${payload.name}&graph=3" class="graph"></td>
<td><img src="?op=graph&domain=${payload.domain}&type=${payload.type}&name=${payload.name}&graph=4" class="graph"></td>
</tr>
</table> --%>
\ No newline at end of file
package com.dianping.cat.report.graph;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.site.lookup.ComponentTestCase;
@RunWith(JUnit4.class)
public class ValueTranslaterTest extends ComponentTestCase {
@Test
public void test() throws Exception {
ValueTranslater translater = lookup(ValueTranslater.class);
double[] values = { 123, 456, 247, 473, 976, 236 };
Assert.assertEquals(1000, translater.getMaxValue(values));
}
}
package com.dianping.cat.report.page.model.transaction;
import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.dianping.cat.report.page.model.spi.ModelRequest;
import com.dianping.cat.report.page.model.spi.ModelResponse;
import com.dianping.cat.report.page.model.spi.ModelService;
import com.site.lookup.ComponentTestCase;
@RunWith(JUnit4.class)
public class TransactionModelServiceTest extends ComponentTestCase {
@Test
public void testLookup() throws Exception {
ModelService<?> local = lookup(ModelService.class, "transaction-local");
ModelService<?> localhost = lookup(ModelService.class, "transaction-localhost");
ModelService<?> composite = lookup(ModelService.class, "transaction");
Assert.assertEquals(LocalTransactionModelService.class, local.getClass());
Assert.assertEquals(RemoteTransactionModelService.class, localhost.getClass());
Assert.assertEquals(CompositeTransactionModelService.class, composite.getClass());
}
@Test
public void testLocal() throws Exception {
LocalTransactionModelService local = (LocalTransactionModelService) lookup(ModelService.class,
"transaction-local");
ModelResponse<?> response = local.invoke(ModelRequest.from("Cat", "CURRENT"));
Assert.assertEquals("null", String.valueOf(response.getModel())); // TODO try to mock up a real consumer for test
}
@Test
public void testRemote() throws Exception {
RemoteTransactionModelService remote = (RemoteTransactionModelService) lookup(ModelService.class,
"transaction-localhost");
ModelRequest request = ModelRequest.from("Cat", "CURRENT");
Assert.assertEquals("http://localhost:2281/cat/r/t/service?domain=Cat&period=CURRENT", remote.buildUrl(request).toString());
ModelResponse<?> response = remote.invoke(request);
Assert.assertEquals("null", String.valueOf(response.getModel())); // TODO start a test server, and do real stuff
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册