Golang SDK 使用说明

在使用前,請先閱讀 數據模型 的介绍。

1. 整合神策分析 SDK

在 Golang 程式碼中整合 神策分析 SDK,使用神策分析採集並分析用戶數據。

我們推薦 Golang 官方工具管理 Golang 專案並取得神策分析 SDK:

go get github.com/sensorsdata/sa-sdk-go
JAVA

或更新本地已經存在的sdk:

go get -u github.com/sensorsdata/sa-sdk-go
CODE

也可以從 GitHub 下載 神策分析 SDK 的原始碼。

SDK 需要 Golang 1.6 以上,不依賴第三方函式庫。目前 Golang SDK 不支援 Windows。

2. 初始化神策分析 SDK

2.1 取得設定資訊

首先從神策分析的主頁中,取得數據接收的 URL 和 Token(Cloud 版)。

如果使用神策分析 Cloud 服務,需取得的設定資訊為:

  • 數據接收網址,建議使用不帶埠號的:http://{$service_name}.datasink.sensorsdata.cn/sa?project={$project_name}&token={$project_token}
  • 數據接收網址,帶埠號的:http://{$service_name}.cloud.sensorsdata.cn:8106/sa?project={$project_name}&token={$project_token}

如果用戶使用單機版私有部署的神策分析,預設的設定資訊為:

  • 數據接收網址:http://{$host_name}:8106/sa?project={$project_name}(注:神策分析 1.7 及之前的版本,單機版私有部署預設埠號為 8006)

如果用戶使用叢集版私有部署的神策分析,預設的設定資訊為:

  • 數據接收網址:http://{$host_name}:8106/sa?project={$project_name}

其中 {$host_name} 可以是叢集中任意一台電腦。

如果私有部署的過程中修改了 Nginx 的預設設定,或透過 CDN 等連接神策分析,則請諮詢相關人員取得設定資訊。

2.2 在程式中初始化 SDK

在程式中初始化的程式碼段中建構神策分析 SDK 的執行個體:

import sdk "github.com/sensorsdata/sa-sdk-go"

// 從神策分析設定頁面中取得數據接收的 URL
SA_SERVER_URL := "YOUR_SERVER_URL"

// 初始化一個 Consumer,用於數據傳送
// DefaultConsumer 是同步傳送數據,因此不要在任何線上的服務中使用此 Consumer
consumer, err := sdk.InitDefaultConsumer(SA_SERVER_URL, 10000)
//...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
defer sa.Close()

properties := map[string]interface{}{
        "price": 12,
        "name": "apple",
}

// 記錄用戶事件
err = sa.Track("ABCDEFG1234567", "ViewProduct", properties, false)
CODE

其中 YOUR_SERVER_URL 是前文中從神策分析取得的數據接收的 URL。用戶程式應該一直持有該執行個體,直到程式結束。程式退出前,需要使用 Close() 方法表示關閉,否則可能遺失部分快取的數據。

使用神策分析時,引入神策分析 SDK 後首先初始化一個 consumer,接著初始化神策分析物件。

  • 初始化神策分析物件的介面為:
// c 為 consumer,projectName 為專案名,timeFree 為是否匯入歷史數據
// 預設情況下,神策會過濾發生時間比較久遠數據(例如 10 天之前,具體取決於伺服器端設定),如果想匯入歷史數據,可以透過開啟 timeFree 選項來跳過這個限制。
func InitSensorsAnalytics(c consumers.Consumer, projectName string, timeFree bool) SensorsAnalytics
CODE
  • 例子
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
// 退出函數前呼叫 Close,資源回收,傳送快取中的數據
defer sa.Close()
// ...
CODE

至此,我們已經可以正常使用神策分析 SDK 了。需了解更多關於 SDK 的使用方法,可以跳到本文末尾的控制神策分析 SDK 一節。

3. 追蹤事件

