.NET 追蹤 SDK

使用 Instana 進行追蹤是自動的,但如果您想要更深入瞭解自訂程式碼、特定應用程式區域或某個內部元件,則可以使用 Instana 的 .NET Tracing SDK ,如下所示。

安裝 SDK

我們在 nuget.org的正式資訊來源上以 nuget-package 形式提供 SDK for .NET。 請搜尋 Instana.ManagedTracing.Sdk ,以尋找並新增至您的專案。 安裝之後,您的專案將有兩個額外的參照 (Instana.ManagedTracing.Sdk Instana.ManagedTracing.Api)

追蹤您自己的程式碼

下列範例將示範如何將分散式追蹤功能新增至程式碼。

附註: SDK 處於作用中開發狀態,因此請務必在每次更新時重新造訪此頁面,以確保使用最新的 API。

建立簡式跨距

追蹤方法呼叫最簡單的方法是使用 CustomSpan.Create API。

public void MyTracedMethod()
{
    using(var span = CustomSpan.Create())
    {
        // your original code here
    }
}

上面的程式碼將建立中間跨距,代表您的方法以及在其中花費的時間。

如果您想要建立「進入」或「結束跨距」,而不是使用中間,則有一些方便的 API (分別稱為 CustomSpan.CreateEntryCustomSpan.CreateExit )。

建立簡式跨距並擷取異常狀況

如果您想要註釋已建立的文字段在處理方法主體期間發生的任何錯誤,您當然可以使用類似這樣的 CustomSpan.SetError API 來手動執行此動作。

public void MyTracedMethod()
{
    using(var span = CustomSpan.Create())
    {
        try
        {
        // your original code here
        }
        catch(Exception e)
        {
            span.SetError(e);
        }
    }
}

更簡單且偏好的方法是改用 CustomSpan.WrapActionCustomSpan.Wrap<T> API。

public void MyTracedMethod()
{
    using(var span = CustomSpan.Create())
    {
        // setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
        // captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
        span.WrapAction(
            ()=>{
                // your original code here
            }, true);
    }
}

如果您要包裝的程式碼區塊傳回進一步處理所需的內容,請改用 CustomSpan.Wrap<T> API。

public void MyTracedMethod()
{
    using(var span = CustomSpan.Create())
    {
        // setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
        // captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
        bool result = span.Wrap<bool>(
            ()=>{
                // your original code here
                return myBooleanValue;
            }, true);
    }
}

現在我們知道如何創造入口、出口和中間產品。 我們也瞭解如何使用 SetErrorWrapAction / Wrap<T> API 來擷取異常狀況。

將資料新增至跨距

跨距本身只是由計時、呼叫堆疊及名稱所組成。 這很好,但在大多數情況下沒有多大幫助。 那麼在您的跨距中新增一些資料如何? 跨距可以包含 DataTagsCustomSpan 類別為其提供簡式 API。

public void MyTracedMethod(string userName, string someSuperRelevantData)
{
    using(var span = CustomSpan.Create())
    {
        span.SetData("username", userName);
        span.SetData("relevant", someSuperRelevantData);
        span.WrapAction(
            ()=>{
                // your original code here
            }, true);
    }
}

資料正在傳輸至後端,可透過下載追蹤從使用者介面下載。 不過,這裡放置的資料不會顯示在 Instana 使用者介面中。

將標籤新增至跨距

您也可以使用 SetTag,而不使用 SetData ,它會採用字串陣列作為索引鍵 (可用來將您傳遞至跨距作為階層的資料結構化)。

public void MyTracedMethod(string userName, string someSuperRelevantData)
{
    using(var span = CustomSpan.Create())
    {
        span.SetTag("username", userName);
        span.SetTag("relevant", someSuperRelevantData);
        span.WrapAction(
            ()=>{
                // your original code here
            }, true);
    }
}

標籤會直接顯示在「通話詳細資料」視圖中,也可以在「無界限」分析中搜尋。

將自訂跨距對映至服務

您通常想要將自訂跨距與 Instana 應用程式視景中的邏輯服務相關聯。 這與呼叫 SetServicename API 一樣簡單, API 只採用字串。 若要區分使用 SDK 實作的端點,您也可以提供端點,以使用 SetEndpointName API 進行更詳細的對映。

public void MyTracedMethod(string userName, string someSuperRelevantData)
{
    using(var span = CustomSpan.Create())
    {
        span.SetServiceName("AwesomeSDKService");
        span.SetEndpointName("TracingEndpoint");
        .
        .
        .
    }
}

雖然您可以在每個跨距上設定服務及端點,但請務必注意,如果有 INTERMEDIATE 跨距,則會捨棄這些設定 (它們將繼承自前一個跨距 ENTRY)

擷取跨距的結果

說出您檢測的方法會傳回值。 讓我們假設在您的追蹤中具有此值將有助於疑難排解。 輸入 SetResult API。

public bool MyTracedMethod(string userName, string someSuperRelevantData)
{
    using(var span = CustomSpan.Create())
    {
        bool result = span.Wrap<bool>(
            ()=>{
                // your original code here
                return resultingBoolean;
            }, true);

        span.SetResult(result.ToString());
        return result;
    }
}

巢狀跨距

巢狀跨距與從另一個方法呼叫一個方法一樣容易。 為了完整,以下是範例。 我們假設有一個方法會作為項目,而其子項跨距是中間的。

