You need to enable JavaScript to run this app.
导航

Java SDK

最近更新时间2024.01.05 11:45:56

首次发布时间2021.09.27 10:14:02

Java SDK,用来简化服务端埋点的复杂度。通过使用SDK,您可以仅仅关注埋点方案而不需要关注具体的上报细节。

1. 使用方法

1.1 SDK 集成

如果您需要使用Java SDK,首先需要在pom文件中引入对应的jar:

<dependency>

  <groupId>com.datarangers</groupId>

  <artifactId>datarangers-sdk-core</artifactId>

  <version>{version}</version>

</dependency>

如果使用SpringBoot框架,我们提供了一个封装完成的的starter包,您可以在pom中通过如下方式引入:

<dependency>

   <groupId>com.datarangers</groupId>

   <artifactId>datarangers-sdk-starter</artifactId>

   <version>{version}</version>

</dependency>

如果您无法访问火山的maven仓库,或者没有jar包管理工具,可以从 github 下载离线包,或者自行build离线包: mvn package -DskipTests ,相关的jar所在路径为:

  • datarangers-sdk-core/target/datarangers-sdk-core-{version}-release-jar-with-dependencies.jar
  • datarangers-sdk-starter/target/datarangers-sdk-starter-{version}-release-jar-with-dependencies.jar

version是sdk的版本号,建议使用最新版本(>=1.5.7),最新版本参考:github开源仓库
火山引擎仓库地址:

<repositories>
  <repository>
    <id>bytedance-volcengine</id>
    <name>bytedance Volcengine</name>
    <url>https://artifact.bytedance.com/repository/Volcengine/</url>
  </repository>
</repositories>

1.2 SDK 上报模式介绍

增长分析的 SDK 支持多种上报模式,需要先选择使用模式。

  • HTTP 模式:使用范围广,部署简单,QPS 高。SDK 直接通过http接口进行上报。
  • FILE 模式 (只支持私有化):部署复杂,需要在服务器上多部署logagent进程服务。由 SDK 先将文件写到本地磁盘,然后通过logagent监听磁盘文件,由logagent使用http接口进行上报。
  • KAFKA 模式 (只支持私有化):适用于同一个网络环境,部署简单,QPS高,稳定性高。由 SDK 直接通过kafka进行上报。

模式

使用场景

部署复杂性

可靠性

上报性能

备注

HTTP

绝大多数场景都可以使用,如果跨网络时延比较高,可以使用批量方式。

简单

高,支持批量上报。

如果参考 “最佳实践”-->"查看上报时延"章节内容来查看上报的时延。
SDK>=1.5.6 版本,支持批量。

FILE

不推荐

复杂

很高

低,写文件之后还需要使用logagent来进行上报。

KAFKA

同一个网络,建议使用该模式。

简单

很高

SDK版本>=1.5.6,私有化4.1版本(含)开始支持。

同一个网络推荐使用KAFKA模式上报,其他场景推荐使用 HTTP 的方式,同时使用 logagent 来补报因为网络抖动等原因导致失败的数据。

1.3 SDK 初始化

SDK 使用前,需要先初始化AppEventCollector,然后使用其提供的接口进行上报。

1.3.1 在 SpringBoot 框架中初始化

推荐使用配置的方式进行初始化。

1.3.1.1 HTTP 模式

datarangers.sdk.mode=http表示使用HTTP模式。

1.3.1.1.1 SaaS 配置

本配置适用于「SaaS版本」以及「SaaS云原生版本」。
不需要配置host,需要配置domain、appkeys,以及openapi相关配置。

# SaaS 配置example
datarangers.sdk.env=saas
datarangers.sdk.mode=http

# [domain]
# 服务器ip或域名
# SaaS版本国内站
datarangers.sdk.domain=https://mcs.ctobsnssdk.com
# SaaS版本国际站
#datarangers.sdk.domain=https://mcs.tobsnssdk.com
# SaaS云原生版
#datarangers.sdk.domain=https://gator.volces.com

# [app key]
datarangers.sdk.appKeys.${SDK_APP_1}=${SDK_APP_KEY_1}

# [openapi]
# openapi的domain
# SaaS版国内站
datarangers.sdk.openapiConfig.domain=https://analytics.volcengineapi.com
# SaaS版国际站
#datarangers.sdk.openapiConfig.domain=https://analytics.byteplusapi.com
# SaaS云原生版
# datarangers.sdk.openapiConfig.domain=https://analytics.volcengineapi.com

# openapi的ak, sk
datarangers.sdk.openapiConfig.ak=${OPENAPI_AK}
datarangers.sdk.openapiConfig.sk=${OPENAPI_SK}
  • domain
    • SaaS版国内站:https://mcs.ctobsnssdk.com
    • SaaS版国际站: https://mcs.tobsnssdk.com
    • SaaS云原生版:https://gator.volces.com
  • openapiConfig: openapi配置,如果在SaaS版/SaaS云原生版上需要进行item和用户属性上报,需要配置,其他情况不需要进行配置
    • openapiConfig.domain: openapi的域名
      • SaaS版国内站: https://analytics.volcengineapi.com
      • SaaS版国际站: https://analytics.byteplusapi.com
      • SaaS云原生版: https://analytics.volcengineapi.com
    • openapiConfig.ak:openapi的ak, 请联系客户经理获取。
    • openapiConfig.sk: openapi的sk ,请联系客户经理获取。

1.3.1.1.2 私有化配置

配置domain和Host即可。

# 私有化配置example
# 使用 http 上报模式
datarangers.sdk.mode=http
# 服务器ip或域名,以http:// 或者 https:// 开头
datarangers.sdk.domain=${SDK_DOMAIN}
# host,私有化环境Host的配置在安装部署的那台机器上,查看/home/{INSTALL_USER}/DataRangersDeploy/conf_rangers.yml中配置项sdk.report.host。 INSTALL_USER 为安装用户,一般是datarangers或者minibase
datarangers.sdk.headers.Host=${SDK_HOST}

#异步线程数量,当并发不够的时候可以调整该数据
#datarangers.sdk.threadCount=20

