使用 Application Insights .NET SDK 追蹤自訂作業

Application Insights SDK 會自動追蹤連入 HTTP 要求和相依服務的呼叫,例如 HTTP 要求和 SQL 查詢。 追蹤和相互關聯要求和相依性可讓您查看結合此應用程式之所有微服務的整個應用程式的回應性和可靠性。

有一個類別的應用程式模式無法一般支援。 正確監視這類模式需要手動程式代碼檢測。 本文涵蓋一些可能需要手動檢測的模式,例如自定義佇列處理和執行長時間執行的背景工作。

本文提供如何使用Application Insights SDK追蹤自定義作業的指引。 本檔與下列項目相關:

  • 適用於 .NET 的 Application Insights (也稱為 Base SDK) 2.4+ 版。
  • 適用於 Web 應用程式的 Application Insights (執行 ASP.NET) 2.4 版+ 。
  • 適用於 ASP.NET Core 2.1+ 版的 Application Insights。

注意

下列文件依賴Application Insights傳統 API。 Application Insights 的長期計劃是使用 OpenTelemetry 收集數據。 如需詳細資訊,請參閱 為 .NET、Node.js、Python 和 Java 應用程式啟用 Azure 監視器 OpenTelemetry。

概觀

作業是由應用程式執行的邏輯工作片段。 它有名稱、開始時間、持續時間、結果,以及執行的內容,例如使用者名稱、屬性和結果。 如果作業 A 是由作業 B 起始,則作業 B 會設定為 A 的父系。作業只能有一個父代,但可以有許多子作業。 如需作業和遙測相互關聯的詳細資訊,請參閱 Application Insights 遙測相互關聯

在 Application Insights .NET SDK 中,作業是由抽象類 OperationTelemetry 及其子系 RequestTelemetryDependencyTelemetry 所描述。

連入作業追蹤

Application Insights Web SDK 會自動收集在 IIS 管線和所有 ASP.NET Core 應用程式中執行之 ASP.NET 應用程式的 HTTP 要求。 其他平臺和架構都有社群支持的解決方案。 如果任何標準或社群支援的解決方案都不支援應用程式,您可以手動檢測它。

另一個需要自定義追蹤的範例是接收佇列專案的背景工作角色。 對於某些佇列,將訊息新增至此佇列的呼叫會追蹤為相依性。 描述訊息處理的高階作業不會自動收集。

讓我們看看如何追蹤這類作業。

概括而言,工作是建立 RequestTelemetry 和設定已知屬性。 作業完成後,您可以追蹤遙測。 下列範例示範這項工作。

Owin 自我裝載應用程式中的 HTTP 要求

在此範例中,追蹤內容會根據 相互關聯的 HTTP 通訊協議傳播。 您應該預期會收到該處所描述的標頭。

public class ApplicationInsightsMiddleware : OwinMiddleware
{
    // You may create a new TelemetryConfiguration instance, reuse one you already have,
    // or fetch the instance created by Application Insights SDK.
    private readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
    private readonly TelemetryClient telemetryClient = new TelemetryClient(telemetryConfiguration);
    
    public ApplicationInsightsMiddleware(OwinMiddleware next) : base(next) {}

    public override async Task Invoke(IOwinContext context)
    {
        // Let's create and start RequestTelemetry.
        var requestTelemetry = new RequestTelemetry
        {
            Name = $"{context.Request.Method} {context.Request.Uri.GetLeftPart(UriPartial.Path)}"
        };

        // If there is a Request-Id received from the upstream service, set the telemetry context accordingly.
        if (context.Request.Headers.ContainsKey("Request-Id"))
        {
            var requestId = context.Request.Headers.Get("Request-Id");
            // Get the operation ID from the Request-Id (if you follow the HTTP Protocol for Correlation).
            requestTelemetry.Context.Operation.Id = GetOperationId(requestId);
            requestTelemetry.Context.Operation.ParentId = requestId;
        }

        // StartOperation is a helper method that allows correlation of 
        // current operations with nested operations/telemetry
        // and initializes start time and duration on telemetry items.
        var operation = telemetryClient.StartOperation(requestTelemetry);

        // Process the request.
        try
        {
            await Next.Invoke(context);
        }
        catch (Exception e)
        {
            requestTelemetry.Success = false;
            requestTelemetry.ResponseCode;
            telemetryClient.TrackException(e);
            throw;
        }
        finally
        {
            // Update status code and success as appropriate.
            if (context.Response != null)
            {
                requestTelemetry.ResponseCode = context.Response.StatusCode.ToString();
                requestTelemetry.Success = context.Response.StatusCode >= 200 && context.Response.StatusCode <= 299;
            }
            else
            {
                requestTelemetry.Success = false;
            }

            // Now it's time to stop the operation (and track telemetry).
            telemetryClient.StopOperation(operation);
        }
    }
    
