diff --git a/cat-client/pom.xml b/cat-client/pom.xml index 85ff0d7479..e9a7a9e53a 100644 --- a/cat-client/pom.xml +++ b/cat-client/pom.xml @@ -30,6 +30,12 @@ junit test + + com.github.wangzihaogithub + spring-boot-protocol + 2.2.9 + test + diff --git a/cat-client/src/main/java/com/dianping/cat/support/servlet/CatFilter.java b/cat-client/src/main/java/com/dianping/cat/support/servlet/CatFilter.java index b4967698ab..2f19e29d35 100644 --- a/cat-client/src/main/java/com/dianping/cat/support/servlet/CatFilter.java +++ b/cat-client/src/main/java/com/dianping/cat/support/servlet/CatFilter.java @@ -38,6 +38,7 @@ import com.dianping.cat.Cat; import com.dianping.cat.CatClientConstants; import com.dianping.cat.message.Message; +import com.dianping.cat.message.MessageTree; import com.dianping.cat.message.Transaction; import com.dianping.cat.message.context.TraceContextHelper; import com.dianping.cat.message.internal.DefaultTransaction; @@ -82,6 +83,7 @@ public void init(FilterConfig filterConfig) throws ServletException { } m_handlers.add(CatHandler.ENVIRONMENT); + m_handlers.add(CatHandler.ID_SETUP); m_handlers.add(CatHandler.LOG_SPAN); m_handlers.add(CatHandler.LOG_CLIENT_PAYLOAD); } @@ -104,6 +106,26 @@ public void handle(Context ctx) throws IOException, ServletException { } }, + ID_SETUP { + @Override + public void handle(Context ctx) throws IOException, ServletException { + HttpServletRequest req = ctx.getRequest(); + String id = req.getHeader("x-cat-id"); + String parentId = req.getHeader("x-cat-parent-id"); + String rootId = req.getHeader("x-cat-root-id"); + + if (id != null) { + MessageTree tree = TraceContextHelper.threadLocal().getMessageTree(); + + tree.setMessageId(id); + tree.setParentMessageId(parentId); + tree.setRootMessageId(rootId); + } + + ctx.handle(); + } + }, + LOG_CLIENT_PAYLOAD { @Override public void handle(Context ctx) throws IOException, ServletException { @@ -163,7 +185,7 @@ private void customizeStatus(Transaction t, HttpServletRequest req) { if (catStatus != null) { t.setStatus(catStatus.toString()); } else { - t.success(); + t.setStatus(Message.SUCCESS); } } @@ -232,19 +254,13 @@ public void handle(Context ctx) throws IOException, ServletException { }; } - protected static class Context { + private static class Context { private FilterChain m_chain; private List m_handlers; private int m_index; - private String m_rootId; - - private String m_parentId; - - private String m_id; - private HttpServletRequest m_request; private HttpServletResponse m_response; @@ -261,26 +277,10 @@ public Context(HttpServletRequest request, HttpServletResponse response, FilterC m_handlers = handlers; } - public String getId() { - return m_id; - } - - public String getParentId() { - return m_parentId; - } - public HttpServletRequest getRequest() { return m_request; } - public HttpServletResponse getResponse() { - return m_response; - } - - public String getRootId() { - return m_rootId; - } - public String getType() { return m_type; } @@ -299,18 +299,6 @@ public boolean isTop() { return m_top; } - public void setId(String id) { - m_id = id; - } - - public void setParentId(String parentId) { - m_parentId = parentId; - } - - public void setRootId(String rootId) { - m_rootId = rootId; - } - public void setTop(boolean top) { m_top = top; } @@ -320,7 +308,7 @@ public void setType(String type) { } } - protected static interface Handler { + private static interface Handler { public void handle(Context ctx) throws IOException, ServletException; } -} +} \ No newline at end of file diff --git a/cat-client/src/test/java/com/dianping/cat/message/MessageAssert.java b/cat-client/src/test/java/com/dianping/cat/message/MessageAssert.java index 011e74f4ae..4b3fe275d9 100644 --- a/cat-client/src/test/java/com/dianping/cat/message/MessageAssert.java +++ b/cat-client/src/test/java/com/dianping/cat/message/MessageAssert.java @@ -24,7 +24,7 @@ public static EventAssert event() { return new EventAssert((Event) message); } - public static AssertSupport event(String type) { + public static EventAssert eventBy(String type) { List types = new ArrayList(); for (MessageTree tree : new ArrayList(s_trees)) { @@ -50,6 +50,26 @@ public static HeaderAssert header() { return new HeaderAssert(tree); } + public static HeaderAssert headerByTransaction(String type) { + List types = new ArrayList(); + + for (MessageTree tree : new ArrayList(s_trees)) { + Message message = tree.getMessage(); + + if (message instanceof Transaction) { + if (message.getType().equals(type)) { + return new HeaderAssert(tree); + } else if (!types.contains(message.getType())) { + types.add(message.getType()); + } + } + } + + Assert.fail(String.format("No message tree(%s) found, but was %s!", type, types.toString())); + + return null; // this will NEVER be reached + } + public static void newTree(MessageTree tree) { s_trees.push(tree); } @@ -71,70 +91,60 @@ public static TransactionAssert transaction() { return new TransactionAssert((Transaction) message); } - public static MessageTreeAssert tree(String messageId) { - List messageIds = new ArrayList(); + public static TransactionAssert transactionBy(String type) { + List types = new ArrayList(); for (MessageTree tree : new ArrayList(s_trees)) { - messageIds.add(tree.getMessageId()); + Message message = tree.getMessage(); - if (tree.getMessageId().equals(messageId)) { - return new MessageTreeAssert(tree); + if (message instanceof Transaction) { + if (message.getType().equals(type)) { + return new TransactionAssert((Transaction) message); + } else if (!types.contains(message.getType())) { + types.add(message.getType()); + } } } - Assert.fail(String.format("No message tree(%s) found, but was %s!", messageId, messageIds.toString())); + Assert.fail(String.format("No transaction(%s) found, but was %s!", type, types.toString())); return null; // this will NEVER be reached } - public static class EventAssert extends AssertSupport { - private Event m_event; - - public EventAssert(Event event) { - super(event, "Event"); + public static MessageTreeAssert tree(String messageId) { + List messageIds = new ArrayList(); - m_event = event; - } + for (MessageTree tree : new ArrayList(s_trees)) { + messageIds.add(tree.getMessageId()); - public Event event() { - return m_event; + if (tree.getMessageId().equals(messageId)) { + return new MessageTreeAssert(tree); + } } - } - public static class HeaderAssert { - private MessageTree m_tree; + Assert.fail(String.format("No message tree(%s) found, but was %s!", messageId, messageIds.toString())); - public HeaderAssert(MessageTree tree) { - m_tree = tree; - } + return null; // this will NEVER be reached + } - public HeaderAssert domain(String domain) { - Assert.assertEquals("Domain mismatched!", domain, m_tree.getDomain()); - return this; - } + public static MessageTreeAssert treeByTransaction(String type) { + List types = new ArrayList(); - public HeaderAssert messageId(String messageId) { - Assert.assertEquals("Message id mismatched!", messageId, m_tree.getMessageId()); - return this; - } + for (MessageTree tree : new ArrayList(s_trees)) { + Message message = tree.getMessage(); - public HeaderAssert messageIdStartsWith(String messageIdPrefix) { - if (!m_tree.getMessageId().startsWith(messageIdPrefix)) { - Assert.fail(String.format("Message id %s does not start with %s!", m_tree.getMessageId(), messageIdPrefix)); + if (message instanceof Transaction) { + if (message.getType().equals(type)) { + return new MessageTreeAssert(tree); + } else if (!types.contains(message.getType())) { + types.add(message.getType()); + } } - - return this; } - public HeaderAssert parentMessageId(String parentMessageId) { - Assert.assertEquals("Parent message id mismatched!", parentMessageId, m_tree.getParentMessageId()); - return this; - } + Assert.fail(String.format("No message tree(%s) found, but was %s!", type, types.toString())); - public HeaderAssert rootMessageId(String rootMessageId) { - Assert.assertEquals("Root message id mismatched!", rootMessageId, m_tree.getRootMessageId()); - return this; - } + return null; // this will NEVER be reached } @SuppressWarnings("unchecked") @@ -197,6 +207,76 @@ public T type(String type) { Assert.assertEquals(format("%s type mismatched!", m_class), type, m_message.getType()); return (T) this; } + + public AssertSupport withData() { + Assert.assertNotNull("Message id is NULL!", m_message.getData()); + return this; + } + } + + public static class EventAssert extends AssertSupport { + private Event m_event; + + public EventAssert(Event event) { + super(event, "Event"); + + m_event = event; + } + + public Event event() { + return m_event; + } + } + + public static class HeaderAssert { + private MessageTree m_tree; + + public HeaderAssert(MessageTree tree) { + m_tree = tree; + } + + public HeaderAssert domain(String domain) { + Assert.assertEquals("Domain mismatched!", domain, m_tree.getDomain()); + return this; + } + + public HeaderAssert messageId(String messageId) { + Assert.assertEquals("Message id mismatched!", messageId, m_tree.getMessageId()); + return this; + } + + public HeaderAssert messageIdStartsWith(String messageIdPrefix) { + if (!m_tree.getMessageId().startsWith(messageIdPrefix)) { + Assert.fail(String.format("Message id %s does not start with %s!", m_tree.getMessageId(), messageIdPrefix)); + } + + return this; + } + + public HeaderAssert parentMessageId(String parentMessageId) { + Assert.assertEquals("Parent message id mismatched!", parentMessageId, m_tree.getParentMessageId()); + return this; + } + + public HeaderAssert rootMessageId(String rootMessageId) { + Assert.assertEquals("Root message id mismatched!", rootMessageId, m_tree.getRootMessageId()); + return this; + } + + public HeaderAssert withMessageId() { + Assert.assertNotNull("Message id is NULL!", m_tree.getMessageId()); + return this; + } + + public HeaderAssert withParentMessageId() { + Assert.assertNotNull("Message id is NULL!", m_tree.getParentMessageId()); + return this; + } + + public HeaderAssert withRootMessageId() { + Assert.assertNotNull("Message id is NULL!", m_tree.getRootMessageId()); + return this; + } } public static class MessageTreeAssert { diff --git a/cat-client/src/test/java/com/dianping/cat/support/servlet/CatFilterTest.java b/cat-client/src/test/java/com/dianping/cat/support/servlet/CatFilterTest.java index 55ded29890..77eb181d1a 100644 --- a/cat-client/src/test/java/com/dianping/cat/support/servlet/CatFilterTest.java +++ b/cat-client/src/test/java/com/dianping/cat/support/servlet/CatFilterTest.java @@ -21,169 +21,172 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; +import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.junit.After; import org.junit.Assert; -import org.junit.Ignore; +import org.junit.Before; import org.junit.Test; import com.dianping.cat.Cat; import com.dianping.cat.message.Message; +import com.dianping.cat.message.MessageAssert; +import com.dianping.cat.message.MessageAssert.HeaderAssert; +import com.dianping.cat.message.MessageAssert.TransactionAssert; +import com.dianping.cat.message.MessageTree; import com.dianping.cat.message.Transaction; import com.dianping.cat.message.context.TraceContextHelper; +import com.dianping.cat.message.pipeline.MessageHandler; +import com.dianping.cat.message.pipeline.MessageHandlerAdaptor; +import com.dianping.cat.message.pipeline.MessageHandlerContext; import com.dianping.cat.support.Files; import com.dianping.cat.support.Urls; +import com.dianping.cat.support.Urls.UrlIO; +import com.github.netty.StartupServer; +import com.github.netty.protocol.HttpServletProtocol; +import com.github.netty.protocol.servlet.ServletContext; -@Ignore public class CatFilterTest { - // @After - // public void after() throws Exception { - // super.stopServer(); - // } - // - // @Before - // public void before() throws Exception { - // System.setProperty("devMode", "true"); - // super.startServer(); - // } - // - // @Override - // protected String getContextPath() { - // return "/mock"; - // } - // - // @Override - // protected int getServerPort() { - // return 2282; - // } - // - // @Override - // protected boolean isWebXmlDefined() { - // return false; - // } - // - // @Override - // protected void postConfigure(WebAppContext context) { - // context.addServlet(MockServlet.class, "/*"); - // context.addFilter(CatFilter.class, "/*", Handler.REQUEST); - // } + private HttpServer m_server; + + @After + public void after() { + m_server.close(); + MessageAssert.reset(); + } + + @Before + public void before() { + // Cat.getBootstrap().testMode(); + Cat.getBootstrap().getComponentContext().registerComponent(MessageHandler.class, new MockMessageHandler()); + Cat.getBootstrap().initializeByDomain("mockApp"); + + m_server = new HttpServer(); + m_server.start(); + } + + private String httpCall(String uri, String... headers) throws IOException { + String url = "http://localhost:2282" + uri; + UrlIO u = Urls.forIO().connectTimeout(1000); + Map> responseHeaders = new HashMap>(); + + for (int i = 0; i < headers.length; i += 2) { + u.header(headers[i], headers[i + 1]); + } + + InputStream in = u.openStream(url, responseHeaders); + String content = Files.forIO().readFrom(in, "utf-8"); + + return content; + } @Test public void testMode0() throws Exception { - String url = "http://localhost:2282/mock/mode0"; - InputStream in = Urls.forIO().openStream(url); - String content = Files.forIO().readFrom(in, "utf-8"); + Assert.assertEquals("/mock/mode0", httpCall("/mock/mode0?k1=v1&k2=v2")); - Assert.assertEquals("mock content here!", content); + TransactionAssert ta = MessageAssert.transaction().type("URL").name("/mock/mode0").success().complete(); - TimeUnit.MILLISECONDS.sleep(100); + ta.childEvent(0).type("URL").name("URL.Server"); + ta.childEvent(1).type("URL").name("URL.Method").data("HTTP/GET /mock/mode0?k1=v1&k2=v2"); + ta.childTransaction(0).type("MockServlet").name("/mode0"); } @Test public void testMode1() throws Exception { - String url = "http://localhost:2282/mock/mode1"; - Transaction t = Cat.newTransaction("Mock", "testMode1"); + Transaction t = Cat.newTransaction("FilterTest", "testMode1"); + String id = TraceContextHelper.threadLocal().getMessageTree().getMessageId(); + String childId = TraceContextHelper.createMessageId(); - try { - String childId = TraceContextHelper.createMessageId(); - String id = TraceContextHelper.threadLocal().getMessageTree().getMessageId(); + Cat.logEvent("RemoteCall", "/mock/mode1", Message.SUCCESS, childId); - Cat.logEvent("RemoteCall", url, Message.SUCCESS, childId); + Assert.assertEquals("/mock/mode1", httpCall("/mock/mode1?k1=v1&k2=v2", // + "x-cat-id", childId, // + "x-cat-parent-id", id, // + "x-cat-root-id", id // + )); - InputStream in = Urls.forIO().connectTimeout(100) // - .header("X-Cat-Id", childId) // - .header("X-Cat-Parent-Id", id) // - .header("X-Cat-Root-Id", id) // - .openStream(url); - String content = Files.forIO().readFrom(in, "utf-8"); + t.success().complete(); - Assert.assertEquals("mock content here!", content); + // child message tree + { + HeaderAssert ha = MessageAssert.headerByTransaction("URL"); - t.setStatus(Message.SUCCESS); - } finally { - t.complete(); - } + ha.withMessageId().withParentMessageId().withRootMessageId(); - TimeUnit.MILLISECONDS.sleep(100); - } + TransactionAssert ta = MessageAssert.transactionBy("URL").name("/mock/mode1").success().complete(); - @Test - public void testMode2() throws Exception { - String url = "http://localhost:2282/mock/mode2"; - Map> headers = new HashMap>(); - InputStream in = Urls.forIO().connectTimeout(100) // - .header("X-Cat-Source", "container") // - .header("X-CAT-TRACE-MODE", "true") // - .openStream(url, headers); - String content = Files.forIO().readFrom(in, "utf-8"); + ta.childEvent(0).type("URL").name("URL.Server"); + ta.childEvent(1).type("URL").name("URL.Method").data("HTTP/GET /mock/mode1?k1=v1&k2=v2"); + ta.childTransaction(0).type("MockServlet").name("/mode1"); + } - Assert.assertEquals("mock content here!", content); + // parent message tree + { + HeaderAssert ha = MessageAssert.headerByTransaction("FilterTest"); - String id = getHeader(headers, "X-CAT-ID"); - String parentId = getHeader(headers, "X-CAT-PARENT-ID"); - String rootId = getHeader(headers, "X-CAT-ROOT-ID"); + ha.withMessageId(); - Assert.assertNotNull(id); - Assert.assertNotNull(parentId); - Assert.assertNotNull(rootId); - Assert.assertFalse(id.equals(rootId)); + TransactionAssert ta = MessageAssert.transactionBy("FilterTest").name("testMode1").success().complete(); - TimeUnit.MILLISECONDS.sleep(100); + ta.childEvent(0).type("RemoteCall").name("/mock/mode1").withData(); + } } - private String getHeader(Map> headers, String name) { - List values = headers.get(name); + private class HttpServer extends StartupServer { + public HttpServer() { + super(2282); + + ServletContext servletContext = new ServletContext(); + EnumSet types = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD); + + servletContext.addFilter("cat-filter", new CatFilter()) // + .addMappingForUrlPatterns(types, false, "/*"); + servletContext.addServlet("mock-servlet", new MockServlet()) // + .addMapping("/mock/*"); - if (values != null) { - int len = values.size(); + HttpServletProtocol protocol = new HttpServletProtocol(servletContext); + protocol.setMaxBufferBytes(1024 * 1024); - if (len == 0) { - return null; - } else if (len == 1) { - return values.get(0); - } else { - StringBuilder sb = new StringBuilder(); - boolean first = true; + getProtocolHandlers().add(protocol); + getServerListeners().add(protocol); + } + } - for (String value : values) { - if (first) { - first = false; - } else { - sb.append(','); - } + private static class MockMessageHandler extends MessageHandlerAdaptor { + @Override + public int getOrder() { + return 0; + } - sb.append(value); - } + @Override + protected void handleMessagreTree(MessageHandlerContext ctx, MessageTree tree) { + MessageAssert.newTree(tree); - return sb.toString(); - } - } else { - return null; + System.out.println(tree); } } - public static class MockServlet extends HttpServlet { + private static class MockServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { PrintWriter writer = res.getWriter(); - Transaction t = Cat.newTransaction("Mock", req.getRequestURI()); + Transaction t = Cat.newTransaction("MockServlet", req.getPathInfo()); try { - writer.write("mock content here!"); - - // no status set by purpose + writer.write(req.getRequestURI()); } finally { - t.complete(); + t.success().complete(); } } }