#[http config]
# 单位是毫秒
#datarangers.sdk.httpConfig.requestTimeout=10000
#datarangers.sdk.httpConfig.connectTimeout=10000
#datarangers.sdk.httpConfig.socketTimeout=20000

# 单位是s
#datarangers.sdk.httpConfig.keepAliveTimeout=30

如果跨网络时延比较大、或者追求更高的QPS,可以开启批量上报的方式。

# [batch]
# 使用 batch 的方式
datarangers.sdk.sendBatch=true
# 批量的数量
#datarangers.sdk.batchSize=20
#datarangers.sdk.waitTimeMs=100
  • Host: datarangers.sdk.headers为http请求中headers字段内容,在私有化环境中必须要添加Host,在私有化环境Host的配置在安装部署的那台机器上,查看/home/{INSTALL_USER}/DataRangersDeploy/conf_rangers.yml中配置项sdk.report.host。INSTALL_USER 为安装用户,一般是datarangers或者minibase。

1.3.1.1.3 SAAS 云原生配置

不需要配置host,需要配置domain、appkeys,不需要openapi相关配置,sdk版本需要 >= 1.5.7。

# saas native 置example
# 设置环境信息
datarangers.sdk.env=saas_native
# 配置上报模式
datarangers.sdk.mode=http

# [domain]
# 服务器ip或域名
datarangers.sdk.domain=https://gator.volces.com

# [app key]
datarangers.sdk.appKeys.${SDK_APP_1}=${SDK_APP_KEY_1}

1.3.1.2 FILE 模式

datarangers.sdk.mode=file表示使用FILE模式,该模式只在私有化支持

# 私有化配置example
# 使用file上报模式,需要配合 loagent 一起使用
datarangers.sdk.mode=file

# 文件路径
#datarangers.sdk.eventSavePath=logs/
#datarangers.sdk.eventSaveName=datarangers.log
# 单位是M
#datarangers.sdk.eventSaveMaxFileSize=100
# 如果没有配置eventFilePaths,那么会把日志文件放到eventSavePath目录下
#datarangers.sdk.eventFilePaths=event/logs/1/,event/logs/2/,event/logs/3/,event/logs/4/,event/logs/5/,event/logs/6/

# 文件最大保留时间,默认是-1,一直保留
#datarangers.sdk.eventSaveMaxDays=-1
  • eventSaveName:保存日志的文件名,需要保证文件的写权限。
  • eventSavePath:保存日志的文件路径,需要保证写权限和创建文件的权限。
  • eventSaveMaxFileSize:表示需要保存的日志文件的最大文件大小,单位为MB。
  • eventFilePaths:表示需要保存的日志文件的位置,为一个字符串数组,数组中的每一个值都表示一个路径,用户将日志文件写到不同的文件夹下,可以配合多个LogAgent实例使用。注意:如果定义了该数组,则 eventSavePath 不会生效。
  • eventSaveMaxDays:最多保留多少天的日志文件,超过这个时间的日志会被删除,默认是-1,即不删除文件。因此长时间运行的时候,为了避免占用过多磁盘,需要自行删除日志文件,或者配置一个合理的值,比如 7。

使用该模式,埋点事件只是记录到磁盘中,还需要配合logagent一起使用,数据才能上报到 DataFinder,关于logagent的使用,请联系客户经理获取。

1.3.1.3 KAFKA 模式

datarangers.sdk.mode=kafka表示使用KAFKA模式,该模式只在私有化支持

# 私有化配置example
# 使用kafka上报的模式
datarangers.sdk.mode=kafka
datarangers.sdk.kafka.bootstrapServers={ip1}:9192,{ip2}:9192

# kafka producer的 properties可以在这里进行配置
#datarangers.sdk.kafka.properties.retries=3

1.3.2 在Spring 容器中初始化

推荐将初始化操作装配成 Bean,交给 Spring 容器来管理。在使用的类中注入即可使用。

  • 使用xml配置,注入bean:

    <bean name="appEventCollector" class="com.datarangers.collector.AppEventCollector">
    
        <constructor-arg name="appType" value="app"/>
    
        <constructor-arg name="properties" ref="dataRangersSDKConfigProperties"/>
    
    </bean>
    
    <bean name="mpEventCollector" class="com.datarangers.collector.AppEventCollector">
    
        <constructor-arg name="appType" value="mp"/>
    
        <constructor-arg name="properties" ref="dataRangersSDKConfigProperties"/>
    
    </bean>
    
    <bean name="webEventCollector" class="com.datarangers.collector.AppEventCollector">
    
        <constructor-arg name="appType" value="web"/>
    
        <constructor-arg name="properties" ref="dataRangersSDKConfigProperties"/>
    </bean>
    
    <!-- sdkMode config -->
    <bean name="sdkMode" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="staticField" value="com.datarangers.config.SdkMode.HTTP" />    
    </bean>
    
    <bean name="dataRangersSDKConfigProperties" class="com.datarangers.config.DataRangersSDKConfigProperties">
    
        <property name="domain" value="{domain}"/>
    
        <property name="mode" ref="sdkMode"/>
    
        <property name="httpTimeout" value="500"/>
    
        <property name="appKeys">
          <map>
            <entry key="${appId}" value="${appKey}"/>
          </map>
        </property>
        <property name="headers">
          <map>
            <entry key="Host" value="{host}"/>
          </map>
        </property>
    </bean>
    
  • 使用注解的方式注入bean:

    @Configuration
    @EnableAsync
    @EnableConfigurationProperties(DataRangersSDKConfigPropertiesInfo.class)
    public class DataRangersEnableAutoConfiguration {
    
      @Autowired
      private DataRangersSDKConfigPropertiesInfo dataRangersSDKConfigPropertiesInfo;
    
      @Bean(name = "appEventCollector")
      public EventCollector defaultAppCollector(Callback callback) {
        return new AppEventCollector("app", dataRangersSDKConfigPropertiesInfo, callback);
      }
    
      @Bean(name = "webEventCollector")
      public EventCollector defaultWebCollector(Callback callback) {
        return new AppEventCollector("web", dataRangersSDKConfigPropertiesInfo, callback);
      }
    
      @Bean(name = "mpEventCollector")
      public EventCollector defaultMpbCollector(Callback callback) {
        return new AppEventCollector("mp", dataRangersSDKConfigPropertiesInfo, callback);
      }
    
      @Bean
      @ConditionalOnMissingBean(Callback.class)
      public Callback callback() {
        return new LoggingCallback(dataRangersSDKConfigPropertiesInfo.getEventSavePath(),
            "error-" + dataRangersSDKConfigPropertiesInfo.getEventSaveName(),
            dataRangersSDKConfigPropertiesInfo.getEventSaveMaxFileSize());
      }
    }
    

