SkyWalking自定義鏈路跟蹤(@Trace和apm-customize-enhance-plugin介紹)


SkyWalking方法級trace粒度實現 @Trace和apm-customize-enhance-plugin介紹
 
場景
在開發過程了,我們除了想知道鏈路的整體耗時以外,有的時候也想要知道某些方法的執行耗時。為了達到這個目的,我們需要做一些額外的配置。
今天就給大家介紹SkyWalking方法級trace的實現。
實現
SkyWalking方法級trace的實現具體分為侵入式和外部配置兩種方式,各有優劣,可根據項目情況自行選擇。首選給大家介紹侵入式實現方式。

一、侵入式實現

1、pom.xml依賴
  <dependency>
      <groupId>org.apache.skywalking</groupId>
      <artifactId>apm-toolkit-trace</artifactId>
      <version>${skywalking.version}</version>
   </dependency>
${skywalking.version} 和你當前使用的SkyWalking版本保持一致。
Methods annotated with @Tag will try to tag the current active span with the given key (Tag#key()) and (Tag#value()), if there is no active span at all, this annotation takes no effect.
根據官方文檔的介紹,我們了解到可以使用@Tag注解實現方法級trace,@tag同時也包括key()和value()兩個參數。同時必須保證方法在一個激活的span中,否則注解將不會起作用。
了解到基礎用法以后,我們再接着往下看。下面的代碼塊中官方提供了基礎的注解用例。
/**
 * The codes below will generate a span,
 * and two types of tags, 
      one type tag: keys are `tag1` and `tag2`, values are the passed-in parameters, respectively, 
      the other type tag: keys are `username`  and `age`, values are the return value in User, respectively
 */
@Trace
@Tag(key = "tag1", value = "arg[0]")
@Tag(key = "tag2", value = "arg[1]")
@Tag(key = "username", value = "returnedObj.username")
@Tag(key = "age", value = "returnedObj.age")
public User methodYouWantToTrace(String param1, String param2) {
    // ActiveSpan.setOperationName("Customize your own operation name, if this is an entry span, this would be an endpoint name");
    // ...
}
在指定的方法上標注上@trace標記,表明這是一個方法級trace。如果,通過配置@Tag,額外獲取我們想要收集的數據。除了入參以外,還能獲取到具體的返回值參數。
基礎用法也了解完了,下面就開始我們的實踐。
2、注解配置
先在項目中引入依賴,然后再對應的方法上新增上注解。這里我們通過@Tag獲取了name,address,token以及traceId參數。
    @Trace
    @Tags({@Tag(key = "name",value = "arg[0]"),
            @Tag(key = "token",value = "returnedObj.token"),
            @Tag(key = "address",value = "arg[1]"),
            @Tag(key = "traceId",value = "returnedObj.traceId")})
    public User doSkyWalkingPluginsV2(String name, String address, int id){
        CachaUser cachaUser = new CachaUser();
        cachaUser.setName(name);
        cachaUser.setAddress(address);
        cachaUser.setId(id);
        String token = TokenUtils.getToken(cachaUser);
        User user = new User();
        user.setAge("18");
        user.setName(name);
        user.setToken(token);
        user.setTraceId(TraceContext.traceId());
        return user;
    }
配置完成后,啟動服務。通過postman遠程調用服務,在skywalking服務頁面可以看到,我們trace的doSkyWalkingPluginsV2方法也被收集到了,方法耗時也能一目了然。
0
點擊方法查看詳情,name,address,token以及traceId參數也都全部被統計收集上來了。
0
去掉@Trace再試一次,這次只顯示了接口鏈路,沒有顯示方法級別的trace粒度。
 

二、非侵入式實現

上面介紹了侵入式實現,侵入式實現需要我們引入pom.xml依賴,同時還需要對指定的方法配置注解。在某些場景下,我們對於監控的項目不方便修改,甚至無法修改的情況下,這個時候就要選擇非侵入式方式來實現方法級的trace粒度追蹤。
apm-customize-enhance-plugin插件作用:自定義增強任意類里的任意方法,從而實現對任意類里任意方法的監控。
1、移動apm-customize-enhance-plugin.jar
首先,找到skywalking的安裝目錄${path}\skywalking,${path}對於具體的文件目錄
0
打開\apache-skywalking-apm-bin\agent目錄,\optional-plugins\apm-customize-enhance-plugin.jar 移動到\plugin/apm-customize-enhance-plugin.jar
2、編寫增強規則
舉例,有個業務方法如下:
public class TestService1 {
    public static void staticMethod(String str0, int count, Map m, List l, Object[] os) {
      // 業務邏輯
    }
  ...
}
對如上方法,新建 customize_enhance.xml,xml的內容就需要如下配置,這里我們額外配置了name和address屬性
<?xml version="1.0" encoding="UTF-8"?>
<enhanced>
    <class class_name="test.apache.skywalking.testcase.customize.service.TestService1">
        <method method="staticMethod(java.lang.String,int.class,java.util.Map,java.util.List,[Ljava.lang.Object;)" operation_name="/is_static_method_args" static="true">
            <operation_name_suffix>arg[0]</operation_name_suffix>
            <operation_name_suffix>arg[1]</operation_name_suffix>
            <operation_name_suffix>arg[3].[0]</operation_name_suffix>
            <tag key="tag_1">arg[2].['k1']</tag>
            <tag key="tag_2">arg[4].[1]</tag>
            <log key="log_1">arg[4].[2]</log>
        </method>
    </class>
</enhanced>
規則說明:
0
特別需要注意的是method的寫法:
基本類型: 基本類型.class ,例如: int.class
非基本類型: 類的完全限定名稱 ,例如:java.lang.String
數組:可以寫個數組打印一下,就知道格式了,例如:
public static void main(String[] args) {
        String[] s = new String[]{};
        System.out.println(s);

        int [] x = new int []{};
        System.out.println(x);
    }
果:
[Ljava.lang.String;@1b0375b3
[I@2f7c7260
此,對於String類型的數組,就可以寫成  [Ljava.lang.String;;對於int類型的數組,則寫成 [I。
通配符方式:
顯示該類里所有靜態方法(√)
    <?xml version="1.0" encoding="UTF-8"?>
    <enhanced>
    <class class_name="com.bx.bean.Order">
        <method method=".*()" static="true"/>
        <method method=".*(.*)" static="true"/>
    </class>
    </enhanced>
顯示該類里所有以get開頭的實例方法(√)
<?xml version="1.0" encoding="UTF-8"?>
    <enhanced>
        <class class_name="com.bx.bean.Order">
            <method method="get.*()" static="false"/>
            <method method="get.*(.*)" static="false"/>
        </class> 
    </enhanced>
顯示所有方法與get方法是否沖突(不沖突,只顯示get方法)
   <?xml version="1.0" encoding="UTF-8"?>
    <enhanced>
        <class class_name="com.bx.bean.Order">
            <method method=".*()" static="false"/>
            <method method="get.*(.*)" static="false"/>
        </class> 
    </enhanced>
3、配置agent.config中添加配置:
修改\config\agent.config文件,新增plugin.customize.enhance_file=${path}:\customize_enhance.xml,${path}對於具體的文件目錄
例如:
plugin.customize.enhance_file=D:\soft\worksoft\skywalking\agent\config\customize_enhance.xml
配置完成后,重新啟動SkyWalking,重啟服務。通過postman遠程調用服務,在skywalking服務頁面可以看到,我們trace的doSkyWalkingPlugins方法也被收集到了,方法耗時也能一目了然。
0
點擊方法查看詳情,name,address參數也都全部被統計收集上來了。
0
至此,兩種方式實現方法級trace粒度的實現就全部介紹完了。
 
三、底層實現
@Trace為什么可以自定義監控指定方法
org.skywalking.apm.toolkit.activation.trace.TraceAnnotationActivation 類里:
0
使用TraceAnnotationMethodInterceptor 攔截器
/**
* {@link TraceAnnotationMethodInterceptor} create a local span and set the operation name which fetch from
* org.apache.skywalking.apm.toolkit.trace.annotation.Trace.operationName. if the fetch value is blank string, and
* the operation name will be the method name.
*
* @author zhangxin
*/
public class TraceAnnotationMethodInterceptor implements InstanceMethodsAroundInterceptor {
 
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
Trace trace = method.getAnnotation(Trace.class); //trace
String operationName = trace.operationName();
if (operationName.length() == 0) {
operationName = generateOperationName(method);
}
 
ContextManager.createLocalSpan(operationName); // 創建LocalSpan
}
 
 
private String generateOperationName(Method method) {
StringBuilder operationName = new StringBuilder(method.getDeclaringClass().getName() + "." + method.getName() + "(");
Class[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
operationName.append(parameterTypes[i].getName());
if (i < (parameterTypes.length - 1)) {
operationName.append(",");
}
}
operationName.append(")");
return operationName.toString();
}
 
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes,
Object ret) throws Throwable {
ContextManager.stopSpan();
return ret;
}
 
@Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class[] argumentsTypes, Throwable t) {
ContextManager.activeSpan().errorOccurred().log(t);
}
}
參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM