在项目中集成神策分析 SDK

Java SDK 已经开源到 GitHub,链接地址:https://github.com/sensorsdata/sa-sdk-java。并且已经发布到 maven 中央仓库。您可以选择以下方式进行集成:

使用 Maven 集成

	<dependencies>
        <!-- 引入神策分析 SDK -->
        <dependency>
            <groupId>com.sensorsdata.analytics.javasdk</groupId>
            <artifactId>SensorsAnalyticsSDK</artifactId>
            <version>3.2.1</version>
        </dependency>
	</dependencies>
CODE

使用 Gradle 集成

dependencies {
    compile 'com.sensorsdata.analytics.javasdk:SensorsAnalyticsSDK:3.2.1'
}
CODE

依赖本地 lib

如果您的项目没有使用任何 jar 包管理工具,可以选择从 GitHub 下载指定 release 版本,进行本地依赖;

神策分析 SDK 介绍

Java SDK 主要由以下两个组件组成:

  • ISensorsAnalytics :对外暴露的生成数据的顶层接口对象,构造函数需要传入一个 consumer 实例对象
  • Consumer :提供不同能力的数据发送机制

SDK Consumer 介绍

DebugConsumer

用于校验 SDK 是否集成正常,以及数据发送到神策服务器是否正常。

由于 Debug 模式是为了校验 SDK 集成以及数据传输地址是否正常而提供的模式,该模式会逐条校验数据并在校验失败时抛出异常,性能远低于正常模式。

即仅适用于本地调试环境,任何线上服务请务必不要使用该模式。

构造参数说明:

参数名参数类型是否必填说明
serverUrlString数据接收地址
writeDataboolean数据是否入库,true:表示数据入库;false:表示数据不入库(只展示不保存)

代码示例:

// 从神策服务器数据接入引导页面获取的数据接收的 URL
String serverUrl = ""; 
ISensorsAnalytics sa = new SensorsAnalytics(new DebugConsumer(serverUrl,true));
CODE

ConcurrentLoggingConsumer

用于将数据输出到指定文件目录并按天切割生成日志,并使用 LogAgent 等工具导入,该工具能保证导入不重复、不遗漏。推荐在生产环境中使用 ConcurrentLoggingConsumer 导入数据。支持多个进程写同一个目录(目录不能是 nas、nfs 类文件系统),生成的文件始终是带日期后缀的,默认每天一个。

构造参数说明:

参数名参数类型是否必填说明
filenamrPrefixString数据保存文件路径
bufferSizeintConsumer 内部缓存大小; 默认 8192 字节数
lockFileNameString

用于 windows 环境下,指定一个文件地址用于文件锁;用于避免 LogAgent 和 SDK 同时读写文件

splitModeLogSplitMode生成日志切割模式,默认是 LogSplitMode.DAY;可选 LogSplitMode.HOUR

代码示例:

// 将数据输出到 /data/sa 下的 access.log.2017-01-11 文件中,每天一个文件;对应目录文件需要自己创建,日志文件后缀会自动生成
final SensorsAnalytics sa = new SensorsAnalytics(new ConcurrentLoggingConsumer("/data/sa/access.log"));

// !! 注意 !! 如果是在 Windows 环境下使用 ConcurrentLoggingConsumer 并使用 LogAgent 发送数据,
// 需要额外通过构造函数的第二个参数指定一个文件地址用于文件锁,例如:
// SensorsAnalytics sa = new SensorsAnalytics(new ConcurrentLoggingConsumer("D:\\data\\service", "D:\\var\\sa.lock"));
// 若该文件与数据文件同目录,配置 LogAgent 的 pattern 时请不要匹配到这个文件。
CODE

BatchConsumer 

批量发送数据的 Consumer,当数据达到指定的量时(默认50条,最多可指定1000条),才将数据进行发送。也可以调用 flush() 方法去强制发送。注意:BatchConsumer 默认不会抛出异常,如果网络出现异常,导致数据发送失败,会造成数据丢失。如果你想获取对应的异常信息,需要通过初始化函数  BatchConsumer(final String serverUrl, final int bulkSize, final boolean throwException) 指定 throwException 参数为 true。