第一次接入神策分析時,建議先追蹤 3~5 個關鍵的事件,只需要幾行程式碼,便能體驗神策分析的分析功能。例如:

  • 圖片社交產品,可以追蹤用戶瀏覽圖片和評論事件
  • 電商產品,可以追蹤用戶註冊、瀏覽商品和下訂單等事件

用戶透過 Track() 介面記錄事件,對於任何事件,必須包含用戶識別符(Distinct ID)和事件名(event)兩個參數。同時,用戶可以在 Track() 的第三個參數傳入一個 map[string]interface{} 物件,為事件增加自定義事件屬性。以電商產品為例,可以這樣追蹤一次購物行為:

  • 介面
// distinctId 是用戶標識,event 是事件名,properties 是自定義事件屬性,isLoginId 表示是否登入
func (sa *SensorsAnalytics) Track(distinctId, event string, properties map[string]interface{}, isLoginId bool) error
CODE
  • 例子
distinct_id := "ABCDEF123456"

properties := map[string]interface{}{
    // "$time" 屬性是系統預設屬性,取值為 int64 型別,表示事件發生的時間,如果設定該屬性,則替換 SDK 內部取得的系統當前時間。如果不填入該屬性,則預設使用系統當前時間
    "$time" : time.Now(),
    // "$ip" 屬性是系統預設屬性,如果伺服器端中能取得用戶 IP 網址,並填入該屬性,神策分析會自動根據 IP 網址解析用戶的省份、城市資訊
    "$ip" : "123.123.123.123",
    // 商品 ID
    "ProductId" : "123456",
    // 商品類別
    "ProductCatalog" : "Laptop Computer",
    // 是否加入收藏夾,Boolean 型別的屬性
    "IsAddedToFav" : True,
}

// 記錄用戶瀏覽商品事件
err := sa.Track(distinct_id, "ViewProduct", properties, true)

properties:= map[string]interface{}{
    // 用戶 IP 網址
    "$ip" : "123.123.123.123",
    // 商品 ID 列表,[]string 型別的屬性
    "ProductIdList" : []string{"123456", "234567", "345678"},
    // 訂單價格
    "OrderPaid" : 12.10,
}

// 記錄用戶訂單付款事件
err = sa.Track(distinct_id, "PaidOrder", properties, true)

CODE

3.1 事件屬性

如前文中的範例,追蹤的事件可以設定自定義的事件屬性,例如瀏覽商品事件中,將商品 ID、商品分類等資訊作為事件屬性。在後續的分析工作中,事件屬性可以作為統計過濾條件使用,也可以作為維度進行多維分析。對於事件屬性,神策分析有一些限制:

  • 事件屬性是一個 map[string]interface{} 物件
  • map 中每個元素描述一個屬性,Key 為屬性名稱,必需是 string 型別
  • map 中,每個元素的 Value 是屬性的值,支援 stringintfloat64[]stringtime.Time

對於神策分析中事件屬性的更多限制,請參考 數據格式

3.1.1 系统預設屬性

如前文中範例,事件屬性中以 "$" 開頭的屬性為系統預設屬性,在自定義事件屬性中填入對應 "$" 開頭的屬性值可以覆蓋這些預設屬性:

  • $ip - 填入該屬性,神策分析會自動根據 IP 網址解析用戶的省份、城市資訊,該屬性值為 string 型別;
  • $time - 填入該屬性,神策分析將事件時間設定為屬性值的時間,該屬性值必須為 time.Time 。請注意,神策分析預設會過濾忽略 365 天前或 3 天後的數據,如需修改請聯繫我們。

關於其他更多預設屬性,請參考 數據格式 中 "預設屬性" 一節。

4. 用戶識別

在伺服器端應用中,神策分析也要求為每個事件設定用戶的 Distinct ID,這有助於神策分析提供更準確的留存率等數據。

對於註冊用戶,推薦使用系統中的用戶 ID 作為 Distinct ID,不建議使用用戶名、Email、手機號碼等可以被修改的資訊。

