You need to enable JavaScript to run this app.
最新活动
产品
解决方案
定价
合作与生态
支持与服务
开发者
了解我们
导航

Java SDK

更新时间:2023.05.29 17:11:57

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

1. 使用方法

1.1 引入 jar 包

如果您需要使用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>

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>
    
    <bean name="dataRangersSDKConfigProperties" class="com.datarangers.config.DataRangersSDKConfigProperties">
    
        <property name="domain" value="{domain}"/>
    
        <property name="mode" value="http"/>
    
        <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 配置参考

前缀都是datarangers.sdk

配置模块配置项含义备注
-mode上报模式(不区分大小写):http、file、kafka在java SDK 版本>=1.5.6 版本之后,建议使用新的该配置。当mode和save同时存在的时候,以mode为准。
saveboolean型变量,表示是否保存到本地文件, saas 只支持配置成 false。该配置在>=1.5.6 版本之后废弃,使用mode 配置。
env枚举类型,saas 表示云上,privatization表示私有化,非必须,sdk可以根据配置自动判定。
syncboolean类型,同步异步,默认为false,使用异步上报的方式。

HTTP

domain

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

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

headers.Hostdatarangers.sdk.headers为http请求中headers字段内容,在私有化环境中必须要添加Host。 其值可以在私有化环境Host的配置在安装部署的那台机器上,查看/home/{INSTALL_USER}/DataRangersDeploy/conf_rangers.yml中配置项sdk.report.host。 INSTALL_USER 为安装用户,一般是datarangers或者minibase。在saas上不需要进行配置。
threadCount异步线程池核心线程数里,默认是20。
httpConfig.requestTimeouthttp发送的请求超时时间,单位是毫秒,默认是10000。
httpConfig.connectTimeouthttp发送的连接超时时间,单位是毫秒,默认是10000。
httpConfig.socketTimeouthttp发送的socket超时时间,单位是毫秒,默认是20000。
httpConfig.keepAliveTimeouthttp连接池的keepAliveTimeout,单位是秒,默认是30。
sendBatchboolean类型,表示是否开启批量上报。默认是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.akopenapi的ak,请联系客户经理获取。
openapiConfig.skopenapi的sk,请联系客户经理获取。
FILEeventSaveName保存日志的文件名,需要保证文件的写权限,默认是 datarangers.log。
eventSavePath保存日志的文件路径,需要保证写权限和创建文件的权限,模式是 logs/
eventSaveMaxFileSize表示需要保存的日志文件的最大文件大小,单位为MB,默认是100。

eventFilePaths

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

说明

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

eventSaveMaxDays最多保留多少天的日志文件,超过这个时间的日志会被删除,默认是-1,即不删除文件。因此长时间运行的时候,为了避免占用过多磁盘,需要自行删除日志文件,或者配置一个合理的值,比如 7。
KAFKAbootstrapServerskafka的地址。使用kafka模式需要进行配置。
properties是一个map,需要配置的其他的kafka properties。kafkaProducer的参数参考:https://kafka.apache.org/0102/documentation.html#producerconfigs
verifyverify.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 模式

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

1.4.2.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.2.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.2.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.3 FILE 模式(只支持私有化)

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

1.4.4 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查看)
业务对象内置的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,即要使用appEventCollector或者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 注意事项

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

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。流程如下:

alt

这种情况下,用户一般都是会把埋点的所有信息,包括埋点发生时间,上报到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.常见问题处理
  1. SDK 日志

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

  2. 使用SDK上报事件,但是在finder系统中查询不到数据,按以下路径进行检查:

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

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

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

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