通常用于导入小规模历史数据,或者离线 / 旁路导入数据的场景。由于是网络直接发送数据,如果网络出现异常可能会导致数据丢失,因此不推荐在线上服务中使用。

若使用网络发送模式,不想发送异常丢失数据,推荐使用 FastBatchConsumer  模式

构造参数说明:

参数名参数类型是否必填说明
serverUrlString数据接收地址
bulkSizeint触发 flush 操作阈值,当内存缓存队列达到该值时,将缓存中的数据批量上报,默认 50
timeoutSecint

网络请求超时时间,默认 3S

maxCacheSizeint最大缓存刷新大小,若超过该值,立即触发 flush 操作,默认为 0 ,根据 bulkSize 来进行判断
throwExceptionboolean网络请求时发生异常是否抛出,true:抛出;false:不抛出;默认不抛出

代码示例:

// 从神策分析获取的数据接收的 URL
final String SA_SERVER_URL = "YOUR_SERVER_URL";
// 当缓存的数据量达到50条时,批量发送数据
final int SA_BULK_SIZE = 50;
// 数据同步失败不抛出异常
final boolean THROW_EXCEPTION = false;
// 内存中数据最大缓存条数,如此值大于0,代表缓存的数据会有条数限制,最小 3000 条,最大 6000 条。否则无条数限制。
final int MAX_CACHE_SIZE = 0;
// 使用 BatchConsumer 初始化 SensorsAnalytics
// 不要在任何线上的服务中使用此 Consumer
final SensorsAnalytics sa = new SensorsAnalytics(new BatchConsumer(SA_SERVER_URL, SA_BULK_SIZE, MAX_CACHE_SIZE, THROW_EXCEPTION));
CODE

FastBatchConsumer

针对 BatchConsumer 模式的一种优化模式,通过提供回调函数收集发送失败的数据。

回调函数会收集放入缓存失败的数据以及网络请求过程中发送失败的数据,需要自行处理发送失败的数据。

构造参数说明:

参数名参数类型是否必填说明
serverUrlString数据接收地址
timingboolean消费者线程运行时,true:表示消费者线程定时运行执行 flush 操作; false:表示消费者线程定时运行时检查缓存队列是否达到 bulkSize,进行 flush 操作
bulkSizeint触发 flush 操作阈值,当内存缓存队列达到该值时,将缓存中的数据批量上报,默认 50
timeoutSecint

网络请求超时时间,默认 3S

maxCacheSizeint内存缓存队列保存记录最大条数,默认 6000,上限 10000,下限 1000
flushSecint消费者线程定时执行频率,默认 1S
callbackCallback回调函数返回失败数据

代码示例:

    // 从神策分析获取的数据接收的 URL
    final String SA_SERVER_URL = "YOUR_SERVER_URL";
    //是否定时触发 flush 操作
    final boolean TIMING = true;
	// 当缓存的数据量达到50条时,批量发送数据
    final int SA_BULK_SIZE = 50;
	// 数据同步失败不抛出异常
    final boolean THROW_EXCEPTION = false;
	// 内存中数据最大缓存条数,如此值大于0,代表缓存的数据会有条数限制,最小 3000 条,最大 6000 条。否则无条数限制。
    final int MAX_CACHE_SIZE = 0;
	// 使用 BatchConsumer 初始化 SensorsAnalytics
	// 不要在任何线上的服务中使用此 Consumer
    final SensorsAnalytics sa = new SensorsAnalytics(new FastBatchConsumer(SA_SERVER_URL, TIMING, SA_BULK_SIZE,
        MAX_CACHE_SIZE, new Callback() {
      @Override
      public void onFailed(FailedData failedData) {
          // 处理发送失败的数据
      }
    }));
CODE


ConsoleConsumer

用于将数据输出到特定 Writer,一般用于在生产环境的 Java 程序中处理历史数据,生成日志文件并使用 BatchImporter 等工具导入

构造参数说明:

参数名参数类型是否必填说明

writer

Writer指定 Writer