所有的 Track 和 Profile 系列方法建議明確指定 isLoginId 參數,以便明確告知神策分析用戶 ID 的類型。

4.1 用戶註冊/登入

當同一個用戶的 Distinct ID 發生變化時(一般情況為匿名用戶註冊行為),可以透過 TrackSignup() 將舊的 Distinct ID 和新的 Distinct ID 關聯,以確保用戶分析的準確性。

  • 介面
// distinctId 為新的 ID(註冊後的 ID),originId 為舊 ID(匿名 ID)
func (sa *SensorsAnalytics) TrackSignup(distinctId, originId string) error
CODE
  • 例子
// 匿名 ID 由前端傳過來
anonymous_id := "9771C579-71F0-4650-8EE8-8999FA717761"

register_id := "0012345678"
// 用戶註冊/登入時,將用戶註冊 ID 與 匿名 ID 關聯
err := sa.TrackSignup(register_id, anonymous_id)
CODE


注意,對同一個用戶,TrackSignup() 一般情況下建議只呼叫一次(通常在用戶 註冊 時呼叫),用戶登入前後的行為的關聯建議在業務端實作。在神策分析 1.13 版本之前,多次呼叫 TrackSignup() 時,只有第一次關聯行為是有效的。神策分析 1.13 版本之後提供了多裝置 ID 關聯的方法。更詳細的說明请参考 標識用戶,並在必要時聯繫我們的技術支援人員。


5. 設定用戶屬性

為了更準確地提供針對人群的分析服務,神策分析 SDK 可以設定用戶屬性,如年齡、性別等。用戶可以在留存分析、分佈分析等功能中,使用用戶屬性作為過濾條件或以用戶屬性作為維度進行多維分析。

使用 ProfileSet() 設定用戶屬性:

  • 介面
// distinctId 為用戶標識,properties 為要設定的用戶屬性,isLoginId 標識 distinctId 是否為登入後的 ID
func (sa *SensorsAnalytics) ProfileSet(distinctId string, properties map[string]interface{}, isLoginId bool) error
CODE
  • 例子
distinct_id := "ABCDEF123456789"

properties := map[string]interface{}{
    // 用戶性別屬性(Sex)為男性
    "Sex" : "Male",
    // 用戶等級屬性(Level)為 VIP
    "UserLevel" : "Elite VIP",
}

// 設定用戶屬性
err := sa.ProfileSet(distinct_id, properties, true)
CODE

對於不再需要的用戶屬性,可以透過 ProfileUnset() 介面將屬性刪除。

用戶屬性中,屬性名稱與屬性值的限制條件與事件屬性相同,詳細說明請參考 數據格式

5.1 記錄初次設定的屬性

對於只在首次設定時有效的屬性,我們可以使用 ProfileSetOnce() 記錄這些屬性。與 ProfileSet() 介面不同的是,如果被設定的用戶屬性已存在,則這條記錄會被忽略而不會覆蓋已有數據,如果屬性不存在則會自動建立。因此,ProfileSetOnce() 比較適用於為用戶設定首次啟用時間、首次註冊時間等屬性。

  • 介面
// distinctId 為用戶標識,properties 為要設定的用戶屬性,isLoginId 標識 distinctId 是否為登入後的 ID
func (sa *SensorsAnalytics) ProfileSetOnce(distinctId string, properties map[string]interface{}, isLoginId bool) error
CODE
  • 例子
distinct_id := "ABCDEF123456789"

// 設定用戶管道屬性(AdSource)為 "App Store"
properties := map[string]interface{}{
    "AdSource" : "App Store",
}
err := sa.ProfileSetOnce(distinct_id, properties, true)

properties = map[string]interface{}{
    "AdSource" : "Search Engine",
}
// 再次設定用戶管道屬性(AdSource),設定無效,屬性 "AdSource" 的值仍為 "App Store"
err = sa.ProfileSetOnce(distinct_id, properties, true)
CODE

