TARS 染色日誌|收集記錄特定日誌

做者|Eatonhtml

導語|
記日誌能夠說是程序猿/媛平常開發中的屢見不鮮了。在平常業務場景中,常常須要分析特定用戶的日誌,通常的日誌記錄方式很難知足需求,有什麼解決辦法呢?TARS 框架中包含染色日誌的功能,可以記錄特定用戶的日誌,優雅地解決這一問題。本文將會介紹染色日誌的原理和功能,以及如何在 TARS 中使用染色日誌。ios

目錄

  • 背景
  • 初識染色日誌
  • TARS 染色功能概述
  • 染色日誌初體驗git

    • 主動打開染色日誌
    • 被動打開染色日誌
    • 添加特定邏輯
  • 總結

背景

不少業務場景中,須要對特定用戶的行爲進行追蹤,好比部分用戶反饋服務有 BUG,須要排查,但發現只是個例,要單獨找出這些用戶的日誌進行分析;又或是 APP 上線了一個新的功能,先對一部分用戶開放進行測試,須要追蹤這部分用戶的行爲等。github

按照通常方式,要在已經記錄的日誌中檢索這部分用戶的日誌。看起來挺簡單,可是在微服務的大背景下,服務間調用關係複雜(以下圖),日誌分散在各個服務節點上。對於一次調用,須要先獲取調用鏈,再找出具體服務調用的節點,而後從節點上獲取日誌,這個過程很是繁瑣。segmentfault

另外,日誌的記錄會消耗程序的性能,佔用用戶的計算資源。所以在正式環境中,平常業務都會控制日誌打印的量,確保日誌打印不影響用戶服務。而在對特定用戶行爲進行分析時,每每須要記錄額外的日誌,才能知足分析等需求,直接增長日誌的打印顯然不太現實。後端

可見,傳統的日誌記錄方式沒法知足對特定用戶行爲的日誌記錄,染色日誌就是來解決這一問題的。服務器

初識染色日誌

什麼是染色日誌呢?在一個調用鏈裏面,標誌出某個特定需求的過程,讓整個調用鏈裏的上下文信息一直被傳輸下去,就像一開始被標記染色同樣,這種標記日誌的方式,咱們叫它染色日誌。微信

簡單來講,就是咱們只想要某幾個用戶的日誌信息,經過染色日誌把這幾個用戶的日誌另外打印一份,並收集在同一個地方,這樣咱們在一個地方就能查找到這些染色用戶的日誌。app

咱們能夠用染色日誌定位一些特定的位置,也能夠定一些特定的邏輯。好比某微信用戶反饋語音發送存在問題,經過對用戶ID(微信號)進行染色,後端服務接收到染色用戶的請求後,就會將該用戶本次調用的處理過程日誌打印出來,能夠很是方便地獲取咱們須要的關鍵信息。除了打印日誌,還能夠經過染色執行特定邏輯,好比新上線一個功能,開放給一部分用戶使用,經過染色,後端服務能夠判斷用戶是否在測試名單中,再執行相應的服務邏輯。框架

TARS 染色功能概述

TARS 框架支持染色日誌功能,能夠在某服務某接口中對特定用戶號碼的消息進行染色,方便地實時察看該用戶引發後續全部相關調用消息流的日誌。下圖爲 TARS 中的染色流程

染色日誌打開後,在業務中經過指定染色的 key 值,例如指定 QQ 號爲 123456 的用戶,後續該用戶的請求都會在框架中被自動染色,並經過日誌服務(tarslog)集中打印染色日誌。

咱們只須要到 tarslog 服務所在的服務器上查看相應的染色日誌便可,具體的路徑通常爲

/usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/

該目錄下通常包含兩種日誌文件:染色的滾動日誌和染色的按天日誌,日誌文件名格式分別爲 tars_dyeing.dyeing_roll_yyyymmdd.log, tars_dyeing.dyeing_day_yyyymmdd.log,好比:

# 染色滾動日誌
tars_dyeing.dyeing_roll_20201103.log
# 染色按天日誌
tars_dyeing.dyeing_day_20201103.log
滾動日誌是服務的本地日誌,通常用於記錄服務調試信息;按天日誌是服務的遠程日誌,通常用於記錄重要的業務信息。開啓染色後,若是接收到染色的請求,兩種日誌都會額外打印一份到 tarslog,只記錄染色請求打印的日誌,天天保存一個文件。

關於滾動日誌和按天日誌的用法以及更多信息,能夠看到官方文檔中日誌部分,這裏再也不贅述。

染色日誌初體驗