代码示例:

// 将数据输出到标准输出
final Writer writer = new PrintWriter(System.out);

// 使用 ConsoleConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new ConsoleConsumer(writer));

// 使用神策分析记录用户行为数据
// ...

// Flush the writer
writer.flush();
CODE

LoggingConsumer (已不推荐使用)

已不推荐在生产环境中使用,因为在多进程下文件切分可能有问题。已使用 LoggingConsumer 的客户建议按照如下步骤切换到 ConcurrentLoggingConsumer;

ISensorsAnalytics API 介绍

接口方法名称请求参数返回参数描述
setEnableTimeFreeboolean  true:表示开启历史数据导入模式,默认关闭void开启导入历史数据模式

registerSuperProperties

SuperPropertiesRecord:公共属性实体

void给事件属性设置全局公共属性,避免给单个事件都添加

clearSuperProperties

-void清除设置的全局公共属性

track

EventRecord:事件信息实体,填充用户行为属性记录

void记录用户行为埋点操作

trackSignUp

  • loginId:登录 ID
  • anonymousId:匿名 ID
void记录匿名用户和登录 ID 绑定操作,用于识别用户登录前后的行为序列
profileSetUserRecord:用户信息实体,填充用户属性记录void记录单个用户的属性信息
profileSetOnceUserRecord:用户信息实体,填充用户属性记录void首次设置用户属性;与 profileSet 接口不同的是:在这个用户的 profile 中已经存在,则不处理,否则,新创建
profileIncrementUserRecord:用户信息实体,填充用户属性记录void为指定用户的一个或多个数值类型的属性累加一个数值,若该属性不存在,则创建它并设置默认值为0。属性取值只接受 Number类型
profileAppendUserRecord:用户信息实体,填充用户属性记录void为指定用户的一个或多个数组类型的属性追加字符串,属性取值类型必须为 java.util.List,且列表中元素的类型 必须为 String
profileUnsetUserRecord:用户信息实体,填充用户属性记录void删除指定用户已存在的一条或者多条属性
profileDeleteUserRecord:用户信息实体,填充用户属性记录void删除指定用户所有属性
itemSetItemRecord:维度表信息实体void增加item 记录
itemDeleteItemRecord:维度表信息实体void删除 item
flush-void立即发送缓存中的所有日志(非调试或程序关闭时,请不要主动调用该方法
shutdown-void停止SensorsDataAPI所有线程,API停止前会清空所有本地数据

用户唯一标识

在服务端应用中,神策分析也要求为每个事件设置用户的 Distinct ID,这有助于神策分析提供更准确的留存率等数据。

对于注册用户,推荐使用客户业务系统中的用户 ID 作为 Distinct ID,不建议使用用户名、Email、手机号码等可以被修改的信息作为 Distinct ID;

对于未注册的匿名用户,获取用户匿名 ID 的方法如下:

  1. 后端获取前端 JavaScript SDK 生成的匿名 ID 的方式:
    可以在 Cookie 里面找到 key 为 sensorsdata2015jssdkcross 的 value 值然后进行 decodeURIComponent 解码,最后通过 JSON.parse 方法得到一个对象,对象里面的 distinct_id 的值即为所需要的 ID
    (注意,如果前端已经调用过 login 方法,那么此时 distinct_id 为登录 ID,所以需要先获取 first_id 字段的值,如果获取不到,就去获取 distinct_id 的值)。
  2. 如果 App 中嵌入了  Login 方法,需要客户端使用神策提供的获取匿名 ID 接口,将匿名 ID 传给服务端,服务端使用客户端传过来的匿名 ID 作为 Distinct ID。

所有的 track  profile 系列方法都必须同时指定用户 ID(distinctId)和用户 ID 是否为登录 ID (isLoginId) 这两个参数,以便明确告知神策分析用户 ID 的类型。

用户注册/登录

通过 trackSignUp() 将匿名 ID 和登录 ID 关联,以保证用户分析的准确性。例如:

// 前端的匿名 ID,获取匿名 ID 的方式参考上文
String anonymousId = "9771C579-71F0-4650-8EE8-8999FA717761";

String registerId = "0012345678";
// 用户注册/登录时,将用户登录 ID 与匿名 ID 关联
sa.trackSignUp(registerId, anonymousId);
CODE

注意以下问题:

  • trackSignUp() 建议在用户注册/登录时调用。如果客户端也有采集任意事件,在注册/登录时,也需要在客户端调用一次关联方法 login() 将匿名 ID 和登录 ID 关联。 注册/登录时,客户端和服务端都各自调用一次关联方法的原因如下:
    • 一对一关联机制下,避免出现用户注册/登录时,客户端的关联信息 $SignUp 事件没有发送成功/延迟发送到神策系统,而服务端触发的事件( $is_login_id=true )先发送到神策系统中,导致登录 ID 自关联,从而导致登录 ID 无法再和匿名 ID 关联,客户端匿名行为和登录后的行为识别两个用户的行为。
    • 客户端调用一次关联方法 login() 的作用,除了将匿名 ID 和 登录 ID 关联之外,还会会将客户端标记用户的 distinctId 值从匿名 ID 切换为登录 ID。这样查看用户行为序列时,可以很好的根据 distinctId 的值判断用户行为是登录后的行为还是匿名行为。因此强烈建议用户登录/注册时,在客户端调用一次 login() 的同时,在服务端也调用一次 trackSignUp() 。
  • 如果服务端只在用户登录成功之后,才会采集相关事件或者设置用户属性,要保证 track 事件/profileSet 设置用户属性($is_login_id 设置为 true)的代码在 trackSignUp() 方法之后调用,从而可以保证先将匿名 ID 和登录 ID 关联之后,再采集登录用户的行为事件/设置用户属性。
  • 在神策分析 1.13 版本之前,多次调用 trackSignUp() /login()时,只有第一次关联行为是有效的。神策分析 1.13 版本之后提供了多设备 ID 关联的方法。更详细的说明请参考 标识用户,并在必要时联系我们的技术支持人员。

追踪事件

第一次接入神策分析时,建议先追踪 3~5 个关键的事件,只需要几行代码,便能体验神策分析的分析功能。例如:

  • 图片社交产品,可以追踪用户浏览图片和评论事件
  • 电商产品,可以追踪用户注册、浏览商品和下订单等事件

神策分析 SDK 初始化成功后,可以通过 track() 记录事件,必须包含用户 ID(distinctId)、用户 ID 是否为登录 ID (isLoginId)、事件名(eventName)这三个参数,同时可以传入一个 EventRecord 对象,为事件添加自定义事件属性。以电商产品为例,可以这样追踪一次购物行为:

// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new SensorsAnalytics.ConcurrentLoggingConsumer("您的日志文件路径"));

// 用户的 Distinct ID
String distinctId = "ABCDEF123456789";

// 用户浏览商品
{
     EventRecord lookRecord = EventRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.FALSE)
           .setEventName("ViewProduct")
           .addProperty("$time", new Date())  // '$time' 属性是系统预置属性,表示事件发生的时间,如果不填入该属性,则默认使用系统当前时间
           // '$ip' 属性是系统预置属性,如果服务端中能获取用户 IP 地址,并填入该属性,神策分析会自动根据 IP 地址解析用户的省份、城市信息
          .addProperty("$ip", "123.123.123.123")
          .addProperty("ProductId", "123456")
          .build();
    // 记录用户浏览商品事件
    sa.track(lookRecord);
}

