使用HiTraceChain打点（C/C++）
=======================

#### 接口说明

分布式跟踪接口由HiTraceChain模块提供，详细API请参考[trace.h](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-trace-h)。

下表所示的接口提供基本的分布式跟踪功能，ArkTS中也有相应的接口。

| 方法 | 接口描述 |
| :-- | :-- |
| HiTraceId OH\_HiTrace\_BeginChain(const char \*name, int flags) | 开始跟踪，并返回创建的HiTraceId。 |
| void OH\_HiTrace\_EndChain() | 停止跟踪。 |
| HiTraceId OH\_HiTrace\_GetId() | 从当前线程TLS中获取跟踪标识。 |
| void OH\_HiTrace\_SetId(const HiTraceId \*id) | 将当前线程TLS中的跟踪标识设置为id。 |
| void OH\_HiTrace\_ClearId(void) | 清除当前线程的跟踪标识。 |
| HiTraceId OH\_HiTrace\_CreateSpan(void) | 创建跟踪分支。创建一个HiTraceId，使用当前线程TLS中的chainId、spanId初始化HiTraceId的chainId、parentSpanId，并为HiTraceId生成一个新的spanId，返回该HiTraceId。 |
| bool OH\_HiTrace\_IsIdValid(const HiTraceId \*id) | 
判断HiTraceId是否有效。

true：HiTraceId有效；false：HiTraceId无效。

 |
| bool OH\_HiTrace\_IsFlagEnabled(const HiTraceId \*id, HiTrace\_Flag flag) | 

判断HiTraceId中指定的跟踪标志是否已启用。

true：指定的跟踪标志已启用；false：指定的跟踪标志未启用。

 |
| void OH\_HiTrace\_EnableFlag(const HiTraceId \*id, HiTrace\_Flag flag) | 启用HiTraceId中指定的跟踪标志。 |
| void OH\_HiTrace\_Tracepoint(HiTrace\_Communication\_Mode mode, HiTrace\_Tracepoint\_Type type, const HiTraceId \*id, const char \*fmt, ...) | HiTraceMeter跟踪信息埋点。 |

下表所示的接口提供对HiTraceId的一些拓展操作，这些接口仅在C/C++中提供。

| 方法 | 接口描述 |
| :-- | :-- |
| void OH\_HiTrace\_InitId(HiTraceId \*id) | 初始化HiTraceId。 |
| int OH\_HiTrace\_GetFlags(const HiTraceId \*id) | 获取HiTraceId中设置的跟踪标志位。 |
| void OH\_HiTrace\_SetFlags(HiTraceId \*id, int flags) | 设置跟踪标志位到HiTraceId中。 |
| uint64\_t OH\_HiTrace\_GetChainId(const HiTraceId \*id) | 获取HiTraceId中的跟踪链ID。 |
| void OH\_HiTrace\_SetChainId(HiTraceId \*id, uint64\_t chainId) | 设置跟踪链ID到HiTraceId中。 |
| uint64\_t OH\_HiTrace\_GetSpanId(const HiTraceId \*id) | 获取HiTraceId中的分支ID。 |
| void OH\_HiTrace\_SetSpanId(HiTraceId \*id, uint64\_t spanId) | 设置分支ID到HiTraceId中。 |
| uint64\_t OH\_HiTrace\_GetParentSpanId(const HiTraceId \*id) | 获取HiTraceId中的父分支ID。 |
| void OH\_HiTrace\_SetParentSpanId(HiTraceId \*id, uint64\_t parentSpanId) | 设置父分支ID到HiTraceId中。 |
| int OH\_HiTrace\_IdToBytes(const HiTraceId\* id, uint8\_t\* pIdArray, int len) | 将HiTraceId转换为字节数组，用于缓存或通信传递。 |
| void OH\_HiTrace\_IdFromBytes(HiTraceId \*id, const uint8\_t \*pIdArray, int len) | 根据字节数组创建HiTraceId。 |

#### 开发步骤