5.2 數值型別的屬性

對於數值型的用戶屬性,可以使用 ProfileIncrement() 對屬性值進行累加。常用於記錄用戶付費次數、付費額度、積分等屬性。

  • 介面
// distinctId 為用戶標識,properties 為要設定的用戶屬性,這個屬性中應該只包含 value 為 int 的屬性,isLoginId 標識 distinctId 是否為登入後的 ID
func (sa *SensorsAnalytics) ProfileIncrement(distinctId string, properties map[string]interface{}, isLoginId bool) error
CODE
  • 例子
distinct_id := "ABCDEF123456789"

properties := map[string]interface{}{
    "GamePlayed" : 1,
}
// 設定用戶遊戲次數屬性(GamePlayed),將次數累加1次
err := sa.ProfileIncrement(distinct_id, properties, true)
CODE

5.3 列表型別的屬性

對於用戶喜愛的電影、用戶評論過的餐廳等屬性,可以記錄列表型屬性。需要注意的是,列表型屬性中的元素必須為 string 型別,且元素的值會自動去重。關於列表類型限制請見  數據格式7.3 屬性長度限制。

  • 介面
// distinctId 為用戶標識,properties 為要設定的用戶屬性,這個屬性中應該只包含 value 為 [string] 的屬性,isLoginId 標識 distinctId 是否為登入後的 ID
func (sa *SensorsAnalytics) ProfileAppend(distinctId string, properties map[string]interface{}, isLoginId bool) error
CODE
  • 例子
distinct_id := "ABCDEF123456789"

properties := map[string]interface{}{
    // 電影列表
    "Movies" : []string{"Sicario", "Love Letter"},
    // 遊戲列表
    "Games" : []string{"Call of Duty", "Halo"},
}

// 傳入properties,設定用戶喜歡的電影屬性(movies)和喜歡的遊戲屬性(games)
// 設定成功後,"Movies" 屬性值為 ["Sicario", "Love Letter"];"Games" 屬性值為 ["Call of Duty", "Halo"]
err := sa.ProfileAppend(distinct_id, properties, true)

// 傳入屬性名稱和需要插入屬性的值,設定用戶喜歡的電影屬性(Movies)
// 設定成功後 "Movies" 屬性值為 ["Sicario", "Love Letter", "Dead Poets Society"]
properties = map[string]interface{}{
    "Movies" : []string{"Dead Poets Society"},
}
err = sa.ProfileAppend(distinct_id, properties, true)

// 傳入屬性名稱和需要插入屬性的值,設定用戶喜歡的電影屬性(Movies),
// 但屬性值 "Love Letter" 與已列表中已有元素重複,操作無效,
// "Movies" 屬性值仍為 ["Sicario", "Love Letter", "Dead Poets Society"]
properties = map[string]interface{}{
    "Movies" : []string{"Love Letter"},
}
err = sa.ProfileAppend(distinct_id, properties, true)
CODE

6. 物品元數據上報

在神策推薦專案中,客戶需要將物品元數據上報,以開展後續推薦業務的開發與維護。神策分析 SDK 提供了設定與刪除物品元數據的方法。

item_id(物品 ID )與 item_type (物品所屬型別)共同组成了一個物品的唯一標識。所有的 item 系列方法都必需同時指定物品 ID 及物品所屬型別這兩個參數,來完成對物品的操作。

6.1 設定物品

直接設定一个物品,如果已存在則覆蓋。除物品 ID 與 物品所屬型別外,其他物品屬性需在 $properties 中定義。

物品屬性中,屬性名稱與屬性值的限制條件與事件屬性相同,詳細說明请参考 數據格式

// 範例
itemType := "apple"
itemId := "12345"
err = sa.ItemSet(itemType, itemId, map[string]interface{}{"price": "3",})
CODE

6.2 刪除一個物品

如果物品不被推薦而需要下線,刪除該物品即可,如不存在則忽略。