// 用户订单付款
{
	// 订单中的商品 ID 列表
    List<String> productIdList = new ArrayList<String>();
    productIdList.add("123456");
    productIdList.add("234567");
    productIdList.add("345678");
    EventRecord lookRecord = EventRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.FALSE)
           .setEventName("PaidOrder")
           // '$ip' 属性是系统预置属性,如果服务端中能获取用户 IP 地址,并填入该属性,神策分析会自动根据 IP 地址解析用户的省份、城市信息
           .addProperty("$ip", "123.123.123.123")
           .addProperty("OrderId", "123456")
           .addProperty("ProductIdList", productIdList)
           .build();
    // 记录用户订单付款事件
    sa.track(lookRecord);
}
CODE

通过 调试模式 ,可以校验追踪的事件及属性是否正确。正常模式下,数据导入后,在神策分析中稍等片刻,便能看到追踪结果。

事件属性

如前文中的样例,追踪的事件可以设置自定义的事件属性,例如浏览商品事件中,将商品 ID、商品分类等信息作为事件属性。在后续的分析工作中,事件属性可以作为统计过滤条件使用,也可以作为维度进行多维分析。对于事件属性,神策分析有一些约束:

  • 事件属性是一个 EventRecord 对象;
  • EventRecord 中每个元素描述一个属性,Key 为属性名称,必需是 String 类型;
  • EventRecord 中,每个元素的 Value 是属性的值,支持 StringBooleanNumberList<String>  Date。