    public static string GetOperationId(string id)
    {
        // Returns the root ID from the '|' to the first '.' if any.
        int rootEnd = id.IndexOf('.');
        if (rootEnd < 0)
            rootEnd = id.Length;

        int rootStart = id[0] == '|' ? 1 : 0;
        return id.Substring(rootStart, rootEnd - rootStart);
    }
}

相互關聯的 HTTP 通訊協定也會宣告 Correlation-Context 標頭。 為了簡單起見,這裡省略了。

佇列檢測

相互關聯的 W3C 追蹤內容和 HTTP 通訊協定會與 HTTP 要求傳遞相互關聯詳細數據,但每個佇列通訊協議必須定義如何沿著佇列訊息傳遞相同的詳細數據。 某些佇列通訊協定,例如AMQP,允許傳遞更多元數據。 其他通訊協定,例如 Azure 儲存體 佇列,需要將內容編碼成訊息承載。

注意

佇列尚不支援跨元件追蹤。

使用 HTTP 時,如果您的生產者和取用者將遙測傳送至不同的 Application Insights 資源,交易診斷體驗和應用程式對應會顯示交易,並端對端對應。 在佇列的情況下,尚不支援此功能。

服務匯流排佇列

如需追蹤資訊,請參閱透過 Azure 服務匯流排 傳訊分散式追蹤和相互關聯。

Azure 儲存體 佇列

下列範例示範如何追蹤 Azure 儲存體 佇列作業,並將產生者、取用者和 Azure 儲存體 之間的遙測相互關聯。

儲存體 佇列具有 HTTP API。 針對 HTTP 要求,Application Insights 相依性收集器會追蹤佇列的所有呼叫。 根據預設,它會在 ASP.NET 和 ASP.NET Core 應用程式上設定。 使用其他類型的應用程式,請參閱 主控台應用程式檔

您也可以將 Application Insights 作業標識碼與 儲存體 要求標識元相互關聯。 如需如何設定和取得 儲存體 要求客戶端和伺服器要求標識符的資訊,請參閱監視、診斷和疑難解答 Azure 儲存體

加入佇列

由於 儲存體 佇列支援 HTTP API,因此 Application Insights 會自動追蹤具有佇列的所有作業。 在許多情況下,此檢測應該就足夠了。 若要將取用者端的追蹤與產生者追蹤相互關聯,您必須傳遞一些相互關聯內容,類似於我們在 HTTP 通訊協定中執行相互關聯的方式。

此範例示範如何追蹤 Enqueue 作業。 您可以:

  • 相互關聯重試(如果有的話):它們都有一個常見的父代,也就是 Enqueue 作業。 否則,系統會將他們追蹤為傳入要求的子系。 如果佇列有多個邏輯要求,可能很難找到導致重試的呼叫。
  • 將 儲存體 記錄相互關聯(如果需要的話):它們與 Application Insights 遙測相互關聯。

作業 Enqueue 是父作業的子系。 例如傳入的 HTTP 要求。 HTTP 相依性呼叫是作業的 Enqueue 子系和傳入要求的子系。

public async Task Enqueue(CloudQueue queue, string message)
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>("enqueue " + queue.Name);
    operation.Telemetry.Type = "Azure queue";
    operation.Telemetry.Data = "Enqueue " + queue.Name;

    // MessagePayload represents your custom message and also serializes correlation identifiers into payload.
    // For example, if you choose to pass payload serialized to JSON, it might look like
    // {'RootId' : 'some-id', 'ParentId' : '|some-id.1.2.3.', 'message' : 'your message to process'}
    var jsonPayload = JsonConvert.SerializeObject(new MessagePayload
    {
        RootId = operation.Telemetry.Context.Operation.Id,
        ParentId = operation.Telemetry.Id,
        Payload = message
    });
    
    CloudQueueMessage queueMessage = new CloudQueueMessage(jsonPayload);

    // Add operation.Telemetry.Id to the OperationContext to correlate Storage logs and Application Insights telemetry.
    OperationContext context = new OperationContext { ClientRequestID = operation.Telemetry.Id};

    try
    {
        await queue.AddMessageAsync(queueMessage, null, null, new QueueRequestOptions(), context);
    }
    catch (StorageException e)
    {
        operation.Telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
        operation.Telemetry.Success = false;
        operation.Telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
        telemetryClient.TrackException(e);
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }
}  