1.3.3 在普通 java 程序手动初始化

DataRangersSDKConfigProperties properties = new DataRangersSDKConfigProperties();
// 设置模式
properties.setMode(SdkMode.HTTP);

// 设置domain。 注意设置成真实的参数
properties.setDomain(System.getenv("SDK_DOMAIN"));

// 私有化需要设置Host,saas不需要设置Host。注意设置成真实的参数
properties.getHeaders().put("HOST", System.getenv("SDK_HOST"));

// 初始化collector
EventCollector appEventCollector = new AppEventCollector("app", properties);
EventCollector webEventCollector = new AppEventCollector("web", properties);
EventCollector mpEventCollector = new AppEventCollector("mp", properties);

1.3.4 SDK 配置参考

AppEventCollector 使用的参数有两个:appType 和 properties。
appType 只支持:

  • app,在服务端上传应用移动端相关的数据
  • web,在服务端上传应用web端相关的数据
  • mp,在服务端上传应用小程序端相关的数据

properties 前缀都是datarangers.sdk

配置模块

配置项

含义

备注

mode

上报模式(不区分大小写):http、file、kafka

在java SDK 版本>=1.5.6 版本之后,建议使用新的该配置。当mode和save同时存在的时候,以mode为准。

save

boolean型变量,表示是否保存到本地文件, saas 只支持配置成 false。

该配置在>=1.5.6 版本之后废弃,使用mode 配置。

env

枚举类型,saas 表示云上,privatization表示私有化,非必须,sdk可以根据配置自动判定。

sync

boolean类型,同步异步,默认为false,使用异步上报的方式。

HTTP

domain

DataFinder 的域名或者ip,支持http和https,例如为 https://{具体域名地址}
在SaaS环境中需要修改成对应的域名:

当使用http上报模式的时候,必须配置。

headers.Host

datarangers.sdk.headers为http请求中headers字段内容,在私有化环境中必须要添加Host。 其值可以在私有化环境Host的配置在安装部署的那台机器上,查看/home/{INSTALL_USER}/DataRangersDeploy/conf_rangers.yml中配置项sdk.report.host。 INSTALL_USER 为安装用户,一般是datarangers或者minibase。

在saas上不需要进行配置。

threadCount

异步线程池核心线程数里,默认是20。

httpConfig.requestTimeout

http发送的请求超时时间,单位是毫秒,默认是10000。

httpConfig.connectTimeout

http发送的连接超时时间,单位是毫秒,默认是10000。

httpConfig.socketTimeout

http发送的socket超时时间,单位是毫秒,默认是20000。

httpConfig.keepAliveTimeout

http连接池的keepAliveTimeout,单位是秒,默认是30。

httpConfig.trustDisable

true/false。是否禁用双向认证,如果发生ssl相关的错误,建议优先配置证书,或者配置为true,表示禁用双向认证。默认是true

httpConfig.customKeyTrustEnable

true表示自定义客户端的证书路径以及密码,默认是false

httpConfig.keyMaterialPath

keyMaterial 路径

httpConfig.keyPassword

key密码

httpConfig.storePassword

store密码

httpConfig.trustMaterialPath

trustMaterial路径

httpConfig.trustStrategy

信任策略。

  • self,自签名策略
  • all,信任所有

sendBatch

boolean类型,表示是否开启批量上报。默认是false,不使用批量。

saas暂不支持批量上报。

batchSize

批量上报的数量

waitTimeMs

批量上报,不足batchSize的时候,最大等待时间

appKeys.${SDK_APP_1}

SAAS环境,上报设置的appkeys,可以设置多个。多个可以的方式为:

datarangers.sdk.appKeys.${APP_ID_1}=${APP_KEY_1}
datarangers.sdk.appKeys.${APP_ID_2}=${APP_KEY_2}

注意替换成真实的app_id和appkey。

HTTP (OpenAPI)

openapiConfig.domain

openapi的域名:

如果在saas上需要进行item和用户属性上报,需要配置openapiconfig,其他情况不需要进行配置。

openapiConfig.ak

openapi的ak,请联系客户经理获取。

openapiConfig.sk

openapi的sk,请联系客户经理获取。

FILE

eventSaveName

保存日志的文件名,需要保证文件的写权限,默认是 datarangers.log。

eventSavePath

保存日志的文件路径,需要保证写权限和创建文件的权限,模式是 logs/

eventSaveMaxFileSize

表示需要保存的日志文件的最大文件大小,单位为MB,默认是100。

eventCountFileDisable

表示是否禁用count.log 文件,默认为false;设置为true,则不会有count.log

eventFilePaths

表示需要保存的日志文件的位置,为一个字符串数组,数组中的每一个值都表示一个路径,用户将日志文件写到不同的文件夹下,可以配合多个LogAgent实例使用。

说明

如果定义了该数组,则 eventSavePath 不会生效。

eventSaveMaxDays

最多保留多少天的日志文件,超过这个时间的日志会被删除,默认是-1,即不删除文件。因此长时间运行的时候,为了避免占用过多磁盘,需要自行删除日志文件,或者配置一个合理的值,比如 7。

KAFKA

bootstrapServers

kafka的地址。

使用kafka模式需要进行配置。

properties

是一个map,需要配置的其他的kafka properties。kafkaProducer的参数参考:https://kafka.apache.org/0102/documentation.html#producerconfigs

verify

verify.url