除物品 ID 與 物品所屬型別外,不解析其他物品屬性。

// 範例
itemType := "apple"
itemId := "12345"
err = sa.ItemDelete(itemType, itemId)
CODE

7. 設定神策分析 SDK

Golang SDK 主要由以下兩個元件構成:

  • SensorsAnalytics:用於傳送數據的介面物件,建構函式需要傳入一個 Consumer 執行個體
  • Consumer:Consumer 會進行實際的數據傳送

為了讓開發者更靈活的接入數據,神策分析 SDK 實作了以下 Consumer:

  • LoggingConsumer
  • ConcurrentLoggingConsumer
  • DebugConsumer
  • DefaultConsumer
  • BatchConsumer

7.1 LoggingConsumer(推薦)

用於將數據輸出到指定目錄並按天切割檔案,支援透過參數指定是否按小時切割,一般用來處理即時數據,產生 log 檔並使用 LogAgent 等工具匯入。

  • 初始化介面
// filename 為輸出檔案字首,hourRotate 為是否按小時切割
func InitLoggingConsumer(filename string, hourRotate bool) (*consumers.LoggingConsumer, error)
CODE
  • 例子
import sdk "github.com/sensorsdata/sa-sdk-go"

// 初始化 LoggingConsumer
consumer, err := sdk.InitLoggingConsumer("/data/sa/access.log", false)
// ...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
defer sa.Close()

// ...
CODE

LoggingConsumer 的第一個參數是保存檔案字首,第二個參數表示是否啟用按小時切割,預設是每天 0 點切割保留所有文件。

  • 按小時切割關閉的情況下,文件將保存在以 prefix.YYYY-MM-DD(例如:假設 prefix 為 /data/sa/access.log,當天是2018-04-13,則輸出文件為 /data/ sa/access.log.2018-03-13)
  • 按小時切割開啟的情況下,在小時整點切割,檔案將保存在以 prefix.YYYY-MM-DD.H(例如:假設 prefix 為 /data/sa/access.log,當天是 2018-04-13 14 點,則輸出檔案為 /data/sa/access.log.2018-03-13.14)
// 按小時切分
consumer, err := sdk.InitLoggingConsumer("/data/sa/access.log", true)
CODE

注意,請不要使用多程序寫入同一個 log 檔,可能會造成數據遺失或者錯亂。如果需要多程序寫入,請使用 ConcurrentLoggingConsumer

7.2 ConcurrentLoggingConsumer(推薦)

用於將數據輸出到指定目錄,並自動按 天 切割檔案,支援按小時切割,參數含義同 LoggingConsumer ,與 LoggongConsumer 不同的是,它支援多程序寫入同一個檔案。

  • 初始化介面
// filename 為輸出文件字首,hourRotate 為是否按小時切割
func InitConcurrentLoggingConsumer(filename string, hourRotate bool) (*consumers.ConcurrentLoggingConsumer, error)
CODE
  • 例子
import sdk "github.com/sensorsdata/sa-sdk-go"

// 初始化 ConcurrentLoggingConsumer
consumer, err := sdk.InitConcurrentLoggingConsumer("/data/sa/access.log", true)
// ...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)

// 程式結束前呼叫 Close() ,让 Consumer 重新整理所有快取數據到文件中
defer sa.Close()

// ...
CODE

注意: LogAgent 設定檔案中一定要註解掉 real_time_file_name 參數,否則無法正常匯入數據。已使用 LoggingConsumer 的客戶建議按照如下步驟切換到 ConcurrentLoggingConsumer:

  • 第 1 步 停掉 LogAgent,並註解掉 LogAgent 設定中的 real_time_file_name 參數。
  • 第 2 步 將 log 目錄下的 real_time_file_name 的檔案加上當前時間的字尾 ".YYYY-MM-DD"。
  • 第 3 步 後端程式升級切換到 ConcurrentLoggingConsumer。
  • 第 4 步 重新啟動 LogAgent。