TARS 框架的染色日誌有主動打開和被動打開兩種方式。接下來讓咱們經過實例,瞭解如何經過這兩種方式在 TARS 中使用染色日誌。本章中使用的實例源碼均可以在這裏找到。

主動打開染色日誌

主動打開,指的是在發起請求的客戶端的業務代碼中,主動打開框架的染色日誌開關。具體流程以下:

  1. 在客戶端程序適當的地方聲明染色日誌開關 tars::TarsDyeingSwitch 的對象。
  2. 調用該開關對象的 enableDyeing 方法便可打開染色日誌。被調用的服務也會自動打開染色開關,並打印日誌到 tarslog。若是該被調服務還要調用其餘服務,則自動傳遞給下個服務。
  3. 在染色日誌開關關閉前,客戶端打印的全部日誌和被調用的服務打印的日誌,都會額外打印一份到日誌服務 tarslog
  4. 客戶端開關對象析構,染色日誌關閉,後續的調用和日誌打印再也不生成染色日誌。

下面,咱們經過一個實例來了解如何在客戶端(主調方)中主動打開染色日誌。

實例

這裏以 C++ 爲例,按照剛纔介紹的流程,開啓染色日誌並調用 TARS 服務。

在開始以前,咱們須要先準備一個 TARS 服務,供以後的客戶端調用,源碼見 TestServer 源碼。咱們建立一個服務,應用名爲 TestApp,服務名 TestServerObj 名爲 TestTest.tars 文件中定義的接口以下

module TestApp
{

interface Test
{
    int test(string id, out string output);
};

};

TestImp.hTestImp.cpptest 接口的定義和實現以下

// TestImp.h
virtual int test(const std::string &id, std::string &output, tars::TarsCurrentPtr current);

//TestImp.cpp
int TestImp::test(const std::string &id, std::string &output, tars::TarsCurrentPtr current)
{
    // 打印本地日誌
    TLOGDEBUG(__FILE__ << "|" << __LINE__ << "|" << "id: " << id << endl);
    output = id;
    return 0;
}

構建該服務並在 TarsWeb 上部署發佈便可。

關於 TARS 服務的建立與部署,參考文檔 TARS 開發入門,這裏再也不贅述。

接下來,咱們編寫一個簡單的客戶端 Demo,完成打開染色日誌、調用 TARS 服務接口的流程,以下

#include <iostream>
#include <string>

#include "servant/Communicator.h"
#include "servant/TarsLogger.h"
#include "util/tc_option.h"
#include "util/tc_file.h"

#include "Test.h"

using namespace std;
using namespace tars;

int main(int argc,char ** argv)
{
    try
    {   // 初始化通訊代理,用於調用服務
        CommunicatorPtr comm = new Communicator();
        comm->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h 192.168.0.121 -p 17890 -t 10000");
        TarsRollLogger::getInstance()->setLogInfo("TestApp", "TestServer", "./log", 100000, 10, comm, "tars.tarslog.LogObj");
        TarsRollLogger::getInstance()->sync(false);
        TarsTimeLogger::getInstance()->setLogInfo(comm, "tars.tarslog.LogObj", "TestApp", "TestServer", "./log");

        {   // 在打開染色日誌以前,打印日誌,這條日誌只會打印到本地日誌中。
            TLOGDEBUG     (__FILE__ << "|" << __LINE__ << "|" << "Test Before Dyeing" << endl);
            DLOG        << __FILE__ << "|" << __LINE__ << "|" << "D/Test Before Dyeing" << endl;
        }

        {   // 聲明一個染色日誌開關對象
            TarsDyeingSwitch dye;
            // 打開染色日誌
            dye.enableDyeing();

            /* 在打開染色日誌以後,打印日誌,會在本地日誌和染色日誌中看到
             * TLOGDEBUG: 打印本地 DEBUG 日誌
             *      DLOG: 打印按天的遠程日誌
             */
            {
                TLOGDEBUG(__FILE__ << "|" << __LINE__ << "|" << "Test After Dyeing" << endl);
                DLOG   << __FILE__ << "|" << __LINE__ << "|" << "D/Test After Dyeing" << endl;
            }

            // 生成調用代理
            TestApp::TestPrx prx = comm->stringToProxy<TestApp::TestPrx>("TestApp.TestServer.TestObj");
            string output;
            // 調用 test 接口
            prx->test("hello", output);
            cout << output << endl;
        }

        //染色日誌開關已經析構,染色功能失效,之後的日誌不會打印到染色日誌中
        TLOGDEBUG(__FILE__ << "|" << __LINE__ << "|" << "~Dyeing" << endl);
        DLOG   << __FILE__ << "|" << __LINE__ << "|" << "D/~Dyeing" << endl;
    }
    catch(exception& e)
    {
        cerr << "exception:" << e.what() << endl;
    }
    catch (...)
    {
        cerr << "unknown exception." << endl;
    }
    sleep(10); // 等待異步寫日誌線程同步日誌數據到 logserver
    return 0;
}