对于神策分析中事件属性的更多约束,请参考 数据格式在开发多线程程序时,开发者不能在线程间复用传入的属性对象

系统预置属性

如前文中样例,事件属性中以 '$' 开头的属性为系统预置属性,在自定义事件属性中填入对应 '$' 开头的属性值可以覆盖这些预置属性:

  • $ip - 填入该属性,神策分析会自动根据 IP 地址解析用户的省份、城市信息,该属性值为 String 类型;
  • $time - 填入该属性,神策分析将事件时间设置为属性值的时间,该属性值必须为 Date 类型。请注意,神策分析默认会过滤忽略 2 年前或 1 小时后的数据,如需修改请联系我们;
  • $project - 填入该属性,神策分析某些导入工具例如 LogAgent (LogAgent 的配置中未指定 project 参数时)会将数据导入指定项目。

关于其他更多预置属性,请参考  数据格式 中 '预置属性' 一节。

事件公共属性

特别地,如果某个事件的属性,在所有事件中都会出现,可以通过 registerSuperProperties() 将该属性设置为事件公共属性。例如将服务器的应用版本及机房地址设置为事件的公共属性,设置方法如下:

//设置公共属性,以后上传的每一个事件都附带该属性
SuperPropertiesRecord propertiesRecord = SuperPropertiesRecord.builder()
        .addProperty("ServerVersion", "1.2")
        .addProperty("Location", "Beijing")
        .build();
sa.registerSuperProperties(propertiesRecord);
CODE

成功设置事件公共属性后,再通过 track() 追踪事件时,事件公共属性会被添加进每个事件中,例如:

String distinctId = "ABCDEF123456789";
EventRecord loginRecord = EventRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.FALSE)
        .setEventName("UserLogin")
        .addProperty("$ip", "123.123.123.123")
        .build();
// 追踪用户登录事件
sa.track(loginRecord);
CODE

在设置事件公共属性后,实际发送的事件中会被加入 ServerVersion  Location 属性,等价于

String distinctId = "ABCDEF123456789";
EventRecord loginRecord = EventRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.FALSE)
        .setEventName("UserLogin")
        .addProperty("$ip", "123.123.123.123")
        .addProperty("ServerVersion", "1.2")
        .addProperty("Location", "Beijing")
        .build();
// 追踪用户登录事件
sa.track(loginRecord);
CODE

使用 clearSuperProperties() 会删除所有已设置的事件公共属性。

当事件公共属性和事件属性的 Key 冲突时,事件属性优先级最高,它会覆盖事件公共属性。

设置用户属性

为了更准确地提供针对人群的分析服务,神策分析 SDK 可以设置用户属性,如年龄、性别等。用户可以在留存分析、分布分析等功能中,使用用户属性作为过滤条件或以用户属性作为维度进行多维分析。

使用 profileSet() 设置用户属性:

String distinctId = "ABCDEF123456789";
UserRecord userRecord = UserRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
        .addProperty("Sex", "Male") // 设置用户性别属性(Sex)为男性
        .build();
sa.profileSet(userRecord);

userRecord = UserRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
        .addProperty("UserLv", "Elite VIP") // 设置用户等级属性(Level)为 VIP
        .build();
