1. 前言
安裝使用C++ SDK可以幫助開發者快速接入使用天翼云的日志服務相關功能。
2. 使用條件
2.1. 先決條件
用戶需要具備以下條件才能夠使用LTS SDK C++版本:
1、購買并訂閱了天翼云的云日志服務,并創建了日志項目和日志單元,獲取到相應編碼(logProject、logUnit)。
2、已獲取AccessKey 和 SecretKey。
3、已安裝c++ 運行環境,推薦安裝c++11或以上版本。
2.2. 下載及安裝
從官方渠道下載ctyun_lts_cpp_sdk.zip壓縮包,放到相應位置后并解壓。“ctyun_lts_cpp_sdk”目錄中sample_putlogs.cpp為SDK的使用示例代碼。
2.1.1. 依賴
cmake2.8或更新版本,GCC 4.9或更新版本。以下庫及其相關頭文件,可在對應Linux發行版的包管理器中安裝,括號中給出的是經過驗證的版本。
libcurl-devel?? (7.6.1)
openssl-devel ? (1.1.1k)
protobuf? ? ? ? (3.5.0)
lz4? ? ? ? ? ? (v1.8.3)
boost (1.66.0)
對于 CentOS 安裝,可用如下命令搭建依賴環境
yum install gcc gcc-c++ ? ?
yum install cmake
yum install libcurl-devel openssl-devel libuuid-devel
yum install lz4-devel.x86_64
yum install protobuf-devel.x86_64
yum install boost-devel.x86_64
2.1.2. 編譯使用
1、進入ctyun_lts_cpp_sdk目錄下,里面有CMakelists.txt文件。
2、執行以下命令:
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=..
make
make install
其中make 主要做的工作有:為.proto文件生成對應的.pb.cc、.pb.h,以及編譯出靜態庫文件libltssdk.a。
3、將ctyun_lts_cpp_sdk的include目錄下的頭文件,全部移動到您項目的include目錄下,(如果直接使用SDK 則可以不用移動)。
4、之后就可以在您的項目內使用SDK了,只需要引入對應的頭文件即可。
5、編譯您的程序,在您目錄下內編譯您的程序,構建出可執行文件。 如sample_putlogs.cpp:
g++ sample_putlogs.cpp -I./include -L./lib/ -lcurl -lcrypto -llz4 -lprotobuf -lltssdk
或者 根據主目錄中的Makefile 文件。執行以下命令,也能實現上面的構建出可執行文件。
make sample_putlogs
6、執行可執行文件。
./sample_putlogs
3. SDK使用設置
3.1. 基本使用
使用 SDK訪問 LTS 的服務,需要設置正確的 AccessKey、SecretKey 和服務端 Endpoint,所有的服務可以使用同一 key 憑證來進行訪問,但不同的服務需要使用不同的 endpoint 進行訪問,詳情參考天翼云官網-SDK接入概述。在調用前SDK,需要已知以下參數:
1、云日志服務訪問地址。詳情請查看訪問地址(Endpoint)。
2、key憑證:accessKey和secretKey 。詳情請查看如何獲取訪問密鑰(AK/SK)。
3、日志項目編碼:logProject,在使用SDK前,需要確保您有至少一個已經存在的日志項目,日志項目就是您要將日志上傳到的地方。
4、日志單元編碼:logUnit,在使用SDK前,需要確保日志項目中有至少一個已經存在的日志單元。
| 參數 | 參數類型 | 描述 | 是否必須 |
|---|---|---|---|
| endpoint | string | 域名 | 是 |
| accessKey | string | AccessKey,簡稱ak | 是 |
| secretKey | string | SecretKey ,簡稱sk | 是 |
| logProject | string | 日志項目編碼 | 是 |
| logUnit | string | 日志單元編碼 | 是 |
目前通過SDK將日志上傳到云日志服務有兩種上傳形式:同步上傳和異步批量上傳。
同步上傳:當調用日志上傳接口時,sdk會立即進行http請求調用,并返回發送結果。這種方式結構簡單,可用于發送頻率不高的場景。
異步批量上傳:當調用日志上傳接口時,后臺線程會將日志進行累積,當達到發送條件時,會進行一次合并發送。對于需要頻繁調用發送接口的場景,這種方式性能更卓越,更高效。
示例代碼:同步上傳
int main(int argc, char **argv) {
string ak = "your accessKey";
string sk = "your secretKey";
string logProject = "log project Code";
string logUnit = "log unit Code";
string endpoint = "endpoint";
vector<LogItem> logItems;
? ? ?int64_t logTimestamp = GetCurrentTimeStamp(3);
? ? ?string oriMessage = "c++ sdk test oriMessage";
? ? ?map<std::string, boost::any> contents;
? ? ?map<std::string, boost::any> labels;
? ? ?contents["level"] = string("error");
? ? ?contents["unit_id"] = 3.1415926;
? ? ?labels["user_tag"] = string("string");
? ? ?LogItem logItem;
? ? ?logItem.logTimestamp = logTimestamp;
? ? ?logItem.oriMessage = oriMessage;
? ? ?logItem.contents = contents;
? ? ?logItem.labels = labels;
? ? ?logItems.push_back(logItem);
try {
Client *client = new Client(endpoint, ak, sk,logProject);
for (int i = 0; i < 100; i++) { //發送了100次
LogResponse logResponse = client->PutLogs(logProject, logUnit, logItems);
cout << logResponse.statusCode << ":" << logResponse.message << ":" << logResponse.error << endl;
}
} catch (LOGException &e) {
cout << e.GetExceptionErrorCode() << ":" << e.GetExceptionMessage() << endl;
}
}
示例代碼:異步批量上傳
int main(int argc, char **argv) {
string ak = "your accessKey";
string sk = "your secretKey";
string logProject = "log project Code";
string logUnit = "log unit Code";
string endpoint = "endpoint";
vector<LogItem> logItems;
? ? ?int64_t logTimestamp = GetCurrentTimeStamp(3);
? ? ?string oriMessage = "c++ sdk test oriMessage";
? ? ?map<std::string, boost::any> contents;
? ? ?map<std::string, boost::any> labels;
? ? ?contents["level"] = string("error");
? ? ?contents["unit_id"] = 3.1415926;
? ? ?labels["user_tag"] = string("string");
? ? ?LogItem logItem;
? ? ?logItem.logTimestamp = logTimestamp;
? ? ?logItem.oriMessage = oriMessage;
? ? ?logItem.contents = contents;
? ? ?logItem.labels = labels;
? ? ?logItems.push_back(logItem);
try{
? ? ? ? ProducerConfig producerConfig = ProducerConfig(); ?//使用默認producer配置
? ? ? ? ClientConfig clientConfig(endpoint, ak, sk, logProject);
? ? ? ? Client *client = ?createClient(clientConfig);
? ? ? ? map<string, Client *> clientPool; ? ?//可使用不同client配置,生成多個client
? ? ? ? clientPool.insert(make_pair(client->GetLogProject(), client));
? ? ? ? IoWorker ioWorker(clientPool, producerConfig.getMaxWorkerCount()); ?
? ? ? ? LogAccumulator logAccumulator(producerConfig, ioWorker); ? ? ? ? ? ?
? ? ? ? Mover mover(logAccumulator, ioWorker, producerConfig.getLingerMs()); ?
? ? ? ? Producer producer(producerConfig, ioWorker, logAccumulator, mover); ?
? ? ? ? producer.start(); ?
? ? ? ? for (int i=0;i<1000;i++){
? ? ? ? ? ? producer.sendLogsCallback(logProject,logUnit,logItems);
? ? ? ? }
? ? ? ? std::this_thread::sleep_for(std::chrono::milliseconds(3000));
? ? ? ? producer.safeClose();
}catch (exception &e){
? ? ? ? cout<<e.what()<<endl;
? ?}}
4. 服務代碼示例-同步上傳
4.1. 關于Client的操作
4.1.1. New Client()
此操作是初始化Client。用戶需要配置至少3個關鍵的參數才夠初始化Client。初始化Client之后,其包含的配置信息如下:
| 參數 | 參數類型 | 描述 | 是否必須 |
|---|---|---|---|
| endpoint | string | 域名 | 是 |
| accessKey | string | AccessKey,簡稱ak | 是 |
| secretKey | string | SecretKey ,簡稱sk | 是 |
| logProject | string | 日志單元編碼 | 是 |
| userAgent | string | 使用的SDK信息標識 | 否 |
| requestTimeout | int | http請求超時時間,默認30s | 否 |
| compressType | int | 日志壓縮算法,默認“lz4” | 否 |
| securityToken | TokenInfo* | 獲取的token信息 | 否 |
| httpCurl | CURL* | 定義的CURL ,用于http請求 | 否 |
示例代碼:初始化Client配置
Client *client = new Client(endpoint, ak, sk,logProject);
//或者
ClientConfig clientConfig(endpoint, ak, sk, logProject);
Client *client = createClient(clientConfig);
4.2. 關于臨時憑證Token的操作
4.2.1. SetTokenByApi()
此操作是為client注入token信息,這一步需要使用ak和sk信息換取臨時憑證TokenInfo,其中包含了token隨機串和過期時間兩個參數。這一步需要去訪問CTIAM的api接口,調用api接口,傳入ak/sk/endpoint信息,返回token信息。
TokenInfo信息如下:
| 參數 | 類型 | 描述 |
|---|---|---|
| token | string | token 隨機串 |
| expireTime | int64 | 過期時間,默認30分鐘 |
獲取TokenInfo這一步在Client 初始化時會自動調用。用戶默認可以不用進行這一步操作。
示例代碼:為client注入Token信息
this->SetTokenByApi();
4.3. 關于Log的操作
4.3.1. logItems.push_back(logItem)
此操作用于生成待上傳的日志,日志上傳只能上傳LogItem格式的日志,logItems是一個vector類型,里面包含若干條LogItem日志,格式如下:
| 參數 | 類型 | 描述 | 是否必須 |
|---|---|---|---|
| logItems | vector |
LogItem格式的vector數組,將多份日志組合起來發送 | 是 |
LogItem類型的日志格式如下如下。
| 參數 | 類型 | 描述 | 是否必須 |
|---|---|---|---|
| logTimestamp | int | 時間戳,單位納秒 | 是 |
| originMesssage | string | 原始日志內容 | 是 |
| contents | map<string,any> | 日志內容,分詞后的內容,可用于索引 | 否 |
| labels | map<string,any> | 自定義標簽 | 否 |
注意:其中Contents和Labels的key的長度不超過64字符,僅支持數字、字母、下劃線、連字符(-)、點(.),且必須以字母開頭。value類型最好使用字符串(string)和數字類型(int,double),其他類型建議先轉為字符串類型,并且value值不能為空或空字符串。
示例代碼:組裝生成10條日志
vector<LogItem> logItems;
for (int k = 0; k < 10; k++) {
? ? ?int64_t logTimestamp = GetCurrentTimeStamp(3);
? ? ?string oriMessage = "c++ sdk test oriMessage";
? ? ?map<std::string, boost::any> contents;
? ? ?map<std::string, boost::any> labels;
? ? ?contents["level"] = string("error");
? ? ?contents["unit_id"] = 3.1415926;
? ? ?labels["user_tag"] = string("string");
? ? ?LogItem logItem;
? ? ?
logItem.logTimestamp = logTimestamp;
logItem.oriMessage = oriMessage;
logItem.contents = contents;
logItem.labels = labels;
logItems.push_back(logItem);
}
4.4. 關于日志上傳的操作
4.4.1. PutLogs()
此操作用于日志上傳服務,需要傳入的參數有三個,分別是logProject(日志項目編碼),logUnit(日志單元編碼),logItems(要上傳的日志)。
| 參數 | 類型 | 描述 | 是否必須 |
|---|---|---|---|
| logProject | string | 日志項目編碼 | 是 |
| logUnit | string | 日志單元編碼 | 是 |
| logItems | vector |
日志信息 | 是 |
示例代碼:上傳日志
LogResponse logResponse = client->PutLogs(logProject, logUnit, logItems);
cout<<logResponse.statusCaode<<":"<<logResponse.message<<":"<<logResponse.error<<endl;
logResponse 是LogResponse 格式的返回響應體,如下表格式:
| 參數 | 類型 | 描述 | 示例 |
|---|---|---|---|
| statusCode | int | 返回碼,取值范圍:0:-正常、-1:嚴重錯誤,其他自定義 | |
| message | string | 狀態描述 | SUCCESS |
| error | string | 參考錯誤編碼列表 |
日志服務相關錯誤編碼(部分):
| statusCode | error | message |
|---|---|---|
| -1 | LTS_8000 | 請求失敗,請稍候重試,或提交工單反饋 |
| -1 | LTS_8001 | 內容不合法,無法解析 |
| -1 | LTS_8004 | 日志內容包含的日志必須小于[x] MB和[y]條 |
| -1 | LTS_8006 | 日志內容解壓失敗 |
| -1 | LTS_8007 | Token失效,請重新獲取 |
| -1 | LTS_8009 | 無云日志服務產品實例,請先開通云日志服務 |
| -1 | LTS_8010 | 日志項目不存在 |
| -1 | LTS_8011 | 日志單元不存在 |
| -1 | LTS_8013 | 在1個日志項目下,寫入流量最大限制:200MB/s |
| -1 | LTS_8014 | 在1個日志項目下,寫入次數最大限制:1000次/s |
| -1 | LTS_8015 | 在1個日志單元下,寫入流量最大限制:100MB/s |
| -1 | LTS_8016 | 在1個日志單元下,寫入次數最大限制:500次/s |
| -1 | LTS_18000 | 調用ITIAM的接口失敗 |
5. 服務代碼-異步批量上傳
異步批量上傳是為了解決同步上傳無法高頻異步發送等問題所增加的模塊。原理是會開啟多個線程,當調用日志發送接口后,會立刻返回,而內部的線程會將日志數據緩存合并,最后進行批量發送。
5.1. 關于Producer操作
5.1.1. ProducerConfig()
此操作是初始化producer配置,producer可以看作是一個啟動器,內部封裝了異步線程的初始化、啟動和關閉等功能,只需要對producer進行操作,即可安全便捷地控制這些異步的線程。使用這份producerConfig配置去初始化一個producer。
//使用默認參數
ProducerConfig producerConfig = ProducerConfig();
//自定義參數
producerConfig.setLingerMs(2000);
producerConfig.setMaxBatchCount(4096);
...
producerConfig內的屬性是異步操作中的線程所需要的參數,如果不設置參數,則初始化的時候會使用默認的參數,默認參數如下所示:
| 參數 | 類型 | 描述 | 默認值 |
|---|---|---|---|
| max_worker_count_ | int | 線程數 | 4 |
| max_batch_size_ | int | 每批日志大小,最大值5MB | 512KB |
| max_batch_count_ | int | 每批日志條數,最大值40960 | 4096 |
| linger_ms_ | int | 定時器等待時間 | 2000 |
5.2. 關于異步批量日志上傳操作
5.2.1. SendLogs()
此操作是將日志發送到后臺的日志累加器隊列中,然后立刻返回。累加器的狀態達到可發送條件時(日志量達到閾值或者等待時間達到閾值),后臺任務的線程將里面的日志進行打包批量發送,參數如下:
| 參數 | 類型 | 描述 | 是否必須 |
|---|---|---|---|
| logProject | int | 日志項目編碼 | 是 |
| logUnit | int | 日志單元編碼 | 是 |
| logItem logItems | LogItem vector |
待上傳的日志,可以是單條,也可以是多條(支持重載) | 是 |
producer.sendLogs(logProject, logUnit, logItem);
sendLogs()方法有很多重載方法,可以滿足多種類型的發送,既可以發送單條日志,也可以發送多條日志,同時也可以根據需求是否需要結果返回值。類型如下:
sendLogs(string logProject,string logUnit,LogItem &logItems);
sendLogs(string logProject,string logUnit,vector<LogItem> &logItems);
sendLogsCallback(string logProject,string logUnit,LogItem &logItems);
sendLogsCallback(string logProject,string logUnit,vector<LogItem> &logItems);
5.2.2. onCallback()
此操作是為線程注冊回調函數,異步非阻塞獲取返回結果。如果您想自定義回調函數,可以修改onCallback()函數。onCallback() 函數位于ioWorker.cpp 內。
void onCallback(LogResponse &response, int logsize){
? ? std::cout << "statusCode:" << response.statusCode
<< ", message:" << response.message
<< ", error:" << response.error
<< ", count:" << logsize << std::endl;
}
5.2.3. safeClose()
此操作是用于關閉producer。當不再需要發送數據或當前進程即將終止時,關閉producer是必要的步驟,以確保producer中緩存的所有數據都能得到妥善處理。當調用時,異步線程會停止接收數據,然后把所有緩存數據放進線程發送上傳,等待所有線程任務結束后停止任務。
producer.safeClose();