std::thread不支持自动传递HiTraceId，开发示例展示了该场景下分布式跟踪的使用方法。开发者可参考[约束与限制](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/hitracechain-intro#约束与限制)，了解常见的支持与不支持HiTraceChain自动传递的机制。

1.  在DevEco Studio中新建工程，选择“Native C++”，工程的目录结构如下：
    
    ├── entry
    │   ├── src
    │       ├── main
    │       │   ├── cpp
    │       │   │   ├── CMakeLists.txt
    │       │   │   ├── napi\_init.cpp
    │       │   │   └── types
    │       │   │       └── libentry
    │       │   │           ├── Index.d.ts
    │       │   │           └── oh-package.json5
    │       │   ├── ets
    │       │   │   ├── entryability
    │       │   │   │   └── EntryAbility.ets
    │       │   │   ├── entrybackupability
    │       │   │   │   └── EntryBackupAbility.ets
    │       │   │   └── pages
    │       │   │       └── Index.ets
    
2.  在“entry > src > main > cpp > CMakeLists.txt”文件中新增libhitrace\_ndk.z.so和libhilog\_ndk.z.so动态链接库，完整的文件内容如下：
    
    \# the minimum version of CMake.
    cmake\_minimum\_required(VERSION 3.5.0)
    project(HiTraceChainTest03)
    
    set(NATIVERENDER\_ROOT\_PATH &#36;{CMAKE\_CURRENT\_SOURCE\_DIR})
    
    if(DEFINED PACKAGE\_FIND\_FILE)
        include(&#36;{PACKAGE\_FIND\_FILE})
    endif()
    
    include\_directories(&#36;{NATIVERENDER\_ROOT\_PATH}
                        &#36;{NATIVERENDER\_ROOT\_PATH}/include)
    
    add\_library(entry SHARED napi\_init.cpp)
    target\_link\_libraries(entry PUBLIC libace\_napi.z.so libhitrace\_ndk.z.so libhilog\_ndk.z.so)
    
3.  编辑“entry > src > main > cpp > napi\_init.cpp”文件，使用HiTraceChain跟踪多线程任务，完整的示例代码如下：
    
    #include <thread>
    
    #include "hilog/log.h"
    #include "hitrace/trace.h"
    #include "napi/native\_api.h"
    
    #undef LOG\_TAG
    #define LOG\_TAG "testTag"
    
    void Print2(HiTraceId id)
    {
        // 为当前线程设置HiTraceId
        OH\_HiTrace\_SetId(&id);
        // 生成分支标识spanId
        id = OH\_HiTrace\_CreateSpan();
        // 为当前线程设置带spanId的HiTraceId
        OH\_HiTrace\_SetId(&id);
        OH\_LOG\_INFO(LogType::LOG\_APP, "Print2");
        // 结束当前线程的分布式跟踪，功能同OH\_HiTrace\_EndChain()
        OH\_HiTrace\_ClearId();
        OH\_LOG\_INFO(LogType::LOG\_APP, "Print2, HiTraceChain end");
    }
    
    void Print1(HiTraceId id)
    {
        // 为当前线程设置HiTraceId
        OH\_HiTrace\_SetId(&id);
        // 生成分支标识spanId
        id = OH\_HiTrace\_CreateSpan();
        // 为当前线程设置带spanId的HiTraceId
        OH\_HiTrace\_SetId(&id);
        OH\_LOG\_INFO(LogType::LOG\_APP, "Print1");
        std::thread(Print2, OH\_HiTrace\_GetId()).detach();
        // 结束当前线程的分布式跟踪
        OH\_HiTrace\_EndChain();
        OH\_LOG\_INFO(LogType::LOG\_APP, "Print1, HiTraceChain end");
    }
    
    static napi\_value Add(napi\_env env, napi\_callback\_info info)
    {
        // 任务开始，开启分布式跟踪
        HiTraceId hiTraceId = OH\_HiTrace\_BeginChain("testTag: hiTraceChain begin", HiTrace\_Flag::HITRACE\_FLAG\_DEFAULT);
        // 判断生成的hiTraceId是否有效，有效则输出一行hilog日志
        if (OH\_HiTrace\_IsIdValid(&hiTraceId)) {
            OH\_LOG\_INFO(LogType::LOG\_APP, "HiTraceId is valid");
        }
        // 使能HITRACE\_FLAG\_INCLUDE\_ASYNC标志位，表示会在系统支持的异步机制里自动传递HiTraceId
        OH\_HiTrace\_EnableFlag(&hiTraceId, HiTrace\_Flag::HITRACE\_FLAG\_INCLUDE\_ASYNC);
        // 判断hitraceId的HITRACE\_FLAG\_INCLUDE\_ASYNC标志位是否已经使能，使能则把hiTraceId设置到当前线程TLS中
        if (OH\_HiTrace\_IsFlagEnabled(&hiTraceId, HiTrace\_Flag::HITRACE\_FLAG\_INCLUDE\_ASYNC)) {
            OH\_HiTrace\_SetId(&hiTraceId);
            OH\_LOG\_INFO(LogType::LOG\_APP, "HITRACE\_FLAG\_INCLUDE\_ASYNC is enabled");
        }
        size\_t argc = 2;
        napi\_value args\[2\] = {nullptr};
    
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    
        napi\_valuetype valuetype0;
        napi\_typeof(env, args\[0\], &valuetype0);
    
        napi\_valuetype valuetype1;
        napi\_typeof(env, args\[1\], &valuetype1);
    
        double value0;
        napi\_get\_value\_double(env, args\[0\], &value0);
    
        double value1;
        napi\_get\_value\_double(env, args\[1\], &value1);
    
        napi\_value sum;
        napi\_create\_double(env, value0 + value1, &sum);
    
        // 创建线程执行打印任务，传递当前线程的HiTraceId
        std::thread(Print1, OH\_HiTrace\_GetId()).detach();
        // 任务结束，结束分布式跟踪
        OH\_HiTrace\_EndChain();
        OH\_LOG\_INFO(LogType::LOG\_APP, "Add, HiTraceChain end");
    
        return sum;
    }
    
    EXTERN\_C\_START
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {
            { "add", nullptr, Add, nullptr, nullptr, nullptr, napi\_default, nullptr }
        };
        napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        return exports;
    }
    EXTERN\_C\_END
    
    static napi\_module demoModule = {
        .nm\_version = 1,
        .nm\_flags = 0,
        .nm\_filename = nullptr,
        .nm\_register\_func = Init,
        .nm\_modname = "entry",
        .nm\_priv = ((void\*)0),
        .reserved = { 0 },
    };
    
    extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void)
    {
        napi\_module\_register(&demoModule);
    }
    
    编辑“entry > src > main > ets > pages > Index.ets”文件，在按钮点击事件里调用Add方法，完整的示例代码如下：
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import testNapi from 'libentry.so';
    
    const DOMAIN = 0x0000;
    
    @Entry
    @Component
    struct Index {
      @State message: string = 'clickTime=0';
      @State clickTime: number = 0;
    
      build() {
        Row() {
          Column() {
            Button(this.message)
              .fontSize(20)
              .margin(5)
              .width(350)
              .height(60)
              .fontWeight(FontWeight.Bold)
              .onClick(() => {
                this.clickTime++;
                this.message = 'clickTime=' + this.clickTime;
                hilog.info(DOMAIN, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
              })
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    
4.  点击DevEco Studio界面中的运行按钮，运行应用工程。然后点击设备上“clickTime=0”按钮，触发业务逻辑。
    
5.  在DevEco Studio Log窗口查看分布式跟踪的相关信息。
    
    *   设备屏幕上按钮显示“clickTime=1”，表示已点击了按钮一次并触发业务逻辑。
        
    *   示例所有hilog打印均使用了“testTag”，因此可以使用“testTag”关键字过滤日志，查看该业务代码打印的hilog信息。
        
        06-05 21:26:01.006   9944-9944     C02D33/com.exa...tion/HiTraceC  com.examp...lication  I     \[a92ab19ae90197d 0 0\]HiTraceBegin name:testTag: hiTraceChain begin flags:0x00.
        06-05 21:26:01.006   9944-9944     A00000/com.exa...ation/testTag  com.examp...lication  I     \[a92ab19ae90197d 0 0\]HiTraceId is valid
        06-05 21:26:01.006   9944-9944     A00000/com.exa...ation/testTag  com.examp...lication  I     \[a92ab19ae90197d 0 0\]HITRACE\_FLAG\_INCLUDE\_ASYNC is enabled
        06-05 21:26:01.007   9944-9944     A00000/com.exa...ation/testTag  com.examp...lication  I     Add, HiTraceChain end
        06-05 21:26:01.007   9944-9944     A00000/com.exa...ation/testTag  com.examp...lication  I     Test NAPI 2 + 3 = 5
        06-05 21:26:01.007   9944-13961    A00000/com.exa...ation/testTag  com.examp...lication  I     \[a92ab19ae90197d 2544fdb 0\]Print1
        06-05 21:26:01.007   9944-13961    A00000/com.exa...ation/testTag  com.examp...lication  I     Print1, HiTraceChain end
        06-05 21:26:01.008   9944-13962    A00000/com.exa...ation/testTag  com.examp...lication  I     \[a92ab19ae90197d 236699a 2544fdb\]Print2
        06-05 21:26:01.008   9944-13962    A00000/com.exa...ation/testTag  com.examp...lication  I     Print2, HiTraceChain end
        
    *   hilog日志前附加的\[chainId spanId parentSpanId\]格式的信息即为HiTraceId信息，例如\[a92ab19ae90197d 236699a 2544fdb\]表示跟踪链标识chainId值为a92ab19ae90197d，分支标识spanId值为236699a，父分支标识parentSpanId值为2544fdb。
        
    *   通过手动传递HiTraceId，创建spanId，并将其设置到std::thread创建的子线程中，子线程中运行的Print1和Print2业务的hilog日志也携带上同主线程一致的跟踪标识“a92ab19ae90197d”。
        
    *   使用OH\_HiTrace\_EndChain()或OH\_HiTrace\_ClearId()结束分布式跟踪后，hilog打印信息不再携带HiTraceId信息。