能夠看到,該客戶端中包含了三第二天志打印,只有一次在染色日誌開啓期間打印。同時,染色日誌開啓期間,調用了服務 TestApp.TestServer.TestObj 的接口 test

編譯並執行客戶端後,咱們就能夠在日誌服務所在服務器的路徑 /usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/ 中,找到染色日誌文件,如 tars_dyeing.dyeing_roll_20201104.logtars_dyeing.dyeing_day_20201104.log

  • tars_dyeing.dyeing_roll_20201104.log
# 使用TLOGDEBUG打印的滾動日誌
192.168.0.121|TestApp.TestServer|2020-11-04 03:56:53|139734968739712|DEBUG|main.cpp|37|Test After Dyeing
  • tars_dyeing.dyeing_day_20201104.log
# 使用DLOG打印的遠程日誌
192.168.0.121|TestApp.TestServer|2020-11-04 03:56:53|main.cpp|38|D/Test After Dyeing

能夠看出,染色日誌文件中,只有在 TarsDyeingSwitch dye 做用域內打印的日誌。

有興趣的讀者能夠下載客戶端 源碼自行嘗試,將其中的 IP 替換爲本身服務所在節點的 IP 便可。

被動打開染色日誌

被動打開,指的是在請求的服務端預先設定染色的條件,判斷傳遞的 key 值,由服務端來打開自身的染色日誌開關,相比於主動打開的方式,被動打開不須要修改客戶端(調用方)業務代碼,是一種非侵入式的方式。具體流程以下:

  1. 在 tars 接口文件中,經過 routekey 來設置接口的某個參數爲 key,如用戶 ID;
  2. 在 TarsWeb 經過框架命令傳入須要染色的用戶ID的值以及服務 Obj 和接口名;
  3. 服務收到匹配的請求後,對請求包進行染色處理,並打印日誌到日誌服務 tarslog
  4. 服務處理過程若是繼續調用其餘 TARS 服務,則自動傳遞給下個服務。

接下來咱們經過一個實例,瞭解如何經過被動方式打開染色日誌。

實例

  • 將接口 test 的參數 id 設置爲染色 key

這裏咱們繼續使用前面建立的 TARS 服務 Demo。在 tars 接口文件中,修改接口 test,使用 routekey 指定其中的參數 id 爲染色 key,以下

int test(routekey string id, out string output);

接着,替換客戶端(調用方)本來使用的 tars 文件並從新編譯構建就完成了客戶端的修改,無需修改業務代碼。

  • 開啓染色日誌並設置須要染色的 key 值(testid 的值)

TARS 框架中預設了一些框架命令,可以經過 TarsWeb 平臺發送。這裏咱們能夠經過命令 tars.setdyeing 開啓染色日誌,命令格式以下:

tars.setdyeing dyeingKey dyeingServant [dyeingInterface]

三個參數分別爲染色 key 值(routekey 標記的參數,本例爲 id),服務對象名稱,接口名稱(可選)。 例如本文中的服務對象爲 TestApp.TestServer.TestObj,染色接口名稱爲 test,咱們想對 id123456 的用戶染色,那麼能夠經過管理平臺發佈如下命令:

tars.setdyeing 123456 TestApp.TestServer.TestObj test

該命令經過 TarsWeb 頁面的 服務管理->更多命令->自定義命令 來發送,如圖所示

若後續沒有滾動日誌輸出,能夠在此頁面設置日誌等級爲 DEBUG

完成上述步驟,就完成了染色的打開和染色用戶ID值的添加。

當接口 testid123456 的請求發到該服務,將會另外打印染色日誌,一樣保存在日誌服務(tarslog)機器的路徑 /usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/ 下的滾動日誌文件和按天日誌文件中。

這裏咱們能夠繼續使用上節的客戶端,由於不須要在代碼中主動聲明並打開染色開關,對客戶端代碼進行精簡,保持原有的接口調用邏輯便可,修改後以下

#include <iostream>
#include <string>

#include "servant/Communicator.h"
#include "util/tc_option.h"
#include "util/tc_file.h"

#include "Test.h"

using namespace std;
using namespace tars;