7.3 DebugConsumer(測試使用)

用於校驗數據匯入是否正確,關於 Debug 模式 的詳細資訊,請進入相關頁面查看。請注意:Debug 模式是為方便開發者除錯而設定的模式,該模式會逐條校驗數據並在校驗失敗時拋出異常,效能遠低於正常模式。線上環境使用 Debug 模式會嚴重影響效能並存在崩潰風險,產品上線前請務必替換掉/關閉 Debug 模式

  • 初始化介面
// url是接收端url,writeData表示是否校驗數據,timeout是傳送超時時間,單位是毫秒
// writeData為
//   true - 校驗數據,並將數據匯入到神策分析中
//   false - 校驗數據,但不進行數據匯入
func InitDebugConsumer(url string, writeData bool, timeout int) (*consumers.DebugConsumer, error)
CODE
  • 例子
import sdk "github.com/sensorsdata/sa-sdk-go"

// 神策分析數據接收的 URL
SA_SERVER_URL := "YOUR_SERVER_URL"
// 傳送數據的超時時間,單位毫秒
SA_REQUEST_TIMEOUT := 100000
// Debug 模式下,是否將數據匯入神策分析
//   true - 校驗數據,並將數據匯入到神策分析中
//   false - 校驗數據,但不進行數據匯入
SA_DEBUG_WRITE_DATA := true

// 初始化 Debug Consumer
consumer, err := sdk.InitDebugConsumer(SA_SERVER_URL, SA_DEBUG_WRITE_DATA, SA_REQUEST_TIMEOUT)
// ...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
defer sa.Close()

// ...
CODE

7.4 DefaultConsumer

通常用於匯入小規模歷史數據的場景。由於是網路直接傳送數據,如果網路出現異常可能會導致數據重發或丟失,因此不要用在任何線上服務中。普通 Consumer,實作,逐條、同步的傳送數據給接收服務器。

  • 初始化介面
// url 是接收端 URL,timeout 是傳送超時時間,單位是毫秒
func InitDefaultConsumer(url string, timeout int) (*consumers.DefaultConsumer, error)
CODE
  • 例子
import sdk "github.com/sensorsdata/sa-sdk-go"

// 神策分析數據接收的 URL
SA_SERVER_URL := "YOUR_SERVER_URL"
// 傳送數據的超時時間,單位毫秒
SA_REQUEST_TIMEOUT := 100000

// 初始化 Default Consumer
consumer, err := sdk.InitDefaultConsumer(SA_SERVER_URL, SA_REQUEST_TIMEOUT)
// ...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
defer sa.Close()

// ...
CODE

7.5 BatchConsumer

通常用於匯入小規模歷史數據,或者離線 / 略過匯入數據的場景。由於是網路直接傳送數據,如果網路出現異常可能會導致數據重發或丟失,因此不要用在任何線上服務中。批量傳送數據的 Consumer,當且僅當數據達到指定的量時,才將數據進行傳送。

  • 初始化介面
// url是接收端url,max是最大快取條數,timeout是傳送超時時間,單位是毫秒
func InitBatchConsumer(url string, max, timeout int) (*consumers.BatchConsumer, error)
CODE
  • 例子
import sdk "github.com/sensorsdata/sa-sdk-go"

// 神策分析數據接收的 URL
SA_SERVER_URL := "YOUR_SERVER_URL"
// 傳送數據的超時時間,單位毫秒
SA_REQUEST_TIMEOUT := 100000
// 當快取的數據量達到參數值時,批量傳送數據
SA_BULK_SIZE := 100

// 初始化 Batch Consumer
consumer, err := sdk.InitBatchConsumer(SA_SERVER_URL, SA_BULK_SIZE, SA_REQUEST_TIMEOUT)
// ...
// 使用 Consumer 來建構 SensorsAnalytics 物件
sa := sdk.InitSensorsAnalytics(consumer, "default", false)
defer sa.Close()

// ...
CODE