You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2018/09/24 12:56:27 UTC

[incubator-skywalking] branch 5.x updated: Add Undertow 2.x plugin (#1697)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch 5.x
in repository https://gitbox.apache.org/repos/asf/incubator-skywalking.git


The following commit(s) were added to refs/heads/5.x by this push:
     new e881421  Add Undertow 2.x plugin (#1697)
e881421 is described below

commit e881421d600093e014656d6651ec59deb5c3c7bf
Author: chenpengfei <la...@gmail.com>
AuthorDate: Mon Sep 24 20:56:20 2018 +0800

    Add Undertow 2.x plugin (#1697)
    
    * Add Undertow 2.x plugin
    
    * Undertow: Interceptor method executeRootHandler in class Connectors
---
 .../src/main/resources/component-libraries.yml     |   3 +
 .../network/trace/component/ComponentsDefine.java  |   5 +-
 apm-sniffer/apm-sdk-plugin/pom.xml                 |   1 +
 .../apm-sdk-plugin/undertow-plugins/pom.xml        |  44 +++++
 .../undertow-plugins/undertow-2.x-plugin/pom.xml   |  54 +++++++
 .../apm/plugin/undertow/v2x/Constants.java         |  27 ++++
 .../v2x/ExecuteRootHandlerInterceptor.java         |  77 +++++++++
 .../plugin/undertow/v2x/ForwardInterceptor.java    |  64 ++++++++
 .../RequestDispatcherImplInstrumentation.java      |  85 ++++++++++
 .../v2x/define/UndertowInstrumentation.java        |  65 ++++++++
 .../src/main/resources/skywalking-plugin.def       |  18 +++
 .../v2x/ExecuteRootHandlerInterceptorTest.java     | 179 +++++++++++++++++++++
 .../undertow/v2x/ForwardInterceptorTest.java       | 103 ++++++++++++
 docs/Supported-list.md                             |   1 +
 14 files changed, 725 insertions(+), 1 deletion(-)

diff --git a/apm-collector/apm-collector-boot/src/main/resources/component-libraries.yml b/apm-collector/apm-collector-boot/src/main/resources/component-libraries.yml
index 7291c7f..ab20b46 100644
--- a/apm-collector/apm-collector-boot/src/main/resources/component-libraries.yml
+++ b/apm-collector/apm-collector-boot/src/main/resources/component-libraries.yml
@@ -171,6 +171,9 @@ Elasticsearch:
 transport-client:
   id: 48
   languages: Java
+Undertow:
+  id: 49
+  languages: Java
 
 # .NET/.NET Core components
 # [3000, 4000) for C#/.NET only
diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
index 8c60442..f4ed8c0 100644
--- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -98,6 +98,8 @@ public class ComponentsDefine {
 
     public static final OfficialComponent TRANSPORT_CLIENT =  new OfficialComponent(48, "transport-client");
 
+    public static final OfficialComponent UNDERTOW =  new OfficialComponent(49, "Undertow");
+
     private static ComponentsDefine INSTANCE = new ComponentsDefine();
 
     private String[] components;
@@ -107,7 +109,7 @@ public class ComponentsDefine {
     }
 
     public ComponentsDefine() {
-        components = new String[49];
+        components = new String[50];
         addComponent(TOMCAT);
         addComponent(HTTPCLIENT);
         addComponent(DUBBO);
@@ -144,6 +146,7 @@ public class ComponentsDefine {
         addComponent(ACTIVEMQ_PRODUCER);
         addComponent(ACTIVEMQ_CONSUMER);
         addComponent(TRANSPORT_CLIENT);
+        addComponent(UNDERTOW);
     }
 
     private void addComponent(OfficialComponent component) {
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index 4b45c80..10b048e 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -59,6 +59,7 @@
         <module>sofarpc-plugin</module>
         <module>elasticsearch-5.x-plugin</module>
         <module>activemq-5.x-plugin</module>
+        <module>undertow-plugins</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml
new file mode 100644
index 0000000..20077b2
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.skywalking</groupId>
+        <artifactId>apm-sdk-plugin</artifactId>
+        <version>5.0.0-GA-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>undertow-plugins</artifactId>
+    <modules>
+        <module>undertow-2.x-plugin</module>
+    </modules>
+    <packaging>pom</packaging>
+
+    <name>undertow-plugins</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <sdk.plugin.related.dir>/..</sdk.plugin.related.dir>
+    </properties>
+</project>
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml
new file mode 100644
index 0000000..a42b68f
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/pom.xml
@@ -0,0 +1,54 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>undertow-plugins</artifactId>
+        <groupId>org.apache.skywalking</groupId>
+        <version>5.0.0-GA-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>apm-undertow-2.x-plugin</artifactId>
+    <packaging>jar</packaging>
+
+    <name>undertow-2.x-plugin</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <undertow.version>2.0.9.Final</undertow.version>
+        <servlet-api.version>3.1.0</servlet-api.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-core</artifactId>
+            <version>${undertow.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet-api.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/Constants.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/Constants.java
new file mode 100644
index 0000000..ac8b208
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/Constants.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x;
+
+/**
+ * @author chenpengfei
+ */
+public class Constants {
+    public static final String FORWARD_REQUEST_FLAG = "SW_FORWARD_REQUEST_FLAG";
+}
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptor.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptor.java
new file mode 100644
index 0000000..6afb43c
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptor.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x;
+
+import org.apache.skywalking.apm.agent.core.context.CarrierItem;
+import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.tag.Tags;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+
+import java.lang.reflect.Method;
+
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HeaderMap;
+
+/**
+ * @author chenpengfei
+ */
+public class ExecuteRootHandlerInterceptor implements StaticMethodsAroundInterceptor {
+
+    @Override
+    public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, MethodInterceptResult result) {
+        HttpServerExchange exchange = (HttpServerExchange) allArguments[1];
+
+        ContextCarrier contextCarrier = new ContextCarrier();
+        HeaderMap headers = exchange.getRequestHeaders();
+        CarrierItem next = contextCarrier.items();
+        while (next.hasNext()) {
+            next = next.next();
+            next.setHeadValue(headers.getFirst(next.getHeadKey()));
+        }
+        AbstractSpan span = ContextManager.createEntrySpan(exchange.getRequestPath(), contextCarrier);
+        Tags.URL.set(span, exchange.getRequestURL());
+        Tags.HTTP.METHOD.set(span, exchange.getRequestMethod().toString());
+        span.setComponent(ComponentsDefine.UNDERTOW);
+        SpanLayer.asHttp(span);
+    }
+
+    @Override
+    public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret) {
+        HttpServerExchange exchange = (HttpServerExchange) allArguments[1];
+
+        AbstractSpan span = ContextManager.activeSpan();
+        if (exchange.getStatusCode() >= 400) {
+            span.errorOccurred();
+            Tags.STATUS_CODE.set(span, Integer.toString(exchange.getStatusCode()));
+        }
+        ContextManager.stopSpan();
+        ContextManager.getRuntimeContext().remove(Constants.FORWARD_REQUEST_FLAG);
+        return ret;
+    }
+
+    @Override
+    public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Throwable t) {
+        ContextManager.activeSpan().errorOccurred().log(t);
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptor.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptor.java
new file mode 100644
index 0000000..3640d53
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptor.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author chenpengfei
+ */
+public class ForwardInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor {
+
+    @Override
+    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+        if (ContextManager.isActive()) {
+            AbstractSpan abstractTracingSpan = ContextManager.activeSpan();
+            Map<String, String> eventMap = new HashMap<String, String>();
+            eventMap.put("forward-url", objInst.getSkyWalkingDynamicField() == null ? "" : String.valueOf(objInst.getSkyWalkingDynamicField()));
+            abstractTracingSpan.log(System.currentTimeMillis(), eventMap);
+            ContextManager.getRuntimeContext().put(Constants.FORWARD_REQUEST_FLAG, true);
+        }
+    }
+
+    @Override
+    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
+
+        return ret;
+    }
+
+    @Override
+    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
+
+    }
+
+    @Override
+    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
+        objInst.setSkyWalkingDynamicField(allArguments[0]);
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/RequestDispatcherImplInstrumentation.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/RequestDispatcherImplInstrumentation.java
new file mode 100644
index 0000000..9ab7b04
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/RequestDispatcherImplInstrumentation.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
+import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+/**
+ * @author chenpengfei
+ */
+public class RequestDispatcherImplInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+    private static final String ENHANCE_CLASS = "io.undertow.servlet.spec.RequestDispatcherImpl";
+    private static final String ENHANCE_METHOD = "forward";
+    public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.undertow.v2x.ForwardInterceptor";
+
+    @Override
+    protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return new ConstructorInterceptPoint[] {
+            new ConstructorInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getConstructorMatcher() {
+                    return takesArgumentWithType(0, "java.lang.String");
+                }
+
+                @Override
+                public String getConstructorInterceptor() {
+                    return INTERCEPTOR_CLASS;
+                }
+            }
+        };
+    }
+
+    @Override
+    protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[] {
+            new InstanceMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    return named(ENHANCE_METHOD);
+                }
+
+                @Override
+                public String getMethodsInterceptor() {
+                    return INTERCEPTOR_CLASS;
+                }
+
+                @Override
+                public boolean isOverrideArgs() {
+                    return false;
+                }
+            }
+        };
+    }
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return byName(ENHANCE_CLASS);
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/UndertowInstrumentation.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/UndertowInstrumentation.java
new file mode 100644
index 0000000..7c27e92
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/undertow/v2x/define/UndertowInstrumentation.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+/**
+ * @author chenpengfei
+ */
+public class UndertowInstrumentation extends ClassStaticMethodsEnhancePluginDefine {
+
+    private static final String ENHANCE_CLASS = "io.undertow.server.Connectors";
+    private static final String ENHANCE_METHOD = "executeRootHandler";
+    private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.undertow.v2x.ExecuteRootHandlerInterceptor";
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return NameMatch.byName(ENHANCE_CLASS);
+    }
+
+    @Override
+    protected StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+        return new StaticMethodsInterceptPoint[] {
+            new StaticMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    return named(ENHANCE_METHOD);
+                }
+
+                @Override
+                public String getMethodsInterceptor() {
+                    return INTERCEPTOR_CLASS;
+                }
+
+                @Override public boolean isOverrideArgs() {
+                    return false;
+                }
+            }
+        };
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000..a6070c6
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+undertow-2.x-plugin=org.apache.skywalking.apm.plugin.undertow.v2x.define.RequestDispatcherImplInstrumentation
+undertow-2.x-plugin=org.apache.skywalking.apm.plugin.undertow.v2x.define.UndertowInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptorTest.java
new file mode 100644
index 0000000..9939425
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ExecuteRootHandlerInterceptorTest.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x;
+
+import org.apache.skywalking.apm.agent.core.context.SW3CarrierItem;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegmentRef;
+import org.apache.skywalking.apm.agent.core.context.util.KeyValuePair;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
+import org.apache.skywalking.apm.agent.test.helper.SegmentRefHelper;
+import org.apache.skywalking.apm.agent.test.helper.SpanHelper;
+import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.ServerConnection;
+import io.undertow.util.HeaderMap;
+import io.undertow.util.HttpString;
+
+import static org.apache.skywalking.apm.agent.test.tools.SpanAssert.assertComponent;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author chenpengfei
+ */
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class ExecuteRootHandlerInterceptorTest {
+
+    private ExecuteRootHandlerInterceptor executeRootHandlerInterceptor;
+
+    @SegmentStoragePoint
+    private SegmentStorage segmentStorage;
+
+    @Rule
+    public AgentServiceRule serviceRule = new AgentServiceRule();
+
+    @Mock
+    private HttpHandler httpHandler;
+    private HttpServerExchange exchange;
+    @Mock
+    ServerConnection serverConnection;
+    private HeaderMap requestHeaders = new HeaderMap();
+    private HeaderMap responseHeaders = new HeaderMap();
+
+    private Object[] arguments;
+    private Class[] argumentType;
+
+    @Mock
+    private MethodInterceptResult methodInterceptResult;
+
+    @Before
+    public void setUp() throws Exception {
+        executeRootHandlerInterceptor = new ExecuteRootHandlerInterceptor();
+        exchange = new HttpServerExchange(serverConnection, requestHeaders, responseHeaders, 0);
+        exchange.setRequestURI("/test/testRequestURL");
+        exchange.setRequestPath("/test/testRequestURL");
+        exchange.setDestinationAddress(new InetSocketAddress("localhost", 8080));
+        exchange.setRequestScheme("http");
+        exchange.setRequestMethod(HttpString.tryFromString("POST"));
+        arguments = new Object[]{httpHandler, exchange};
+        argumentType = new Class[]{httpHandler.getClass(), exchange.getClass()};
+    }
+
+    @Test
+    public void testWithoutSerializedContextData() throws Throwable {
+        executeRootHandlerInterceptor.beforeMethod(EnhancedInstance.class, null, arguments, argumentType, methodInterceptResult);
+        executeRootHandlerInterceptor.afterMethod(EnhancedInstance.class, null, arguments, argumentType, null);
+
+        assertThat(segmentStorage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+
+        assertHttpSpan(spans.get(0));
+    }
+
+    @Test
+    public void testWithSerializedContextData() throws Throwable {
+        requestHeaders.put(HttpString.tryFromString(SW3CarrierItem.HEADER_NAME), "1.234.111|3|1|1|#192.168.1.8:18002|#/portal/|#/testEntrySpan|#AQA*#AQA*Et0We0tQNQA*");
+
+        executeRootHandlerInterceptor.beforeMethod(EnhancedInstance.class, null, arguments, argumentType, methodInterceptResult);
+        executeRootHandlerInterceptor.afterMethod(EnhancedInstance.class, null, arguments, argumentType, null);
+
+        assertThat(segmentStorage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+
+        assertHttpSpan(spans.get(0));
+        assertTraceSegmentRef(traceSegment.getRefs().get(0));
+    }
+
+    @Test
+    public void testStatusCodeNotEquals200() throws Throwable {
+        exchange.setStatusCode(500);
+        executeRootHandlerInterceptor.beforeMethod(EnhancedInstance.class, null, arguments, argumentType, methodInterceptResult);
+        executeRootHandlerInterceptor.afterMethod(EnhancedInstance.class, null, arguments, argumentType, null);
+
+        Assert.assertThat(segmentStorage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+
+        assertThat(spans.size(), is(1));
+
+        List<KeyValuePair> tags = SpanHelper.getTags(spans.get(0));
+        assertThat(tags.size(), is(3));
+        assertThat(tags.get(2).getValue(), is("500"));
+
+        assertHttpSpan(spans.get(0));
+        assertThat(SpanHelper.getErrorOccurred(spans.get(0)), is(true));
+    }
+
+    @Test
+    public void testWithUndertowException() throws Throwable {
+        executeRootHandlerInterceptor.beforeMethod(EnhancedInstance.class, null, arguments, argumentType, methodInterceptResult);
+        executeRootHandlerInterceptor.handleMethodException(EnhancedInstance.class, null, arguments, argumentType, new RuntimeException());
+        executeRootHandlerInterceptor.afterMethod(EnhancedInstance.class, null, arguments, argumentType, null);
+
+        assertThat(segmentStorage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+
+        assertHttpSpan(spans.get(0));
+        List<LogDataEntity> logDataEntities = SpanHelper.getLogs(spans.get(0));
+        assertThat(logDataEntities.size(), is(1));
+        SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class);
+    }
+
+    private void assertTraceSegmentRef(TraceSegmentRef ref) {
+        assertThat(SegmentRefHelper.getEntryApplicationInstanceId(ref), is(1));
+        assertThat(SegmentRefHelper.getSpanId(ref), is(3));
+        assertThat(SegmentRefHelper.getTraceSegmentId(ref).toString(), is("1.234.111"));
+    }
+
+    private void assertHttpSpan(AbstractTracingSpan span) {
+        assertThat(span.getOperationName(), is("/test/testRequestURL"));
+        assertComponent(span, ComponentsDefine.UNDERTOW);
+        SpanAssert.assertTag(span, 0, "http://localhost:8080/test/testRequestURL");
+        assertThat(span.isEntry(), is(true));
+        SpanAssert.assertLayer(span, SpanLayer.HTTP);
+    }
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptorTest.java
new file mode 100644
index 0000000..0ca917a
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/undertow-plugins/undertow-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/undertow/v2x/ForwardInterceptorTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.undertow.v2x;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity;
+import org.apache.skywalking.apm.agent.core.context.util.KeyValuePair;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.agent.test.helper.SpanHelper;
+import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import java.util.List;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author chenpengfei
+ */
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class ForwardInterceptorTest {
+
+    private ForwardInterceptor forwardInterceptor;
+
+    @SegmentStoragePoint
+    private SegmentStorage segmentStorage;
+
+    @Rule
+    public AgentServiceRule serviceRule = new AgentServiceRule();
+
+    @Mock
+    ServletRequest request;
+    @Mock
+    ServletResponse response;
+
+    @Mock
+    private MethodInterceptResult methodInterceptResult;
+
+    @Mock
+    private EnhancedInstance enhancedInstance;
+
+    private Object[] arguments;
+    private Class[] argumentType;
+
+    @Before
+    public void setUp() throws Exception {
+        forwardInterceptor = new ForwardInterceptor();
+        when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn("http://localhost:8080/test/testRequestURL");
+        arguments = new Object[]{request, response};
+        argumentType = new Class[]{request.getClass(), response.getClass()};
+    }
+
+    @Test
+    public void testWithoutSerializedContextData() throws Throwable {
+        AbstractSpan span = ContextManager.createLocalSpan("/testForward");
+        forwardInterceptor.onConstruct(enhancedInstance, arguments);
+        forwardInterceptor.beforeMethod(enhancedInstance, null, arguments, argumentType, methodInterceptResult);
+        forwardInterceptor.afterMethod(enhancedInstance, null, arguments, argumentType, null);
+
+        List<LogDataEntity> logDataEntities = SpanHelper.getLogs(span);
+        assertThat(logDataEntities.size(), is(1));
+        List<KeyValuePair> logs = logDataEntities.get(0).getLogs();
+        assertThat(logs.size(), is(1));
+        assertThat(logs.get(0).getKey(), is("forward-url"));
+        assertThat(logs.get(0).getValue(), is("http://localhost:8080/test/testRequestURL"));
+
+        assertThat(ContextManager.getRuntimeContext().get(Constants.FORWARD_REQUEST_FLAG), CoreMatchers.<Object>is(true));
+    }
+}
\ No newline at end of file
diff --git a/docs/Supported-list.md b/docs/Supported-list.md
index 243d698..b2fc63e 100644
--- a/docs/Supported-list.md
+++ b/docs/Supported-list.md
@@ -9,6 +9,7 @@
   * [Resin](http://www.caucho.com/resin-4.0/) 3 (Optional¹)
   * [Resin](http://www.caucho.com/resin-4.0/) 4 (Optional¹)
   * [Jetty Server](http://www.eclipse.org/jetty/) 9
+  * [Undertow](http://undertow.io/)  2.0.0.Final -> 2.0.13.Final
 * HTTP Client
   * [Feign](https://github.com/OpenFeign/feign) 9.x
   * [Netflix Spring Cloud Feign](https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-starter-feign) 1.1.x, 1.2.x, 1.3.x