服务端埋点实时检测的url地址。

SDK版本 >= 1.5.7

1.4 验证配置

1.4.1 查看启动日志

启动会打印如下日志,可以通过监测启动日志来确定配置是否符合预期。主要关注下sdkModedomainheaders等配置。

sdk config properties: 
enable: true 
env: PRIVATIZATION 
sync: false 
sdkMode: HTTP  
domain: ...
headers: {...}  
threadCount: 20 
queueSize: 10240 
sendBatch: false 
batchSize: 20 
waitTimeMs: 100 
httpConfig: {...} 
eventSavePath: logs/ 
eventSaveName: datarangers.log 
eventFilePaths: [logs/] 
eventSaveMaxFileSize: 100 
eventSaveMaxDays: -1 
openapiConfig: {...} 
kafka: null

1.4.2 HTTP 模式 Debug 验证

查看配置或自动日志,确定配置的 sdkModeHTTP,同时检查:

  • 检查datarangers.log文件是否存在数据,有埋点数据就属于异常,请检查开关是否正确或配置是否生效
  • 检查error-datarangers.log文件,如果存在数据,请检查错误信息

Debug验证请求/响应报文
Debug断点HttpUtils类167行(不同版本可能行数有所差异,但逻辑都是获取请求和响应报文)//以下截图为1.5.3版本
图片
resustStr为响应报文,其中如果e不为0,说明上报失败了,反之表示成功上报
body为请求报文,如果响应报文了解到报错,可以获取下来看看数据格式是否正确

1.4.3 HTTP 模式 curl 验证

可以使用 curl 命令,查看是否能正常上报:
注意修改脚本中的 local_time_ms datetime 字段为当前时间

1.4.3.1 SAAS 环境

appkey 需要替换成相应的配置。

curl --location --request POST 'https://mcs.ctobsnssdk.com/v2/event/json' \
--header 'X-MCS-AppKey: ${AppKey}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user": {
        "user_unique_id": "test_sdk_user"
    },
    "header": {
        "custom": {
            "__sdk_platform": "datarangers_sdk_1.4.9-release"
        },
        "device_id": 0,
        "timezone": 8,
        "tz_offset": 28800,
        "tz_name": "Asia/Shanghai",
        "user_unique_id": "10.1.6.229"
    },
    "events": [
        {
            "event": "enterprise_search",
            "params": "{\"date_time\":\"20220803 15:19:56\",\"operateType\":9,\"moduleName\":\"\",\"productType\":0}",
            "local_time_ms": 1659511196363,
            "datetime": "2022-08-03 15:19:56"
        }
    ]
}'

如下返回表示成功:

{
    "e": 0,
    "message": "success",
    "sc": 1
}

1.4.3.2 私有化环境

domain 和 host 分别替换成相应的配置, app_id 也要修改成相应的配置。

curl --location --request POST '${domain}/sdk/log' \
--header 'User-Agent: DataRangers Java SDK' \
--header 'Host: ${host}' \
--header 'Content-Type: application/json' \
--data '{
    "app_type": "app",
    "_format_name": "datarangers_svc_app_log_v3_server_sdk_1.2.7",
    "app_id": 10000000,
    "header": {
        "custom": {
            "client": "fdaf",
            "__sdk_platform": "datarangers_sdk"
        },
        "device_id": 0,
        "timezone": 8,
        "tz_offset": 28800,
        "tz_name": "Asia/Shanghai",
        "user_unique_id": "test_sdk_user",
        "app_id": 10000000
    },
    "user_unique_id": "xxxx",
    "event_v3": [
        {
            "event": "补贴查询",
            "params": {
                "cardNum": "cardNum",
                "year": "2021"
            },
            "local_time_ms": 1637734994778,
            "datetime": "2021-12-15 14:23:14"
        }
    ]
}'

如下返回表示上报成功:

{
    "e":0,
    "magic_tag":"server_sdk_log",
    "message":"success",
    "server_time":1664352669
}

1.4.3.3 SAAS云原生环境

appkey 需要替换成相应的配置。

curl --location --request POST 'https://gator.volces.com/sdk/log' \
--header 'User-Agent: DataRangers Java SDK' \
--header 'X-MCS-AppKey: ${AppKey}' \
--header 'Content-Type: application/json' \
--data '{
    "app_type": "app",
    "_format_name": "datarangers_svc_app_log_v3_server_sdk_1.2.7",
    "header": {
        "custom": {
            "client": "fdaf",
            "__sdk_platform": "datarangers_sdk"
        },
        "device_id": 0,
        "timezone": 8,
        "tz_offset": 28800,
        "tz_name": "Asia/Shanghai",
        "user_unique_id": "test_sdk_user"
    },
    "user_unique_id": "xxxx",
    "event_v3": [
        {
            "event": "补贴查询",
            "params": {
                "cardNum": "cardNum",
                "year": "2021"
            },
            "local_time_ms": 1637734994778,
            "datetime": "2021-12-15 14:23:14"
        }
    ]
}'

如下返回表示上报成功:

{
    "e":0,
    "magic_tag":"server_sdk_log",
    "message":"success",
    "server_time":1664352669
}

1.4.4 FILE 模式(只支持私有化)

查看配置 datarangers.sdk.eventSavePath的文件目录下是否有文件,是否有文件内容。校验文件内容是否正确,可以直接拷贝一行数据,使用http的方式进行上报。

1.4.5 KAFKA 模式 (只支持私有化)

查看kafka topic: sdk_origin_event 是否有数据上来,kafka 的使用方式参考文档:
Kafka订阅(私有化) 增长分析-火山引擎

1.5 使用 SDK

使用时需要先注入Bean,Bean有三种类型,如下:

// App
@Resource(name = "appEventCollector")
private EventCollector appEventCollector;

// Web
@Resource(name = "webEventCollector")
private EventCollector webEventCollector;

// 小程序
@Resource(name = "mpEventCollector")
private EventCollector mpEventCollector;

或者自定义一个EventCollector, 定义方式可以参考 “SDK 初始化”章节内容。
如果您已经注入完成了,则可以调用bean进行事件发送。发送的接口为:

/**
 * 功能描述: 异步发送事件
 *
 * @param appId        应用id
 * @param custom       用户自定义公共参数
 * @param eventName    事件名称
 * @param eventParams  事件参数
 * @param userUniqueId 用户uuid
 * @return: void
 * @date: 2020/8/26 12:24
 */
void sendEvent(String userUniqueId, int appId, Map<String, Object> custom, String eventName, Map<String, Object> eventParams);

/**
  * 功能描述: 异步发送事件
  *
  *  @param  appId        应用id
  *  @param  custom       用户自定义公共参数
  *  @param  eventName    事件名称
  *  @param  eventParams  事件参数
  *  @param  userUniqueId 用户uuid
  *  @param  localTimeMs  事件发生时间,毫秒
  *  @return:  void
  *  @date:  2020/8/26 12:24
  */
void sendEvent(String userUniqueId, int appId, Map<String, Object> custom, String eventName, Map<String, Object> eventParams,long localTimeMs);

/**
 * 功能描述: 批量发送事件
 *
 * @param header 事件的公共属性,可以通过调用HeaderV3.Builder().build()构建一个header
 * @param events 事件数组 一般不推荐自己构建事件数组,我们推荐使用EventsBuilder这个类对多事件进行构造,并调用build方法生成事件数组
 * @return: void
 * @date: 2020/12/25 15:57
 */
void sendEvents(Header header, List<Event> events);

/**
 * 功能描述: 发送单条事件
 *
 * @param header      事件的公共属性,可以通过调用HeaderV3.Builder().build()构建一个header
 * @param eventName   事件名
 * @param eventParams 事件参数
 * @return: void
 * @date: 2020/9/28 22:00
 */
void sendEvent(Header header, String eventName, Map<String, Object> eventParams);

/**
  * 功能描述: 发送单条事件
  *
  *  @param  header      事件的公共属性,可以通过调用HeaderV3.Builder().build()构建一个header
  *  @param  eventName   事件名
  *  @param  eventParams 事件参数
  *  @param  localTimeMs  事件发生时间,毫秒
  *  @return:  void
  *  @date:  2020/9/28 22:00
  */
void sendEvent(Header header, String eventName, Map<String, Object> eventParams,long localTimeMs);

/**
 * 功能描述: 批量发送事件
 *
 * @param header    事件的公共属性,可以通过调用HeaderV3.Builder().build()构建一个header
 * @param eventName 事件名数组,需要与eventParams数组长度相同
 * @return: void
 * @date: 2020/12/25 15:59
 */
void sendEvent(Header header, List<String> eventName, List<Map<String, Object>> eventParams);

/**
 * 功能描述: 对userUniqueId的用户进行profile属性设置
 *
 * @param appId        app id
 * @param userUniqueId 用户id
 * @param eventParams  需要设置的用户属性
 * @return: void
 * @date: 2020/12/23 10:43
 */

/**
 * 功能描述: 设置用户的属性
 * @param appId  app id
 * @param userUniqueId uuid
 * @param params 需要设置的用户属性
 * @return: void
 * @date: 2020/12/25 16:11
 */
void profileSet(String userUniqueId, int appId, Map<String, Object> eventParams);

/**
 * 功能描述: 设置用户属性,存在则不设置,不存在则创建,适合首次相关的用户属性,比如首次访问时间等。
 * @param appId  app id
 * @param userUniqueId uuid
 * @param params 需要设置的用户属性
 * @return: void
 * @date: 2020/12/25 16:11
 */
void profileSetOnce(String userUniqueId, int appId, Map<String, Object> eventParams);

/**
 * 功能描述: 设置数值类型的用户属性,可进行累加
 * @param appId  app id
 * @param userUniqueId uuid
 * @param params 需要设置的用户属性
 * @return: void
 * @date: 2020/12/25 16:11
 */
void profileIncrement(String userUniqueId, int appId, Map<String, Object> eventParams);

/**
 * 功能描述: 设置List类型的用户属性,可持续向List内添加
 * @param appId  app id
 * @param userUniqueId uuid
 * @param params 需要设置的用户属性
 * @return: void
 * @date: 2020/12/25 16:11
 */
void profileAppend(String userUniqueId, int appId, Map<String, Object> eventParams);

/**
 * 功能描述: 删除用户的属性
 * @param appId  app id
 * @param userUniqueId uuid
 * @param params 需要删除的用户属性名
 * @return: void
 * @date: 2020/12/25 16:11
 */
void profileUnset(String userUniqueId, int appId, List<String> params);

/**
 * 功能描述: 对业务对象进行设置
 *
 * @param appId app id
 * @param name  业务对象的名称
 * @param items 业务对象的类,需要继承Items类,注意
 * @return: void
 * @date: 2020/12/23 10:47
 */
void itemSet(int appId, String name, List<Item> items);

/**
 * 功能描述: 删除item的属性
 * @param appId
 * @param id
 * @param name
 * @param params 需要删除的item属性
 * @return: void
 * @date: 2020/12/25 16:13
 */
void itemUnset(int appId, String id, String name, List<String> params);

1.6 SDK demo

参考代码:
https://github.com/volcengine/datarangers-sdk-java/tree/main/datarangers-sdk-example

2. 使用示例

2.1 发送普通事件

@Resource(name = "appEventCollector")
private EventCollector appEventCollector;

appEventCollector.sendEvent("uuid2", 10000000, null, "test_event_java_sdk", new HashMap<String, Object>() {{
    put("date_time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
    put("current_time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss")));
}});

2.2 设置用户属性

用户属性需要先在系统中创建之后再上报,参考:User Profile API (服务端-SAAS查看)

eventCollector.profileSet("uuid-1", 10000028, new HashMap<String, Object>() {{
    put("profile_1", "param_1");
    put("profile_2", "param_2");
    put("profile_3", "param_3");
    put("profile_4", "param_4");
}});

2.3 设置Item属性

Item属性需要先在系统中创建之后再上报,参考:业务对象(Item)数据接入(SAAS查看)

注意