sa.profileSet(userRecord);
CODE

对于不再需要的用户属性,可以通过 profileUnset() 接口将属性删除。

用户属性中,属性名称与属性值的约束条件与事件属性相同,详细说明请参考 数据格式

记录初次设定的属性

对于只在首次设置时有效的属性,我们可以使用 profileSetOnce() 记录这些属性。与 profileSet() 接口不同的是,如果被设置的用户属性已存在,则这条记录会被忽略而不会覆盖已有数据,如果属性不存在则会自动创建。因此,profileSetOnce() 比较适用于为用户设置首次激活时间、首次注册时间等属性。例如:

String distinctId = "ABCDEF123456789";
UserRecord firstVisitRecord = UserRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
        .addProperty("AdSource", "App Store")
        .build();
sa.profileSetOnce(firstVisitRecord);
CODE

列表类型的属性

对于用户喜爱的电影、用户点评过的餐厅等属性,可以记录列表型属性。需要注意的是,列表型属性中的元素必须为 String 类型,且元素的值会自动去重。关于列表类型限制请见 数据格式 1.7.4 属性长度限制。

String distinctId = "ABCDEF123456789";

// 电影列表
List<String> movies = new ArrayList<String>();
movies.add("Sicario");
movies.add("Love Letter");

// 游戏列表
List<String> games = new ArrayList<String>();
games.add("Call of Duty");
games.add("Halo");

// 用户属性
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("movies", movies);
properties.put("games", games);

// 传入properties,设置用户喜欢的电影属性(movies)和喜欢的游戏属性(games)
// 设置成功后,"movies" 属性值为 ["Sicario", "Love Letter"];"games" 属性值为 ["Call of Duty", "Halo"]
UserRecord appendRecord = UserRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
        .addProperty("movies", movies)
        .addProperty("games", games)
        .build();
sa.profileAppend(appendRecord);

// 传入属性名称和需要插入属性的值,设置用户喜欢的电影属性(movies)
// 设置成功后 "movies" 属性值为 ["Sicario", "Love Letter", "Dead Poets Society"]
UserRecord appendRecord = UserRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
        .addProperty("movies", "Dead Poets Society")
        .build();
sa.profileAppend(appendRecord);

CODE

物品元数据上报

在神策推荐项目中,客户需要将物品元数据上报,以开展后续推荐业务的开发与维护。神策分析 SDK 提供了设置与删除物品元数据的方法。

item_id(物品 ID ) item_type (物品所属类型)共同组成了一个物品的唯一标识。所有的 item 系列方法都必须同时指定物品 ID 及物品所属类型这两个参数,来完成对物品的操作。

设置物品

直接设置一个物品,如果已存在则覆盖。除物品 ID 与物品所属类型外,其他物品属性需在 properties 中定义。

物品属性中,属性名称与属性值的约束条件与事件属性相同,详细说明请参考 数据格式

public void itemSet(String itemType, String itemId, Map<String, Object> properties);

// 例如
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("name", "C++ Primer");
properties.put("price", 31.54);
sensorsAnalytics.itemSet("book", "0321714113", properties);
CODE

删除一个物品

如果物品不可被推荐需要下线,删除该物品即可,如不存在则忽略。

除物品 ID 与 物品所属类型外,不解析其他物品属性。

public void itemDelete(String itemType, String itemId, Map<String, Object> properties);

// 例如
ItemRecord deleteRecord = ItemRecord.builder().setItemId("0321714113").setItemType("book")
        .build();
sensorsAnalytics.itemDelete(deleteRecord);
CODE

获取数据接收地址

登录神策分析主页,点击【数据接入】→ 【埋点数据接入】→ 【数据接入引导】,获取数据接收地址

如果使用神策分析 Cloud 服务,需获取的配置信息为:

如果用户使用单机版私有部署的神策分析,默认的配置信息为:

  • 数据接收地址: http://{$host_name}:8106/sa?project={$project_name}
    (注:神策分析 1.7 及之前的版本,单机版私有部署默认端口号为 8006)

