Java SDK,用来简化服务端埋点的复杂度。通过使用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>
增长分析的 SDK 支持多种上报模式,需要先选择使用模式。
模式 | 使用场景 | 部署复杂性 | 可靠性 | 上报性能 | 备注 |
---|---|---|---|---|---|
HTTP | 绝大多数场景都可以使用,如果跨网络时延比较高,可以使用批量方式。 | 简单 | 高 | 高,支持批量上报。 | 如果参考 “最佳实践”-->"查看上报时延"章节内容来查看上报的时延。 |
FILE | 不推荐 | 复杂 | 很高 | 低,写文件之后还需要使用logagent来进行上报。 | 无 |
KAFKA | 同一个网络,建议使用该模式。 | 简单 | 很高 | 高 | SDK版本>=1.5.6,私有化4.1版本(含)开始支持。 |
同一个网络推荐使用KAFKA模式上报,其他场景推荐使用 HTTP 的方式,同时使用 logagent 来补报因为网络抖动等原因导致失败的数据。
SDK 使用前,需要先初始化AppEventCollector,然后使用其提供的接口进行上报。
推荐使用配置的方式进行初始化。
datarangers.sdk.mode=http
表示使用HTTP模式。
需要配置domain、appkeys,不需要openapi相关配置,sdk版本需要 >= 1.5.7。
# saas-云原生版 配置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}
需要配置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 # [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 # openapi的ak, sk datarangers.sdk.openapiConfig.ak=${OPENAPI_AK} datarangers.sdk.openapiConfig.sk=${OPENAPI_SK}
配置domain。
# 私有化配置example # 使用 http 上报模式 datarangers.sdk.mode=http # sdk 上报地址,以http:// 或者 https:// 开头 datarangers.sdk.domain=${SDK_DOMAIN} #异步线程数量,当并发不够的时候可以调整该数据 #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
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
使用该模式,埋点事件只是记录到磁盘中,还需要配合logagent一起使用,数据才能上报到 DataFinder,关于logagent的使用,请联系客户经理获取。
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
bootstrapServers
为kafka的bootstrapServers 地址。datarangers.sdk.kafka.properties.xxx=xxx
的形式进行配置,kafkaProducer的参数参考:Kafka官网文档。推荐将初始化操作装配成 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> </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()); } }
DataRangersSDKConfigProperties properties = new DataRangersSDKConfigProperties(); // 设置模式 properties.setMode(SdkMode.HTTP); // 设置sdk domain。 注意设置成真实的参数 properties.setDomain(System.getenv("SDK_DOMAIN")); // 初始化collector EventCollector appEventCollector = new AppEventCollector("app", properties); EventCollector webEventCollector = new AppEventCollector("web", properties); EventCollector mpEventCollector = new AppEventCollector("mp", properties);
AppEventCollector 使用的参数有两个:appType 和 properties。
appType 只支持:
properties 前缀都是datarangers.sdk
。
配置模块 | 配置项 | 含义 | 备注 |
---|---|---|---|
不涉及 | mode | 上报模式(不区分大小写):http、file、kafka | 在java SDK 版本>=1.5.6 版本之后,建议使用新的该配置。当mode和save同时存在的时候,以mode为准。 |
env | 枚举类型,saas 表示云上,privatization表示私有化,非必须,sdk可以根据配置自动判定。 | 无 | |
sync | boolean类型,同步异步,默认为false,使用异步上报的方式。 | 无 | |
save | boolean型变量,表示是否保存到本地文件, saas 只支持配置成 false。 | 该配置在>=1.5.6 版本之后废弃,使用 | |
HTTP | domain | DataFinder 的域名或者ip,支持http和https,例如为 https://{具体域名地址}。
| 当使用http上报模式的时候,必须配置。 |
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 | 信任策略。
| ||
sendBatch | boolean类型,表示是否开启批量上报。默认是false,不使用批量。 | saas暂不支持批量上报。 | |
batchSize | 批量上报的数量 | ||
waitTimeMs | 批量上报,不足batchSize的时候,最大等待时间 | ||
appKeys.${SDK_APP_1} | SAAS环境,上报设置的appkeys,可以设置多个。多个可以的方式为:
| 注意替换成真实的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的参数参考:Kafka官网文档 | ||
verify | verify.url | 服务端埋点实时检测的url地址。 | SDK版本 >= 1.5.7 |
启动会打印如下日志,可以通过监测启动日志来确定配置是否符合预期。主要关注下sdkMode
、domain
、headers
等配置。
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
查看配置或自动日志,确定配置的 sdkMode
为 HTTP
,同时检查:
Debug验证请求/响应报文
Debug断点HttpUtils类167行(不同版本可能行数有所差异,但逻辑都是获取请求和响应报文)//以下截图为1.5.3版本
resustStr为响应报文,其中如果e不为0,说明上报失败了,反之表示成功上报
body为请求报文,如果响应报文了解到报错,可以获取下来看看数据格式是否正确
可以使用 curl 命令,查看是否能正常上报:
注意修改脚本中的 local_time_ms
和 datetime
字段为当前时间。
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 }
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 }
domain 替换成相应的配置, app_id 也要修改成相应的配置。
curl --location --request POST '${domain}/sdk/log' \ --header 'User-Agent: DataRangers Java SDK' \ --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 }
查看配置 datarangers.sdk.eventSavePath
的文件目录下是否有文件,是否有文件内容。校验文件内容是否正确,可以直接拷贝一行数据,使用http的方式进行上报。
查看kafka topic: sdk_origin_event
是否有数据上来,kafka 的使用方式参考文档:
Kafka订阅(私有化) 增长分析-火山引擎
使用时需要先注入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);
参考代码:
https://github.com/volcengine/datarangers-sdk-java/tree/main/datarangers-sdk-example
@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"))); }});
用户属性需要先在系统中创建之后再上报,参考: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"); }});
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);
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)); }});
如果需要使用服务端的方式上报匿名用户事件信息,需要把 user_unique_id 的值置为 ""(空字符串),然后设置其他能关联上的设备信息,比如device_id、web_id、anonymousId。如果用户登录之后,能拿到user_unique_id,并且希望登录实名之后的行为能跟匿名之前的行为关联起来,那么在设置正确的user_unique_id之后,还需要带上匿名的时候设置的 device_id、web_id 或 anonymousId。
当使用appEventCollector
上报时,deviceId即为device_id,当使用webEventCollector
、mpEventCollector
上报时,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());
用户 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());
先验证元数据,然后再在行为细查中去查看。
元数据:“数据管理”> “元数据管理”
事件发送成功之后,如果是第一次在新事件上报,最长可能需要等5分钟才能查看到。先检查元数据是否正常:
当事件在元数据中存在后,那么就可以直接在用户细查里面去查询。
(1)先在“数据管理”>“元数据管理”>“用户属性”中验证用户属性是否存在,并检查属性类型是否符合预期。
(2)然后在“分析工具”>“用户分析”>“用户细查”中,点击并进入其中一个用户详情页面,查看最近上报的用户属性。
先在“数据管理”>“业务对象”下查看业务对象是否绑定到了事件。然后可以在行为细查进行查询,在行为细查里面会显示item_id。
如果想查看业务对象的属性值,可以在“分析工具” > “高级分析”> “业务对象分析”里面去查看。
在“数据管理” > “一般事件” > “验证埋点”上选择“服务端埋点”,然后输入需要检测的 user_unique_id,然后将url参数设置到 verify.url上即可。
当有该用户的埋点数据上报时,会看到上报的内容数据。
注意
com.datarangers.sender.VerifySender
,可以debug下。如果页面上查询不到数据,可以尝试下刷新页面。
注意:【SaaS-非云原生版本】历史数据导入需要隔天才能查看,【SaaS-云原生】、【私有化】版本可实时查看
如果数据的客户端时间超过七天,正常情况下无法导入,如需导入历史数据,需在header
的custom
字段中增加历史数据的标识__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);
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);
如果用户已经有自己的埋点,或者自己的服务端服务特别多,为了避免每个服务都集成DataFinder SDK,业务可以把自己的业务埋点都统一上报到一个kafka topic中去,然后新起一个集成了SDK的服务,来消费业务埋点信息,使用 SDK 提供的API把埋点信息上报到 DataFinder。流程如下:
这种情况下,用户一般都是会把埋点的所有信息,包括埋点发生时间,上报到kafka。如果在使用服务端SDK的时候没有设置local_time_ms的话,事件发生时间会认为是SDK处理的时间,这个时间一般跟埋点的发生时间是有差异的。当系统繁忙,kafaka topic lag 的时候,这种差异就会更大,从而导致 DataFinder 上看到的同时段的事件量同预期相差比较大。
服务端是上报到applog服务,可以在服务的granafa上进行看:
http://{IP}:{port}/d/applog/applog?refresh=1m&orgId=1
可以通过curl的方式来获取平均时延。
注意
domain 需要替换成真实的值;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 '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" } ] }'
更多的curl方式:
curl -w "@curl-format.txt" -o /dev/null -s --location --request POST '${domain}/sdk/log' \ --header 'User-Agent: DataRangers Java SDK' \ --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
发送失败的数据默认是会写到 eventSavePath
(默认是logs/)目录下的error-datarangers.log文件下,下面介绍下一般处理方式:
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 } })
如果发现上报的qps不满足需求,可以处理:
1s/时延 * threadCount * batchSize
。SDK 的日志使用 slf4j 接口,可以支持logback和log4j的日志。所在的package为:com.datarangers
,需要需要查看日志,请配置该package日志路径。另外也可以查看console下该package下的日志。
首先确定是否有ERROR 日志,以及检查启动参数配置是否符合预期。
按以下路径进行检查:
按以下路径进行检查:
SDK 的发送的核心逻辑在 com.datarangers.asynccollector.Consumer #run()
,HTTP 发送的逻辑,最终使用 com.datarangers.util.HttpUtils #request
进行发送。可以在相关地方进行debug 调试,进一步定位问题。
这个错误是由于本地 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
id
属性。