如果创建业务对象(Item)时,业务对象名属性名)使用驼峰方式命名,在使用Java SDK上报时会自动转换为下划线格式。例如,您创建的业务对象名为FirstName,数据上报后会转换为First_Name。如果您希望保留驼峰格式,可改用HTTP API方式上报数据。

业务对象内置的id属性,对应的字段名称为item_id,因此不需要再定义一个 id 属性。

List<Item> items = new ArrayList<>();
items.add(new BookItem("1000", "book").setName("Java").setPrice(100).setPublishDate(LocalDate.now()).setAuthors(author).setCategory("computer"));
items.add(new BookItem("1002", "book").setName("PHP").setPrice(100).setPublishDate(LocalDate.now()).setAuthors(author).setCategory("computer"));
eventCollector.itemSet(10000028, "book", items);

2.4 发送携带item的事件

List<Item> items = new ArrayList<>();
items.add(new BookItem("1000", "book"));
items.add(new BookItem("1002", "book"));
items.add(new PhoneItem("1002", "phone"));
eventCollector.sendEvent("user-001", 10000028, null, "set_items", new HashMap<String, Object>() {{
    put("param1", "params");
    put("param2", items.get(0));
    put("param3", items.get(1));
    put("param4", items.get(2));
}});

2.5 上报匿名用户事件

如果需要使用服务端的方式上报匿名用户事件信息,需要把 user_unique_id 的值置为 ""(空字符串),然后设置其他能关联上的设备信息,比如device_id、web_id、anonymousId。如果用户登录之后,能拿到user_unique_id,并且希望登录实名之后的行为能跟匿名之前的行为关联起来,那么在设置正确的user_unique_id之后,还需要带上匿名的时候设置的 device_id、web_id 或 anonymousId。

2.5.1 使用 device_id/web_id 上报匿名用户事件

当使用appEventCollector上报时,deviceId即为device_id,当使用webEventCollectormpEventCollector上报时,deviceId 即为web_id。

Map<String, Object> eventParams = new HashMap<>();
eventParams.put("date_time", new SimpleDateFormat("yyyyMMdd").format(new Date()));
eventParams.put("event_param1", "value1");

// app/web/mp 这里都是deviceId 即可
Long deviceId = 10000L;
Header header = new HeaderV3.Builder()
        .setAppId(appId)
        .setDeviceId(deviceId)
        .setUserUniqueId("")
        .build();

// app/web/mp 使用不同的 EventCollector
// app
appEventCollector.sendEvent(header,"test_app_event_anonymous", eventParams, System.currentTimeMillis());

// 实名之后,关联匿名的行为, u1 会关联上之前设置的匿名用户行为(根据 deviceId 进行关联)
Header header2 = new HeaderV3.Builder()
        .setAppId(appId)
        .setDeviceId(deviceId)
        .setUserUniqueId("u1")
        .build();
appEventCollector.sendEvent(header2,"test_app_event_anonymous", eventParams, System.currentTimeMillis());

// web
webEventCollector.sendEvent(header,"test_web_event_anonymous", eventParams, System.currentTimeMillis());

// mp
mpEventCollector.sendEvent(header,"test_mp_event_anonymous", eventParams, System.currentTimeMillis());

2.5.2 使用 anonymousId 上报匿名用户事件

用户 device_id/web_id 都是long型,如果用户的自定义匿名ID是string的话,device_id/web_id无法满足,可以使用 anonymousId 这个string类型的字段来进行上报。

注意

anonymousId 只支持web/mp,即要使用webEventCollector或者mpEventCollector来进行上报。

Map<String, Object> eventParams = new HashMap<>();
eventParams.put("date_time", new SimpleDateFormat("yyyyMMdd").format(new Date()));
eventParams.put("event_param1", "value1");

String anonymousId="test_anonymousId1";
Header header = new HeaderV3.Builder()
        .setAppId(appId)
        .setAnonymousId(anonymousId)
        .setUserUniqueId("")
        .build();
// web
webEventCollector.sendEvent(header,"test_web_event_anonymous", eventParams, System.currentTimeMillis());

// 实名之后,关联匿名的行为, u1 会关联上之前设置的匿名用户行为(根据 anonymousId 进行关联)
Header header2 = new HeaderV3.Builder()
        .setAppId(appId)
        .setAnonymousId(anonymousId)
        .setUserUniqueId("u1")
        .build();
webEventCollector.sendEvent(header2,"test_web_event_anonymous", eventParams, System.currentTimeMillis());

// mp
mpEventCollector.sendEvent(header,"test_mp_event_anonymous", eventParams, System.currentTimeMillis());

2.6 验证埋点数据

先验证元数据,然后再在行为细查中去查看。
元数据:“数据管理”> “元数据管理”
图片

2.6.1 验证事件

事件发送成功之后,如果是第一次在新事件上报,最长可能需要等5分钟才能查看到。先检查元数据是否正常:

  • 在“应用管理”>“数据管理”> “一般事件”下查看是否有上报的事件。
  • 在“应用管理”>“数据管理”> “事件属性”下查看是否有上报的属性,已经属性的类型、属性关联的事件是否正确。

当事件在元数据中存在后,那么就可以直接在用户细查里面去查询。

2.6.2 验证用户属性

先在元数据中验证是否存在、属性类型是否符合预期。“数据管理”>“用户属性”
然后在行为细查中查看,但是注意上报用户属性之后,一定要上报一条事件,否则行为细查这边可能查询不到。用户细查是跟事件进行关联的。在“最近自定义用户属性” 中能够看到。
图片

2.6.3 验证业务对象

先在“数据管理”>“业务对象”下查看业务对象是否绑定到了事件。然后可以在行为细查进行查询,在行为细查里面会显示item_id。
图片
图片
如果想查看业务对象的属性值,可以在“分析工具” > “高级分析”> “业务对象分析”里面去查看。

2.6.4 埋点实时检测

在“数据管理” > “一般事件” > “验证埋点”上选择“服务端埋点”,然后输入需要检测的 user_unique_id,然后将url参数设置到 verify.url上即可。
图片
当有该用户的埋点数据上报时,会看到上报的内容数据。
图片