public bool MyTracedEntryMethod(string userName, string someSuperRelevantData)
{
    using(var span = CustomSpan.CreateEntry())
    {
        bool result = span.Wrap<bool>(
            ()=>{
                List<string> data = this.GetSomeDataFromSomewhere();
                // do some heavy processing
                return theResultICameUpWith;
            }, true);

        span.SetResult(result.ToString());
        return result;
    }
}


private List<string> GetSomeDataFromSomewhere()
{
    using(var span = CustomSpan.Create())
    {
        List<string> result = span.WrapAction(
            ()=>{
                // read data from somewhere...
                return theListICameUpWith;
            }, true);

        span.SetResult(result.ToString());
        return result;
    }
}

此結果會是進入跨距,具有作為子項的中間跨距。 從技術上來說,巢狀沒有深度限制,但您不應在深度遞迴中建立跨距。

在分散式追蹤中尋找 distributed

到目前為止,我們所觀察到的所有跨距都僅限於一項服務。 他們從未接觸過其他元件,這些元件也會追蹤其活動。 若要達到跨服務界限的真正分散式追蹤,您需要套用一些相關性。

追蹤中的相關性說明如何在結束呼叫中設定相關性資料,以及如何在其他元件進入時重新取得此資料並「繼續」此環境定義。

為了達到此目的, CustomSpan 會超載 CustomSpan.CreateExitCustomSpan.CreateEntry 方法。

雖然 CustomSpan.CreateExit 可以使用 Action<string, string> 作為引數,但 CustomSpan.CreateEntry 會採用 Func<DistributedTraceInformation>

那是如何運作的呢?

假設我們有一個小型 Message 類別,它會傳遞給我們正在呼叫的遠端服務。

    public class Message
    {
        public Message()
        {
            this.Tags = new Dictionary<string, string>();
        }
        public Dictionary<string, string> Tags { get; private set; }
        public int Payload { get; set; }

        public void AddTag(string tagName, string tagValue)
        {
            this.Tags.Add(tagName, tagValue);
        }
    }

這裡的相關部分是 AddTag 方法,它採用兩個字串。 這是我們需要提供給 CustomSpan.CreateExit的簽章。

在呼叫 CreateExit 時使用此方法,會將相關性資料寫入 Message的實例。

public void MyLocalEntryMethod()
{
    // this methd will create an entry span and then call our method
    // that communicates with the remote-service (and thus create our exit span)
    using(var span = CustomSpan.CreateEntry())
    {
        span.WrapAction(()=>{
            CallRemoteService();
        })
    }
}

public void CallRemoteService()
{
    Message message = new Message();
    using(var exitSpan = CustomSpan.CreateExit(this, message.AddTag))
    {
        exitSpan.WrapAction( ()=>{
            var service = new RemoteService();
            service.ValidateRequest(message);
        }
    }
}

因此當我們的訊息離開本端元件的範圍並呼叫 service.ValidateRequest(message)時,它會在其標籤清單中包含相關性資料。 讓我們看看如何在被呼叫的網站上擷取這些。

public void ValidateRequest(Message message)
{
    using(var span = CustomSpan.CreateEntry(this, ()=>ExtractCorrelationData(message))
    {
        // do whatever this method is supposed to do, we only care for extraction
        // if the correlation-data anyway :-)
    }
}

private DistributedTraceInformation ExtractCorrelationData(Message message)
{
    var dti = new DistributedTraceInformation();
    dti.ParentSpanId = Convert.ToInt64(message.Tags[TracingConstants.ExternalParentSpanIdHeader], 16);
    dti.TraceId = Convert.ToInt64(message.Tags[TracingConstants.ExternalTraceIdHeader], 16);
    return dti;
}

這裡的相關部分是 ExtractCorrelationData。 因為我們在建立結束程式時使用 Message.AddTag 方法,所以 SDK 會將相關 ID (parent-span-id 及 trace-id) 寫入訊息的 tags-list ,然後再將它傳送至服務。

現在,服務可以透過讀取標籤來再次擷取這些值 (我們可以透過上述程式碼中用作索引鍵的常數來識別它們)。 然後, SDK 將使用我們建立的 DistributedTraceInformation 實例,將新建立的結束程式附加至已存在的追蹤。 因此,我們建立的進入範圍將是本端服務上結束程式的對應項目。

分割追蹤

當自動化追蹤執行太多,且建立的追蹤太長而無法理解時,就會有一些範例。 例如,當伺服器在長時間執行背景作業的進度上透過回呼將更新項目推送至用戶端時,這可能在長雙工 WCF 通訊中發生。 這種通訊會導致長時間追蹤,可能太長而無法顯示在使用者介面中。

在這類情況下,有一個選項可將追蹤分成多個追蹤,更具體而言,是將用戶端的每一個回呼分成個別追蹤。 若要這樣做,您可以使用 CustomSpan 中的 CreateEntryForNewTrace 來停止現行追蹤,並從該點開始建立新的追蹤。 在呼叫用戶端之前,您會想要在伺服器端執行此動作:

var callbackChanell = OperationContext.Current.GetCallbackChannel<IMathResult>();
if (callbackChanell != null)
{
    using (var span = CustomSpan.CreateEntryForNewTrace(this))
    {
        callbackChanell.SendStatusUpdate(new MathArguments() { InParam = args.InParam, Progress = (float)i / args.InParam, Result = generator.Next(1000, 9999) });
    }
}

瞭解嗎?

現在您應該可以使用我們的 SDK 來建立第一個自訂追蹤。 如果您有任何問題 (或願望!) 關於 SDK ,歡迎透過 支援中心聯絡我們。 快樂追蹤!