int main(int argc,char ** argv)
{
    try
    {   // 初始化通訊代理,用於調用服務(讀者無需關注)
        CommunicatorPtr comm = new Communicator();
        comm->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h 192.168.0.121 -p 17890 -t 10000");

        // 生成調用代理
        TestApp::TestPrx prx = comm->stringToProxy<TestApp::TestPrx>("TestApp.TestServer.TestObj");
        string output;
        // 調用 test 接口, 傳入染色id: 123456
        prx->test("123456", output);
        cout << output << endl;
        // 調用 test 接口, 傳入非染色id: tars
        prx->test("tars", output);
        cout << output << endl;
    }
    catch(exception& e)
    {
        cerr << "exception:" << e.what() << endl;
    }
    catch (...)
    {
        cerr << "unknown exception." << endl;
    }
    return 0;
}
參考客戶端 源碼

編譯執行後,咱們能夠在染色日誌文件中,例如 tars_dyeing.dyeing_roll_20201104.log

192.168.0.121|TestApp.TestServer|2020-11-04 15:58:08|140356905592576|DEBUG|TestImp.cpp|31|id: 123456

添加特定邏輯

前面咱們提到過,可以在服務中爲染色的用戶添加特定的邏輯,接下來將繼續以 TestServer 爲例,介紹如何爲染色用戶添加特定邏輯。

實現方式其實很簡單:經過判斷傳入的請求是否爲染色請求,決定是否執行特定的邏輯便可。例如,咱們在 TestImp.cpp 中修改 TestServer 接口的實現,若是當前用戶是染色用戶則輸出 Dyeing is on,普通用戶則輸出用戶 id,代碼以下

int TestImp::test(const string &id, string &output, tars::TarsCurrentPtr current)
{   // 服務對象名
    string servantName =  ServerConfig::Application + "." + ServerConfig::ServerName + ".TestObj";

    // 判斷是否爲染色用戶
    if (tars::ServantHelperManager::getInstance()->isDyeingReq(id, servantName, "test")) {
        // 染色用戶返回值
        output = "Dyeing is on";
        TLOGDEBUG(__FILE__ << "|" << __LINE__ << "|" << "Test"  << endl);
        DLOG   << __FILE__ << "|" << __LINE__ << "|" << "D/Test"  << endl;
        return 0;
    }
    // 非染色用戶返回用戶 id
    output = id;
    TLOGDEBUG(__FILE__ << "|" << __LINE__ << "|" << "Test" << endl);

    return 0;
}

上述代碼中,咱們使用 ServantHelperManager 中的 isDyeingReq 來判斷請求 ID 是否染色,染色則返回 true,並在 if 中添加對染色用戶執行的邏輯。isDyeingReq 須要傳入三個參數,routekey 標識的入參值(id),服務對象名(TestApp.TestServer.TestObj)和接口名稱(test)。

接下來,咱們直接使用上一節的客戶端實例,其中包含了兩次接口調用

...
        // 調用 test 接口, 傳入染色id: 123456
        prx->test("123456", output);
        cout << output << endl;
        // 調用 test 接口, 傳入非染色id: tars
        prx->test("tars", output);
        cout << output << endl;
...

前面咱們已經在 TarsWeb 平臺開啓了對 123456 的染色,沒有開啓對 tars 的染色,編譯並執行該客戶端,結果以下

Dyeing is on
tars

可見 id123456 的請求成功觸發染色邏輯,而 idtars 的請求沒有。

總結

染色日誌填補了傳統日誌記錄方式的不足,經過相似染色的方式,實現了對特定用戶、調用鏈日誌的集中打印,方便日誌的查看和分析。TARS 框架包含了染色功能,並提供了主動與被動兩種打開染色日誌的方式,方便用戶根據需求選擇。同時,還能爲特定的請求添加額外的特定邏輯,進一步擴大使用場景,如灰度發佈等。

TARS 能夠在考慮到易用性和高性能的同時快速構建系統並自動生成代碼,幫助開發人員和企業以微服務的方式快速構建本身穩定可靠的分佈式應用,從而令開發人員只關注業務邏輯,提升運營效率。多語言、敏捷研發、高可用和高效運營的特性使 TARS 成爲企業級產品。

TARS微服務助您數字化轉型,歡迎訪問:

TARS官網:https://TarsCloud.org

TARS源碼:https://github.com/TarsCloud

Linux基金會官方微服務免費課程:https://www.edx.org/course/bu...

獲取《TARS官方培訓電子書》:https://wj.qq.com/s2/6570357/...

或掃碼獲取:

QR

相關文章
相關標籤/搜索