注意

  1. 注意该url有效期只有60分钟
  2. SDK版本 >= 1.5.7
  3. 如果url错误或者过期,实时检测不会上报或者上报失败,但是不影响正常的埋点上报。
  4. 如果页面上查看不了内容数据,可以先查看实时埋点上报的日志:
    1. 搜索关键词 ERROR 日志 以及 “verify request” 和 “verify config”。
    2. 实时上报的相关的类为com.datarangers.sender.VerifySender,可以debug下。
    3. 没有错误日志,但是实时上报不显示内容,可以刷新下页面重新生成url。

2.6.5 注意事项

如果页面上查询不到数据,可以尝试下刷新页面。

2.7 导入历史数据

注意:【SaaS版本】历史数据导入需要隔天才能查看,【SaaS云原生】、【私有化】版本可实时查看
如果数据的客户端时间超过七天,正常情况下无法导入,如需导入历史数据,需在headercustom字段中增加历史数据的标识__is_history,属性值设置为字符串"true"
代码样例如下:

appEventCollector.sendEvent(userUniqueId, appId, new HashMap<String, Object>() {{
                    put("__is_history","true");
                }}, "test_event",
                new HashMap<String, Object>() {{
                    put("date_time", new SimpleDateFormat("yyyyMMdd").format(new Date()));
                    put("current_time",
                            new SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new Date()));
                }},
                1637734994778);

3. 最佳实践

3.1 使用自定义的 local_time_ms

Datafinder 分析事件发生时间,是根据上报事件在客户端的发生时间(即local_time_ms)来计算的。举个例子:如果一个用户在2022-09-01 23:58:00 发生了购买事件,但是这个事件由于网络延迟或者其他原因导致 Datafinder 实际在2022-09-02 00:03:00 才接受到这个事件,那么该事件在分析的时候,该事件还是被算是在2022-09-01发生的时间,而不是在2022-09-02发生的事件。
使用接口:

/**
  * 功能描述: 异步发送事件
  *
  *  @param  appId        应用id
  *  @param  custom       用户自定义公共参数
  *  @param  eventName    事件名称
  *  @param  eventParams  事件参数
  *  @param  userUniqueId 用户uuid
  *  @param  localTimeMs  事件发生时间,毫秒
  *  @return:  void
  *  @date:  2020/8/26 12:24
  */
void sendEvent(String userUniqueId, int appId, Map<String, Object> custom, String eventName, Map<String, Object> eventParams,long localTimeMs);

/**
  * 功能描述: 发送单条事件
  *
  *  @param  header      事件的公共属性,可以通过调用HeaderV3.Builder().build()构建一个header
  *  @param  eventName   事件名
  *  @param  eventParams 事件参数
  *  @param  localTimeMs  事件发生时间,毫秒
  *  @return:  void
  *  @date:  2020/9/28 22:00
  */
void sendEvent(Header header, String eventName, Map<String, Object> eventParams,long localTimeMs);

3.2 高性能场景

如果用户已经有自己的埋点,或者自己的服务端服务特别多,为了避免每个服务都集成DataFinder SDK,业务可以把自己的业务埋点都统一上报到一个kafka topic中去,然后新起一个集成了SDK的服务,来消费业务埋点信息,使用 SDK 提供的API把埋点信息上报到 DataFinder。流程如下:
图片
这种情况下,用户一般都是会把埋点的所有信息,包括埋点发生时间,上报到kafka。如果在使用服务端SDK的时候没有设置local_time_ms的话,事件发生时间会认为是SDK处理的时间,这个时间一般跟埋点的发生时间是有差异的。当系统繁忙,kafaka topic lag 的时候,这种差异就会更大,从而导致 DataFinder 上看到的同时段的事件量同预期相差比较大。

3.3 查看上报时延

  1. 服务端是上报到applog服务,可以在服务的granafa上进行看:

    http://{IP}:{port}/d/applog/applog?refresh=1m&orgId=1
    
  2. 可以通过curl的方式来获取平均时延。

    注意

    domain、host 需要替换成真实的值;app_id、user_unique_id 也替换成真实的app_id、user_unique_id。

    可以多次执行以下命令:

    curl -o /dev/null -s -w 'Total: %{time_total}s\n'   --location --request POST '${domain}/sdk/log' \
    --header 'User-Agent: DataRangers Java SDK' \
    --header 'Host: ${host}' \
    --header 'Content-Type: application/json' \
    --data '{
        "app_type": "app",
        "_format_name": "datarangers_svc_app_log_v3_server_sdk_1.5.6",
        "app_id": 10000000,
        "header": {
            "aid": 10000000,
            "custom": {
                "client": "fdaf",
                "__sdk_platform": "datarangers_sdk_1.5.6-release"
            },
            "device_id": 0,
            "timezone": 8,
            "tz_offset": 28800,
            "tz_name": "Asia/Shanghai",
            "user_unique_id": "dkqfn5ku9zk8",
            "app_id": 10000000
        },
        "user_unique_id": "xxxx",
        "event_v3": [
            {
                "event": "补贴查询",
                "params": {
                    "cardNum": "cardNum",
                    "year": "2021"
                },
                "local_time_ms": 1637734994778,
                "datetime": "2021-12-15 14:23:14"
            }
        ]
    }'
    
  3. 更多的curl方式:

    curl -w "@curl-format.txt" -o /dev/null -s  --location --request POST '${domain}/sdk/log' \
    --header 'User-Agent: DataRangers Java SDK' \
    --header 'Host: ${host}' \
    --header 'Content-Type: application/json' \
    --data '{
        "app_type": "app",
        "_format_name": "datarangers_svc_app_log_v3_server_sdk_1.5.6",
        "app_id": {替换成真实的app_id},
        "header": {
            "custom": {
                "client": "fdaf",
                "__sdk_platform": "datarangers_sdk_1.5.6-release"
            },
            "device_id": 0,
            "timezone": 8,
            "tz_offset": 28800,
            "tz_name": "Asia/Shanghai",
            "user_unique_id": "{替换成真实的user_unique_id}",
            "app_id": {替换成真实的app_id}
        },
        "user_unique_id": "xxx",
        "event_v3": [
            {
                "event": "补贴查询",
                "params": {
                    "cardNum": "cardNum",
                    "year": "2021"
                },
                "local_time_ms": 1637734994778,
                "datetime": "2021-12-15 14:23:14"
            }
        ]
    }'
    

    curl-format.txt的文件内容是:

    time_namelookup:  %{time_namelookup}s\n
                time_connect:  %{time_connect}s\n
         time_appconnect:  %{time_appconnect}s\n
        time_pretransfer:  %{time_pretransfer}s\n
           time_redirect:  %{time_redirect}s\n
      time_starttransfer:  %{time_starttransfer}s\n
                         ----------\n
              time_total:  %{time_total}s\n
    