若要減少應用程式報告的遙測數量,或如果您不想基於其他原因追蹤 Enqueue 作業,請直接使用 Activity API:

  • 建立 (並啟動) 新的 Activity ,而不是啟動 Application Insights 作業。 除了作業名稱之外,您不需要為其指派任何屬性。
  • 序列化 yourActivity.Id 為訊息承載, operation.Telemetry.Id而不是 。 您也可以使用 Activity.Current.Id

出列

類似於 Enqueue,Application Insights 會自動追蹤對 儲存體 佇列的實際 HTTP 要求。 作業 Enqueue 大概發生在父內容中,例如傳入要求內容。 Application Insights SDK 會自動將這類作業及其 HTTP 部分與相同範圍中報告的父要求和其他遙測相互關聯。

作業 Dequeue 很棘手。 Application Insights SDK 會自動追蹤 HTTP 要求。 但是,在剖析訊息之前,它並不知道相互關聯內容。 無法將 HTTP 要求與其餘遙測數據相互關聯,特別是收到多個訊息時。

public async Task<MessagePayload> Dequeue(CloudQueue queue)
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>("dequeue " + queue.Name);
    operation.Telemetry.Type = "Azure queue";
    operation.Telemetry.Data = "Dequeue " + queue.Name;
    
    try
    {
        var message = await queue.GetMessageAsync();
    }
    catch (StorageException e)
    {
        operation.telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
        operation.telemetry.Success = false;
        operation.telemetry.ResultCode = e.RequestInformation.HttpStatusCode.ToString();
        telemetryClient.TrackException(e);
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }

    return null;
}

處理

在下列範例中,以類似連入 HTTP 要求的方式追蹤傳入訊息:

public async Task Process(MessagePayload message)
{
    // After the message is dequeued from the queue, create RequestTelemetry to track its processing.
    RequestTelemetry requestTelemetry = new RequestTelemetry { Name = "process " + queueName };
    
    // It might also make sense to get the name from the message.
    requestTelemetry.Context.Operation.Id = message.RootId;
    requestTelemetry.Context.Operation.ParentId = message.ParentId;

    var operation = telemetryClient.StartOperation(requestTelemetry);

    try
    {
        await ProcessMessage();
    }
    catch (Exception e)
    {
        telemetryClient.TrackException(e);
        throw;
    }
    finally
    {
        // Update status code and success as appropriate.
        telemetryClient.StopOperation(operation);
    }
}

同樣地,也可以檢測其他佇列作業。 查看作業應該以與清除佇列作業類似的方式進行檢測。 不需要檢測佇列管理作業。 Application Insights 會追蹤 HTTP 之類的作業,而且在大部分情況下,它就足夠了。

當您檢測郵件刪除時,請確定您已設定作業(相互關聯)標識碼。 或者,您可以使用 Activity API。 然後,您不需要在遙測項目上設定作業標識符,因為 Application Insights SDK 會為您執行此動作:

  • 從佇列取得項目之後,請建立新的 Activity
  • 用來 Activity.SetParentId(message.ParentId) 將取用者和產生者記錄相互關聯。
  • Activity啟動 。
  • 使用 Start/StopOperation 協助程序追蹤清除佇列、處理和刪除作業。 請從相同的異步控制流程執行它(執行內容)。 如此一來,它們就會正確相互關聯。
  • 停止 Activity
  • 手動使用 Start/StopOperation 或呼叫 Track 遙測。

相依性類型

Application Insights 使用相依性類型來自定義 UI 體驗。 針對佇列,它會辨識下列可改善交易診斷體驗DependencyTelemetry型別:

  • Azure queue適用於 Azure 儲存體 佇列
  • Azure Event Hubsfor Azure 事件中樞
  • Azure Service Busfor Azure 服務匯流排

批次處理

透過某些佇列,您可以使用一個要求來清除多個訊息佇列。 處理這類訊息大概是獨立的,且屬於不同的邏輯作業。 無法將 Dequeue 作業與正在處理的特定訊息相互關聯。

每個訊息都應該在自己的異步控制流程中處理。 如需詳細資訊,請參閱 傳出相依性追蹤 一節。

長時間執行的背景工作

某些應用程式會啟動使用者要求所造成的長時間執行作業。 從追蹤/檢測的觀點來看,它與要求或相依性檢測不同:

async Task BackgroundTask()
{
    var operation = telemetryClient.StartOperation<DependencyTelemetry>(taskName);
    operation.Telemetry.Type = "Background";
    try
    {
        int progress = 0;
        while (progress < 100)
        {
            // Process the task.
            telemetryClient.TrackTrace($"done {progress++}%");
        }
        // Update status code and success as appropriate.
    }
    catch (Exception e)
    {
        telemetryClient.TrackException(e);
        // Update status code and success as appropriate.
        throw;
    }
    finally
    {
        telemetryClient.StopOperation(operation);
    }
}

在此範例中, telemetryClient.StartOperationDependencyTelemetry 建立並填入相互關聯內容。 假設您有由排程作業的連入要求所建立的父作業。 只要 BackgroundTask 在與連入要求相同的異步控制流程中啟動,它就會與該父作業相互關聯。 BackgroundTask 和所有巢狀遙測項目都會自動與造成它的要求相互關聯,即使在要求結束之後也一致。

當工作從背景線程開始時,沒有任何與它相關聯的作業 (Activity) 時, BackgroundTask 沒有任何父代。 不過,它可以有巢狀作業。 從工作報告的所有遙測項目都會與 DependencyTelemetryBackgroundTask建立的所有遙測專案相互關聯。

連出相依性追蹤

您可以追蹤自己的相依性種類或 Application Insights 不支援的作業。

Enqueue服務匯流排 佇列或 儲存體 佇列中的 方法可作為這類自定義追蹤的範例。

自訂相依性追蹤的一般方法是:

  • TelemetryClient.StartOperation 呼叫 (extension) 方法,以填滿DependencyTelemetry相互關聯所需的屬性,以及一些其他屬性,例如開始、時間戳和持續時間。
  • 在上 DependencyTelemetry設定其他自定義屬性,例如名稱和您需要的任何其他內容。
  • 進行相依性呼叫並等候。
  • 在作業完成時停止 StopOperation 作業。
  • 處理例外狀況。
public async Task RunMyTaskAsync()
{
    using (var operation = telemetryClient.StartOperation<DependencyTelemetry>("task 1"))
    {
        try 
        {
            var myTask = await StartMyTaskAsync();
            // Update status code and success as appropriate.
        }
        catch(...) 
        {
            // Update status code and success as appropriate.
        }
    }
}

處置作業會導致作業停止,因此您可以執行作業,而不是呼叫 StopOperation

警告

在某些情況下,未處理的例外狀況可能會 防止finally 呼叫,因此可能無法追蹤作業。

平行作業處理和追蹤

呼叫 StopOperation 只會停止已啟動的作業。 如果目前的執行作業不符合您想要停止的作業, StopOperation 則不會執行任何動作。 如果您在相同的執行內容中平行啟動多個作業,就可能發生這種情況。

var firstOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 1");
var firstTask = RunMyTaskAsync();

var secondOperation = telemetryClient.StartOperation<DependencyTelemetry>("task 2");
var secondTask = RunMyTaskAsync();

await firstTask;

// FAILURE!!! This will do nothing and will not report telemetry for the first operation
// as currently secondOperation is active.
telemetryClient.StopOperation(firstOperation); 

await secondTask;

請務必一律使用相同的異步方法呼叫StartOperation和處理作業,以隔離平行執行的作業。 如果作業是同步的(或不是異步),請使用 包裝程序並追蹤 Task.Run

public void RunMyTask(string name)
{
    using (var operation = telemetryClient.StartOperation<DependencyTelemetry>(name))
    {
        Process();
        // Update status code and success as appropriate.
    }
}

public async Task RunAllTasks()
{
    var task1 = Task.Run(() => RunMyTask("task 1"));
    var task2 = Task.Run(() => RunMyTask("task 2"));
    
    await Task.WhenAll(task1, task2);
}

ApplicationInsights 作業與 System.Diagnostics.Activity

System.Diagnostics.Activity 代表分散式追蹤內容,由架構和連結庫用來建立和傳播進程內外的內容,並相互關聯遙測專案。 Activity 與架構/連結庫之間的通知機制搭配 System.Diagnostics.DiagnosticSource 運作,以通知傳入或傳出要求和例外狀況等有趣事件。

活動是 Application Insights 中的一流公民。 自動相依性和要求集合會與事件一起 DiagnosticSource 嚴重依賴它們。 如果您在應用程式中建立 Activity ,則不會產生Application Insights 遙測建立。 Application Insights 必須接收 DiagnosticSource 事件,並知道要轉譯 Activity 為遙測的事件名稱和承載。

每個 Application Insights 作業(要求或相依性)都牽涉到 Activity。 呼叫 時 StartOperation ,它會在下方建立 ActivityStartOperation 是手動追蹤要求或相依性遙測的建議方式,並確保所有專案都相互關聯。

下一步