如果用户使用集群版私有部署的神策分析,默认的配置信息为:

  • 数据接收地址: http://{$host_name}:8106/sa?project={$project_name}

其中 {$host_name} 可以是集群中任意一台机器。

如果私有部署的过程中修改了 Nginx 的默认配置,或通过 CDN 等访问神策分析,则请咨询相关人员获得配置信息。

初始化 SDK

由于 SDK 初始化操作,会创建一个与之对应的内存缓存队列,所以请谨慎创建 SDK 实例对象,推荐在应用程序启动时全局初始化一次,然后进行全局调用。避免在方法内部进行初始化操作,程序运行过程中创建多个实例对象,导致 OOM 现象。

在很多客户的使用过程中,我们观察到因为使用 SDK 方式不对,而导致影响整体业务逻辑的性能。所以在按照官网教程完成初始化之后,在采集日志的过程中,不推荐主动调用 flush 操作。因为 flush 操作,会由 SDK 内部主动触发。

如果每次 track 之后,进行 flush 操作,会使得内部缓存队列失效,高并发情况下,会引起频繁锁竞争,会导致服务性能下降。

在普通 Java 程序中初始化 SDK 

在程序启动时(如 public static void main(String[] args) 方法中),调用构造函数 new SensorsAnalytics(Consumer) 初始化 Java SDK 实例(需要全局初始化一次即可)

// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new ConcurrentLoggingConsumer("您的日志文件路径"));

// 用户的 Distinct ID
String distinctId = "ABCDEF123456789";

// 记录用户登录事件
EventRecord loginRecord = EventRecord.builder().setDistinctId(distinctId).isLoginId(Boolean.TRUE)
                              .setEventName("UserLogin")
                              .build();
sa.track(loginRecord);

// 使用神策分析记录用户行为数据
// ...
CODE

在 Spring 框架里面初始化 SDK

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

使用注解创建 Bean 对象

@Configuration
public class SensorsConfig {

    @Bean(destroyMethod = "shutdown")
    public ISensorsAnalytics init() throws IOException {
        //本地日志模式(此模式会在指定路径生成相应的日志文件)
        return new SensorsAnalytics(new ConcurrentLoggingConsumer("您的日志文件路径"));
        //debug 模式(此模式只适用于测试集成 SDK 功能,千万不要使用到生产环境!!!)
        //return new SensorsAnalytics(new DebugConsumer("数据接收地址", true));
        //网络批量发送模式(此模式在容器关闭的时候,如果存在数据还没有发送完毕,就会丢失未发送的数据!!!)
        //return new SensorsAnalytics(new BatchConsumer("数据接收地址"));
    }

}
CODE

使用 xml 配置文件创建 Bean 对象

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <!-- 本地日志模式(此模式会在指定路径生成相应的日志文件) -->
    <bean id="loggingConsumer" class="com.sensorsdata.analytics.javasdk.consumer.ConcurrentLoggingConsumer">
        <constructor-arg name="filenamePrefix" value="您的日志文件路径"/>
    </bean>

    <!-- debug 模式(此模式只适用于测试集成 SDK 功能,千万不要使用到生产环境!!!) -->
    <!--<bean id="debugConsumer" class="com.sensorsdata.analytics.javasdk.consumer.DebugConsumer">
        <constructor-arg name="serverUrl" value="数据接收地址"/>
        <constructor-arg name="writeData" value="true"/>
    </bean>-->

    <!-- 网络批量发送模式(此模式在容器关闭的时候,如果存在数据还没有发送完毕,就会丢失未发送的数据!!!)-->
    <!--<bean id="batchConsumer" class="com.sensorsdata.analytics.javasdk.consumer.BatchConsumer">
        <constructor-arg name="serverUrl" value="数据接收地址"/>
    </bean>-->

    <!-- 此处选择合适的模式进行数据采集操作(此处选择本地日志模式) -->
    <bean id="sensorsAnalytics" class="com.sensorsdata.analytics.javasdk.SensorsAnalytics" destroy-method="shutdown">
        <constructor-arg name="consumer" ref="loggingConsumer"/>
    </bean>

</beans>
CODE