3.4 如何处理发送失败的数据

发送失败的数据默认是会写到 eventSavePath (默认是logs/)目录下的error-datarangers.log文件下,下面介绍下一般处理方式:

  1. 如果可以容忍部分失败的话,就不处理。
  2. 配合提供的logagent(仅私有化提供,联系客户经理来获取)来监听失败的文件,进行失败的补上报。
  3. 自定义失败处理逻辑,比如将失败的消息发送再发送到另外的kafka-topic中,当系统恢复了之后再进行消费补偿。

Spring 注入callback:

@Configuration
public class SdkConfiguration {

//  @Bean
  public Callback callback() {
    return new Callback() {

      @Override
      public void onFailed(FailedData failedData) {
      // handle  failed data
      // cause   exception是异常信息
      // message 是上报的完整报文
        System.out.println(failedData.getCause());
        System.out.println(failedData.getException());
        System.out.println(failedData.getMessage());
      }
    };
  }
}

或者在初始化collector的时候设置一个:

EventCollector appEventCollector =  new AppEventCollector("app", dataRangersSDKConfigPropertiesInfo, new Callback() {
  @Override
  public void onFailed(FailedData failedData) {
    // handle the failed data
  }
})

3.5 上报性能调试

如果发现上报的qps不满足需求,可以处理:

  1. 扩服务app_log的服务。
  2. 调试sdk的相关配置:
    1. 增加 threadCount 的数量,可以根据上报时延,简单快速估算下(并不一定是线性关系): 1s/时延 * threadCount * batchSize
    2. 使用批量上报的方式。
  3. 联系 DataFiner 运维人员进行扩容。

4.常见问题处理

4.1. SDK 日志

SDK 的日志使用 slf4j 接口,可以支持logback和log4j的日志。所在的package为:com.datarangers,需要需要查看日志,请配置该package日志路径。另外也可以查看console下该package下的日志。
首先确定是否有ERROR 日志,以及检查启动参数配置是否符合预期。

4.2. 使用SDK上报事件,但是在finder系统中查询不到数据

按以下路径进行检查:

  1. 先检查 “使用方法” > "验证配置" 提供的 curl 命令验证基本配置。
  2. 检查 SDK 的版本,最后使用最新版本;检查配置 save 或者mode,确定上报是什么模式。当使用 FILE 上报模式的时候,数据只是保存到本地,不会上报。
  3. 查看日志是否有错误。
  4. 检查上报的事件名称是否符合规范,事件的local_time_ms是否正常。如果local_time_ms是一个未来的数据、或者是一个太老的数据(超过7天),就会被丢弃。
  5. 可以在 “数据管理” > “数据质量”,查看是否有异常的数据。
  6. 等5分钟,刷新页面,查看元数据是否存在。当元数据存在的时候,再去行为细查或者事件分析中去查询。
  7. 联系客户经理。

4.3. 事件上报成功,但是在finder行为细查中查看事件属性不存在

按以下路径进行检查:

  1. 在“数据管理”>“事件属性”中查看事件属性是否存在,以及属性类型是否符合预期。
  2. 在“数据管理”>“数据质量”中查看上报事件中是否存在“异常属性条数”。
  3. 在“数据管理”>“事件属性”查看该事件属性是事件属性、还是事件公共属性。
  4. 如果该属性在“any_event”事件下面,则是事件公共属性,否则是事件属性。
  5. 事件公共属性上报的时候是是放在header的custom字段下,即接口的custom参数。
  6. 事件属性是放在params下。

4.4. SDK 调试

SDK 的发送的核心逻辑在 com.datarangers.asynccollector.Consumer #run(),HTTP 发送的逻辑,最终使用 com.datarangers.util.HttpUtils #request进行发送。可以在相关地方进行debug 调试,进一步定位问题。

4.5. SSL 证书配置

  • 证书错误:PKIX path building failed: sun.security.provider.certpath

这个错误是由于本地 JDK 没有信任证书,建议客户优先导入证书,
或者配置:datarangers.sdk.httpConfig.trustDisable=true

  • 自定义证书路径配置

如果客户的证书位置不在默认位置,需要自定义,可以配置相关参数:

# 开启证书路径自定义配置
datarangers.sdk.httpConfig.customKeyTrustEnable=true

# 配置keyMaterial路径
datarangers.httpConfig.keyMaterialPath={keyMaterialPath}
# 配置keyPassword
datarangers.httpConfig.keyPassword={keyPassword}
# 配置storePassword
datarangers.httpConfig.storePassword={storePassword}
# 配置trustMaterial路径
datarangers.httpConfig.trustMaterialPath={trustMaterialPath}

# 信任策略,只支持self,all 两种
datarangers.httpConfig.trustStrategy=self

5. 注意事项
  1. 如果服务端能够获取到client_ip、device_model等用户的设备信息,可以使用入参为Header的接口上报,否则默认情况下,对于事件的公共属性均为 null 或 known。
  2. Saas 上用户属性需要先在系统中创建之后再上报,参考:User Profile API(SaaS查看)
  3. Item属性需要先在系统中创建之后再上报,参考:业务对象(Item)数据接入(SaaS查看)
  4. 业务对象内置的id属性,对应的字段名称为item_id,因此不需要再定义一个 id 属性。