記錄用戶行為的意義?
很多互聯(lián)網(wǎng)產(chǎn)品都會有數(shù)據(jù)分析的后臺,比如,本公眾號的一些數(shù)據(jù)分析:
通過后臺的一些數(shù)據(jù)分析,我可以知道本公眾號讀者的一些年齡分布、地域分布、對哪些文章比較感興趣等信息。
這些數(shù)據(jù)一定程度上對我之后生產(chǎn)內(nèi)容有一定的啟發(fā)。這些數(shù)據(jù)就是微信公眾號把我們的一些用戶信息、閱讀公眾號的一些行為給記錄下來,并形成圖表等形式展現(xiàn)出來。
特別是To C的消費類電子產(chǎn)品,用戶數(shù)量較大,用戶對設備的使用習慣對產(chǎn)品經(jīng)理們之后的決策、工程師之后的優(yōu)化方向很有幫助。
線上的嵌入式設備能記錄用戶的行為,能夠幫助我們深入了解用戶的行為模式,進而實現(xiàn)個性化推薦、故障預測、用戶體驗優(yōu)化等目標。
比如:
??通過分析大量的用戶使用功能A的頻次最多,那么功能A的bug能修復就盡量修復好,哪怕是一些比較偏門的路徑,因為這個功能好用與否可能關乎到用戶對于這個產(chǎn)品地評價。
??通過監(jiān)控用戶在執(zhí)行哪些操作時,觸發(fā)了一些異常,這對于之后地優(yōu)化起到了指導的方向。
??用戶可能在夜間的時候沒有使用設備的習慣,那對于夜間的一些有聲音的預約操作是不是可以通過各種策略提前預防到這種情況,防止打擾用戶休息。
具體到各個行業(yè):
??智能家居行業(yè)。通過智能門鎖、智能照明等設備的埋點數(shù)據(jù),分析用戶的日常行為習慣,以優(yōu)化家居環(huán)境的智能化管理。
??工業(yè)自動化。通過埋點數(shù)據(jù)收集生產(chǎn)過程中的關鍵參數(shù),進行質量控制和數(shù)據(jù)分析,確保產(chǎn)品質量的穩(wěn)定性。
??醫(yī)療健康。通過分析用戶的日常健康數(shù)據(jù)(如步數(shù)、心率、睡眠質量等),提供個性化的健康管理建議。
??智能交通。在智能交通信號燈中嵌入埋點,根據(jù)實時交通流量調整信號燈配時,提高道路通行效率。
??物聯(lián)網(wǎng)。通過對物聯(lián)網(wǎng)設備收集的海量數(shù)據(jù)進行分析,預測設備的運行趨勢和潛在故障,提前采取措施進行預防和維護。
記錄用戶的行為,有個專業(yè)一點的詞,叫做埋點。
嵌入式埋點就是在嵌入式設備中預設一些數(shù)據(jù)采集點(即“埋點”),當特定事件發(fā)生時(如用戶點擊某個按鈕、觀看某個節(jié)目),這些埋點會自動記錄并上傳相關數(shù)據(jù)到服務器進行分析。
如何進行數(shù)據(jù)埋點?
整個數(shù)據(jù)分析的步驟大致如下:
??事件定義與管理:首先,在嵌入式設備中定義和管理數(shù)據(jù)采集點,即“埋點”。這些埋點可以配置為在用戶點擊、交互等事件發(fā)生時觸發(fā)數(shù)據(jù)采集。
??數(shù)據(jù)采集與傳輸:當事件發(fā)生時,嵌入式設備將相關數(shù)據(jù)存儲起來并通過網(wǎng)絡傳輸?shù)綌?shù)據(jù)采集服務器。這里,可以使用HTTP請求、WebSocket、MQTT等協(xié)議實現(xiàn)數(shù)據(jù)的實時傳輸。
??數(shù)據(jù)處理與分析:在服務器端,使用大數(shù)據(jù)處理工具對收集到的數(shù)據(jù)進行實時處理和分析。通過分析用戶的點擊行為、觀看習慣等,可以建立用戶行為模型,實現(xiàn)個性化推薦和安全監(jiān)控等應用。
這里我們著重分享事件定義與管理的例子:
我們基于Linux C,使用POSIX線程(pthread)來創(chuàng)建單獨的線程,并使用POSIX消息隊列來接收來自其他線程的開機次數(shù)及按鍵埋點事件。同時,我們將使用cJSON
庫來處理JSON數(shù)據(jù),以及標準文件操作來記錄數(shù)據(jù)到tracking.log文件中。
本例子源碼可以在本公眾號回復關鍵詞:埋點例子,進行獲取。
本例子源碼可以在本公眾號回復關鍵詞:埋點例子,進行獲取。
本例子源碼可以在本公眾號回復關鍵詞:埋點例子,進行獲取。
1、相關頭文件
#include?<stdio.h>??
#include?<stdlib.h>??
#include?<string.h>??
#include?<unistd.h>
#include?<pthread.h>??
#include?<mqueue.h>??
#include?<sys/stat.h>??
#include?<fcntl.h>??
#include?<time.h>??
#include?"cJSON.h"
2、埋點事件數(shù)據(jù)結構
????//?埋點類型
enum?track_event_type
{
????TRACK_EVENT_TYPE_BOOT,
????TRACK_EVENT_TYPE_BUTTON,
????TRACK_EVENT_TYPE_MAX,
};
//?公共埋點信息
struct?track_event_common_info
{
? ? char?dev_name[32];//?設備名稱
? ? char?serial_num[32];//?設備序列號
? ? char?timestamp[64];//?時間戳
};
//?啟動事件信息
struct?track_event_info_boot
{
? ? unsignedint?cnt;//?開機次數(shù)
};
//?按鍵事件信息
struct?track_event_info_button
{
? ? unsignedchar?button_num;//?按鍵號
? ? unsignedchar?button_type;//?按鍵類型,長按?or?短按
};
//?當前的埋點事件信息
union?track_event_info
{
????struct?track_event_info_boot?track_boot;
????struct?track_event_info_button?track_button;
};
//?埋點事件體
struct?tracking_event
{
????enum?track_event_type?event_type;
????union?track_event_info?event_info;
????struct?track_event_common_info?*event_common_info;
};??
3、消息隊列初始化、清除接口
#define?QUEUE_NAME??"/mq0"?
mqd_t?g_mqd;
intinit_mq(void)
{
????struct?mq_attr?attr;
????attr.mq_flags?=?0;
????attr.mq_maxmsg?=?10;//?最大消息數(shù)??
????attr.mq_msgsize?=?sizeof(struct?tracking_event);//?消息最大大小?
????attr.mq_curmsgs?=?0;//?當前隊列中的消息數(shù)(由系統(tǒng)維護)??
????g_mqd?=?mq_open(QUEUE_NAME,?O_CREAT?|?O_RDWR,0777,&attr);
????if(g_mqd?== (mqd_t)-1)
????{
????????perror("mq_open");
? ? ? ? exit(EXIT_FAILURE);
????}
????return?0;
}
voidcleanup_mq(void)
{
????mq_close(g_mqd);
????mq_unlink(QUEUE_NAME);
}
4、通過消息隊列發(fā)送埋點事件
//?微信公眾號:嵌入式大雜燴
struct?track_event_common_info?*get_track_event_common_info(void)
{
????staticstruct?track_event_common_info?common_info?={0};
????time_t?rawtime;
????struct?tm?*?timeinfo;
????strncpy(common_info.dev_name,"board?xxx",sizeof(common_info.dev_name)-1);
????strncpy(common_info.serial_num,"1234ABCD567",sizeof(common_info.serial_num)-1);
????time(&rawtime);
????timeinfo?=?localtime(&rawtime);
????strftime(common_info.timestamp,sizeof(common_info.timestamp),"%Y-%m-%d?%H:%M:%S",?timeinfo);
????return?&common_info;
}
voidsend_event(enum?track_event_type?event_type,?union?track_event_info?event_info)
{
????struct?track_event_common_info?*common_info?=?get_track_event_common_info();
????struct?tracking_event?event?={0};
????event.event_type?=?event_type;
????event.event_info?=?event_info;
????event.event_common_info?=?common_info;
????if(mq_send(g_mqd,(constchar*)&event,sizeof(event),1)==-1)
????{
????????perror("mq_send");
????}
}
5、埋點線程實現(xiàn)
//?微信公眾號:嵌入式大雜燴
void*tracking_thread(void?*arg)
{
????#define?STR(x)??#x?
????FILE*fp?=?fopen("tracking.log","a");
????if(!fp)
????{
????????perror("fopen");
????????return?NULL;
????}
????struct?tracking_event?event?={0};
????while(1)
????{
????????if(mq_receive(g_mqd,(char*)&event,sizeof(event),NULL)==-1)
????????{
? ? ? ? ????perror("mq_receive");
????????????continue;
????????}
????????cJSON?*root?=?cJSON_CreateObject();
????????printf("event.event_type?=?%dn",?event.event_type);
????????switch(event.event_type)
????????{
????????????case?TRACK_EVENT_TYPE_BOOT:
????????????{
????????????????cJSON_AddStringToObject(root,"even_type",?STR(TRACK_EVENT_TYPE_BOOT));
????????????????cJSON_AddNumberToObject(root,"boot_cnt",?event.event_info.track_boot.cnt);
????????????????break;
????????????}
????????????case?TRACK_EVENT_TYPE_BUTTON:
????????????{
????????????????cJSON_AddStringToObject(root,"even_type",?STR(TRACK_EVENT_TYPE_BUTTON));
????????????????cJSON_AddNumberToObject(root,"button_num",?event.event_info.track_button.button_num);
????????????????cJSON_AddNumberToObject(root,"button_type",?event.event_info.track_button.button_type);
????????????????break;
????????????}
????????????default:
????????????break;
????????}
????????cJSON_AddStringToObject(root,"dev_name",?event.event_common_info->dev_name);
????????cJSON_AddStringToObject(root,"serial_num",?event.event_common_info->serial_num);
????????cJSON_AddStringToObject(root,"timestamp",?event.event_common_info->timestamp);
????????char*json_str?=?cJSON_Print(root);
????????fprintf(fp,"%sn",?json_str);
????????printf("json_str?=?%sn",?json_str);
????????free(json_str);
????????cJSON_Delete(root);
????????fflush(fp);
????????usleep(100*1000);
????}
????fclose(fp);
????pthread_exit(NULL);
}
6、主函數(shù)實現(xiàn)
//?微信公眾號:嵌入式大雜燴
intmain(void)
{
????pthread_t?thread_id;
????init_mq();
????//?創(chuàng)建跟蹤線程??
????if(pthread_create(&thread_id,NULL,?tracking_thread,NULL)!=0)
????{
????????perror("pthread_create");
????????return?1;
????}
????while(1)
????{
????????int?ch?=?getchar();
????????switch(ch)
????????{
????????????case'1':
????????????{
????????????????//?模擬發(fā)送TRACK_EVENT_TYPE_BOOT事件??
????????????????printf("TRACK_EVENT_TYPE_BOOTn");
????????????????enum?track_event_type?event_type?={0};
????????????????union?track_event_info?event_info?={0};
????????????????event_type?=?TRACK_EVENT_TYPE_BOOT;
????????????????event_info.track_boot.cnt?+=1;
????????????????send_event(event_type,?event_info);
????????????????break;
????????????}
????????????case'2':
????????????{
????????????????//?模擬發(fā)送button_1_pressed事件??
????????????????printf("TRACK_EVENT_TYPE_BUTTONn");
????????????????enum?track_event_type?event_type?={0};
????????????????union?track_event_info?event_info?={0};
????????????????event_type?=?TRACK_EVENT_TYPE_BUTTON;
????????????????event_info.track_button.button_num?=1;
????????????????event_info.track_button.button_type?=1;
????????????????send_event(event_type,?event_info);
????????????????break;
????????????}
????????????default:
????????????break;
????????}
????????usleep(100);
????}
????//?清理??
????pthread_join(thread_id,NULL);
????cleanup_mq();
????return0;
}
編譯運行:
gcc?cJSON.c?test.c?-o?test?-lpthread?-lrt
埋點文件把相關埋點事件給記錄了下來,實際埋點信息可以根據(jù)需要進行增刪。
埋點注意事項
??保護用戶隱私:確保收集的數(shù)據(jù)符合隱私政策,避免泄露敏感信息。
??合理控制埋點數(shù)量:過多埋點會影響系統(tǒng)性能和用戶體驗。
??數(shù)據(jù)準確性:確保收集到的數(shù)據(jù)準確無誤,避免誤導決策。
??是否有必要進行埋點?埋點的缺點:
??開發(fā)成本高:嵌入式埋點需要在每個監(jiān)測點都編寫單獨的事件監(jiān)測代碼,增加了開發(fā)工作量。
??存在延遲上報和漏報情況:由于網(wǎng)絡延遲、設備性能等因素,可能存在數(shù)據(jù)上報的延遲或漏報情況。這會影響數(shù)據(jù)的完整性和準確性。進而會影響數(shù)據(jù)分析。
??無歷史記錄:嵌入式埋點只能采集到植入代碼之后的數(shù)據(jù),無法回溯到之前的歷史數(shù)據(jù)。這可能導致在分析用戶行為或系統(tǒng)變化時缺乏全面的數(shù)據(jù)支持。
??影響系統(tǒng)性能:埋點或多或少會帶來一些性能開銷。
埋點作為一種數(shù)據(jù)收集和分析的技術手段,具有其獨特的優(yōu)點和缺點。在實際應用中,需要根據(jù)具體需求和場景進行權衡和選擇。