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 2019/09/18 13:43:27 UTC

[skywalking] branch master updated: Add Light4j plugin (#3323)

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

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 72f07b8  Add Light4j plugin (#3323)
72f07b8 is described below

commit 72f07b812a5d0f7a856b23c1ee8c3843db22e5ce
Author: Louis Tsui <lo...@live.ca>
AuthorDate: Wed Sep 18 09:43:17 2019 -0400

    Add Light4j plugin (#3323)
    
    * Add Light4J 1.6.x plug-in
    
    * Add unit test for Light4J plug-in
    
    * Add missing id to server-starter
    
    * Remove mentions of version 1.6.x
    
    Some local testing showed that the plugin successfully generated traces for a light4j project built with light-codegen v2.x. Therefore, there is no need for a separate plugin for v2.x and no distinction needs to be made with light4j v1.6.x.
    
    * Update supported light4j versions
    
    * Use ContextSnapshot to maintain trace
    
    In a typical Light4j project, the ExceptionHandler#handleRequest is the asynchronous execution point. The flow is ExceptionHandler#handleRequest -> HttpServerExchange#dispatch -> ... -> ExceptionHandler#handleRequest. This means that handleRequest is being intercepted twice and each time a LocalSpan is created. Without handling thread propagation, the trace segment gets split up.
    
    By saving a ContextSnapshot into the enhanced object (ExceptionHandler), we can determine if it is necessary to continue the segment.
    
    * Update logic for span creation in trace
    
    By default, the Light4J plugin now overrides the Undertow plugin's entry span with its own. This new entry span is created just before execution of the ExceptionHandler#handleRequest in the I/O thread. The entry span is generated here because it is considered the starting point of the Light4J handler chain.
    
    There is now also the option to enable detailed span creation when tracing requests through Light4J. These local spans mark which middleware and business handlers are involved with handling the HTTP request to a Light4J service.
    
    * Update skywalking version
    
    * Update light4j version
    
    * Fix LightInstrumentation javadoc
    
    * Remove import violating checkstyle
    
    This import was added from the link reference in the javadoc. The javadoc has now been changed to just reference the method name.
    
    * Move light4j agent config property
    
    * Sync UI
---
 .../network/trace/component/ComponentsDefine.java  |   2 +
 .../skywalking/apm/agent/core/conf/Config.java     |   8 ++
 .../light4j-plugins/light4j-plugin/pom.xml         |  51 ++++++++
 .../plugin/light4j/HandleRequestInterceptor.java   | 141 +++++++++++++++++++++
 .../light4j/define/LightInstrumentation.java       |  73 +++++++++++
 .../src/main/resources/skywalking-plugin.def       |  17 +++
 .../light4j/HandleRequestInterceptorTest.java      |  85 +++++++++++++
 apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml |  42 ++++++
 apm-sniffer/apm-sdk-plugin/pom.xml                 |   1 +
 docs/en/setup/service-agent/java-agent/README.md   |   1 +
 .../service-agent/java-agent/Supported-list.md     |   1 +
 .../src/test/resources/component-libraries.yml     |   3 +
 .../src/main/resources/component-libraries.yml     |   3 +
 skywalking-ui                                      |   2 +-
 14 files changed, 429 insertions(+), 1 deletion(-)

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 0105cea..c79b6ea 100755
--- 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
@@ -134,4 +134,6 @@ public class ComponentsDefine {
 
     public static final OfficialComponent CASSANDRA_JAVA_DRIVER = new OfficialComponent(69, "cassandra-java-driver");
 
+    public static final OfficialComponent LIGHT_4J = new OfficialComponent(71, "Light4J");
+
 }
diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
index ee28cfb..5a8eca4 100755
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
@@ -287,5 +287,13 @@ public class Config {
                 public static Map<String, String> RULE = new HashMap<String, String>();
             }
         }
+
+        public static class Light4J {
+            /**
+             * If true, trace all middleware/business handlers that are part of the Light4J handler chain for a request,
+             * generating a local span for each.
+             */
+            public static boolean TRACE_HANDLER_CHAIN = false;
+        }
     }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml
new file mode 100644
index 0000000..8fe7cd8
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/pom.xml
@@ -0,0 +1,51 @@
+<!--
+  ~ 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>light4j-plugins</artifactId>
+        <groupId>org.apache.skywalking</groupId>
+        <version>6.5.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>apm-light4j-plugin</artifactId>
+    <packaging>jar</packaging>
+
+    <name>light4j-plugin</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <version.light4j>1.6.9</version.light4j>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>handler</artifactId>
+            <version>${version.light4j}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.networknt</groupId>
+            <artifactId>exception</artifactId>
+            <version>${version.light4j}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptor.java
new file mode 100644
index 0000000..b1c7d37
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptor.java
@@ -0,0 +1,141 @@
+/*
+ * 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.light4j;
+
+import com.networknt.exception.ExceptionHandler;
+import com.networknt.handler.MiddlewareHandler;
+import com.networknt.handler.OrchestrationHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HeaderMap;
+import org.apache.skywalking.apm.agent.core.conf.Config;
+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.ContextSnapshot;
+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.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+
+import java.lang.reflect.Method;
+
+/**
+ * {@link HandleRequestInterceptor} creates an entry span before the execution of
+ * {@link com.networknt.exception.ExceptionHandler#handleRequest(HttpServerExchange)} in the I/O thread.
+ *
+ * If the {@link Config.Plugin.Light4J#TRACE_HANDLER_CHAIN} flag is set, additionally a local span is produced for each
+ * {@link com.networknt.handler.MiddlewareHandler} and business handler before their respective
+ * {@link com.networknt.handler.LightHttpHandler#handleRequest(HttpServerExchange)} method executes.
+ * Since {@link com.networknt.handler.LightHttpHandler} is implemented by various middleware and business handlers and
+ * the Light4J framework delegates to these in succession, a chain of
+ * {@link org.apache.skywalking.apm.agent.core.context.trace.LocalSpan}s will be produced.
+ *
+ * @author tsuilouis
+ */
+public class HandleRequestInterceptor implements InstanceMethodsAroundInterceptor {
+
+    @Override
+    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
+                             MethodInterceptResult result) {
+        if (isExceptionHandler(objInst)) {
+            HttpServerExchange exchange = (HttpServerExchange) allArguments[0];
+
+            if (exchange.isInIoThread()) {
+
+                String operationName = exchange.getRequestPath() + "@" + exchange.getRequestMethod();
+                final HeaderMap headers = exchange.getRequestHeaders();
+                final ContextCarrier contextCarrier = new ContextCarrier();
+
+                CarrierItem next = contextCarrier.items();
+                while (next.hasNext()) {
+                    next = next.next();
+                    next.setHeadValue(headers.getFirst(next.getHeadKey()));
+                }
+
+                AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
+                Tags.URL.set(span, exchange.getRequestURL());
+                Tags.HTTP.METHOD.set(span, exchange.getRequestMethod().toString());
+                span.setComponent(ComponentsDefine.LIGHT_4J);
+                SpanLayer.asHttp(span);
+
+                if (exchange.getStatusCode() >= 400) {
+                    span.errorOccurred();
+                    Tags.STATUS_CODE.set(span, String.valueOf(exchange.getStatusCode()));
+                }
+
+                ContextManager.stopSpan(span);
+
+                objInst.setSkyWalkingDynamicField(ContextManager.capture());
+            } else if (Config.Plugin.Light4J.TRACE_HANDLER_CHAIN) {
+                String operationName = objInst.getClass().getName() + "." + method.getName();
+
+                ContextSnapshot snapshot = (ContextSnapshot) objInst.getSkyWalkingDynamicField();
+                ContextManager.createLocalSpan(operationName)
+                        .setComponent(ComponentsDefine.LIGHT_4J);
+
+                ContextManager.continued(snapshot);
+            }
+        } else if (Config.Plugin.Light4J.TRACE_HANDLER_CHAIN &&
+            (isMiddlewareHandler(objInst) || isBusinessHandler(objInst))) {
+            String operationName = objInst.getClass().getName() + "." + method.getName();
+
+            ContextManager.createLocalSpan(operationName)
+                    .setComponent(ComponentsDefine.LIGHT_4J);
+        }
+    }
+
+    @Override
+    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
+                              Object ret) {
+        if (isExceptionHandler(objInst)) {
+            HttpServerExchange exchange = (HttpServerExchange) allArguments[0];
+
+            if (Config.Plugin.Light4J.TRACE_HANDLER_CHAIN && !exchange.isInIoThread()) {
+                ContextManager.stopSpan();
+            }
+        } else if (Config.Plugin.Light4J.TRACE_HANDLER_CHAIN &&
+                (isMiddlewareHandler(objInst) || isBusinessHandler(objInst))) {
+            ContextManager.stopSpan();
+        }
+        return ret;
+    }
+
+    @Override
+    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+                                      Class<?>[] argumentsTypes,
+                                      Throwable t) {
+        ContextManager.activeSpan().errorOccurred().log(t);
+    }
+
+    private boolean isBusinessHandler(EnhancedInstance objInst) {
+        return !objInst.getClass().getInterfaces()[0].equals(MiddlewareHandler.class) && !objInst.getClass().equals(OrchestrationHandler.class);
+    }
+
+    private boolean isMiddlewareHandler(EnhancedInstance objInst) {
+        return objInst.getClass().getInterfaces()[0].equals(MiddlewareHandler.class);
+    }
+
+    private boolean isExceptionHandler(EnhancedInstance objInst) {
+        return objInst.getClass().equals(ExceptionHandler.class);
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/define/LightInstrumentation.java b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/define/LightInstrumentation.java
new file mode 100644
index 0000000..3f2101f
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/java/org/apache/skywalking/apm/plugin/light4j/define/LightInstrumentation.java
@@ -0,0 +1,73 @@
+/*
+ * 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.light4j.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+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 org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch;
+
+/**
+ * This instrumentation is applied to the handleRequest method of {@link com.networknt.handler.LightHttpHandler}
+ * using {@link org.apache.skywalking.apm.plugin.light4j.HandleRequestInterceptor}.
+ *
+ * @author tsuilouis
+ */
+public class LightInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+    private static final String ENHANCE_CLASS = "com.networknt.handler.LightHttpHandler";
+    private static final String ENHANCE_METHOD = "handleRequest";
+    private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.light4j.HandleRequestInterceptor";
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return HierarchyMatch.byHierarchyMatch(new String[] {ENHANCE_CLASS});
+    }
+
+    @Override
+    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return null;
+    }
+
+    @Override
+    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[] {
+            new InstanceMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    return ElementMatchers.named(ENHANCE_METHOD);
+                }
+
+                @Override
+                public String getMethodsInterceptor() {
+                    return INTERCEPTOR_CLASS;
+                }
+
+                @Override
+                public boolean isOverrideArgs() {
+                    return false;
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000..8305555
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,17 @@
+# 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.
+
+light4j=org.apache.skywalking.apm.plugin.light4j.define.LightInstrumentation
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/test/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/test/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptorTest.java
new file mode 100644
index 0000000..43d6418
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/light4j-plugin/src/test/java/org/apache/skywalking/apm/plugin/light4j/HandleRequestInterceptorTest.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.light4j;
+
+import com.networknt.exception.ExceptionHandler;
+import io.undertow.server.HttpServerExchange;
+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.tools.*;
+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.lang.reflect.Method;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author tsuilouis
+ */
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class HandleRequestInterceptorTest {
+
+    private HandleRequestInterceptor handleRequestInterceptor;
+
+    @SegmentStoragePoint
+    private SegmentStorage segmentStorage;
+
+    @Rule
+    public AgentServiceRule serviceRule = new AgentServiceRule();
+
+    @Mock
+    private MethodInterceptResult methodInterceptResult;
+
+    private EnhancedInstance enhancedInstance;
+
+    @Before
+    public void setUp() throws Exception {
+        handleRequestInterceptor = new HandleRequestInterceptor();
+
+        enhancedInstance = new EnhancedInstance() {
+            @Override
+            public Object getSkyWalkingDynamicField() {
+                return null;
+            }
+
+            @Override
+            public void setSkyWalkingDynamicField(Object value) {
+
+            }
+        };
+    }
+
+    @Test
+    public void testHandleRequest() throws Throwable {
+        Method method = ExceptionHandler.class.getMethod("handleRequest", HttpServerExchange.class);
+
+        handleRequestInterceptor.beforeMethod(enhancedInstance, method, null, null, methodInterceptResult);
+        handleRequestInterceptor.afterMethod(enhancedInstance, null, null, null, null);
+
+        assertThat(segmentStorage.getTraceSegments().size(), is(0));
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml
new file mode 100644
index 0000000..c880e2f
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/light4j-plugins/pom.xml
@@ -0,0 +1,42 @@
+<?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>6.5.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>light4j-plugins</artifactId>
+    <modules>
+        <module>light4j-plugin</module>
+    </modules>
+    <packaging>pom</packaging>
+
+    <name>light4j-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/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index f3be935..886dadb 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -75,6 +75,7 @@
         <module>resteasy-plugin</module>
         <module>solrj-7.x-plugin</module>
         <module>cassandra-java-driver-3.x-plugin</module>
+        <module>light4j-plugins</module>
     </modules>
     <packaging>pom</packaging>
 
diff --git a/docs/en/setup/service-agent/java-agent/README.md b/docs/en/setup/service-agent/java-agent/README.md
index 6f4f27a..e3fa37d 100755
--- a/docs/en/setup/service-agent/java-agent/README.md
+++ b/docs/en/setup/service-agent/java-agent/README.md
@@ -102,6 +102,7 @@ property key | Description | Default |
 `plugin.mysql.sql_parameters_max_length`|If set to positive number, the `db.sql.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.|`512`|
 `plugin.solrj.trace_statement`|If true, trace all the query parameters(include deleteByIds and deleteByQuery) in Solr query request, default is false.|`false`|
 `plugin.solrj.trace_ops_params`|If true, trace all the operation parameters in Solr request, default is false.|`false`|
+`plugin.light4j.trace_handler_chain`|If true, trace all middleware/business handlers that are part of the Light4J handler chain for a request.|false|
 `plugin.opgroup.*`|Support operation name customize group rules in different plugins. Read [Group rule supported plugins](op_name_group_rule.md)|Not set|
 
 ## Optional Plugins
diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md
index b04758b..51ddb54 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -13,6 +13,7 @@
   * [Undertow](http://undertow.io/)  2.0.0.Final -> 2.0.13.Final
   * [RESTEasy](https://resteasy.github.io/)  3.1.0.Final -> 3.7.0.Final
   * [Play Framework](https://www.playframework.com/) 2.6.x -> 2.7.x (Optional²)
+  * [Light4J Microservices Framework](https://doc.networknt.com/) 1.6.x -> 2.x
 * 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
diff --git a/oap-server/server-core/src/test/resources/component-libraries.yml b/oap-server/server-core/src/test/resources/component-libraries.yml
index a2b6568..bdcf6c2 100755
--- a/oap-server/server-core/src/test/resources/component-libraries.yml
+++ b/oap-server/server-core/src/test/resources/component-libraries.yml
@@ -221,6 +221,9 @@ cassandra-java-driver:
 Cassandra:
   id: 70
   languages: Java
+Light4J:
+  id: 71
+  languages: Java
 
 # .NET/.NET Core components
 # [3000, 4000) for C#/.NET only
diff --git a/oap-server/server-starter/src/main/resources/component-libraries.yml b/oap-server/server-starter/src/main/resources/component-libraries.yml
index 711a3ba..8846376 100755
--- a/oap-server/server-starter/src/main/resources/component-libraries.yml
+++ b/oap-server/server-starter/src/main/resources/component-libraries.yml
@@ -239,6 +239,9 @@ cassandra-java-driver:
 Cassandra:
   id: 70
   languages: Java
+Light4J:
+  id: 71
+  languages: Java
 
 
 # .NET/.NET Core components
diff --git a/skywalking-ui b/skywalking-ui
index 05556dc..206a514 160000
--- a/skywalking-ui
+++ b/skywalking-ui
@@ -1 +1 @@
-Subproject commit 05556dc723d1baed8755bab1a7ec5d029debdb6f
+Subproject commit 206a514ae71267e5b29ac58dba2dbfeaf71a7b2d