共用方式為


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

Application Insights SDK 會自動追蹤相依服務的連入 HTTP 要求和呼叫,例如 HTTP 要求和 SQL 查詢。 追蹤和相互關聯要求與相依性,可讓您深入了解橫跨所有微服務 (合併此應用程式) 的整體應用程式回應能力和可靠性。

有一類應用程式模式無法以一般方式支援。 適當監視這類模式時,需要進行手動程式碼檢測。 本文涵蓋可能需要手動檢測的一些模式,例如自訂佇列處理和執行長時間執行背景工作。

此文章提供有關如何使用 Application Insights SDK 追蹤自訂作業的指導方針。 本文件相關於:

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

警告

我們建議為新應用程式或客戶提供 Azure 監視器 Application InsightsAzure 監視器 OpenTelemetry 散發版本。 Azure 監視器 OpenTelemetry 散發版本提供與 Application Insights SDK 類似的功能和體驗。 您可以使用適用於 .NETNode.jsPython 的移轉指南,從 Application Insights SDK 移轉,但我們仍在努力新增更多功能以提供回溯相容性。

概觀

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

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

傳入作業追蹤

Application Insights Wb SDK 會針對在 IIS 管線中執行的 ASP.NET 應用程式和所有的 ASP.NET Core 應用程式,自動收集 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 儲存體及進行移難排解

加入佇列

因為 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);
    }
}

同樣地,可能會檢測其他佇列作業。 預覽 (Peek) 作業應以類似清除佇列作業的方式進行檢測。 不一定要檢測佇列管理作業。 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 儲存體佇列的 Azure queue
  • Azure 事件中樞的 Azure Event Hubs
  • Azure 服務匯流排的 Azure Service Bus

批次處理

有些佇列中,您可以使用一個要求清除佇列多個訊息。 處理這類訊息可能是獨立的,並且屬於不同的邏輯作業。 無法將 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.StartOperation 會建立 DependencyTelemetry 並填滿相互關聯內容。 假設您有一項父代作業,由排程作業的連入要求所建立。 只要 BackgroundTask 在與連入要求相同的非同步控制流程中啟動,它就會與該父代作業相互關聯。 BackgroundTask 和所有巢狀遙測項目將會自動與造成它的要求相互關聯,即使在要求結束後亦然。

從沒有任何相關聯作業 (Activity) 的背景執行緒啟動工作時,BackgroundTask 沒有任何父代。 不過,它可以有巢狀作業。 工作回報的所有遙測項目會與在 BackgroundTask 中建立的 DependencyTelemetry 相互關聯。

連出相依性追蹤

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

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

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

  • 呼叫 TelemetryClient.StartOperation (延伸模組) 方法,其能填滿相互關聯所需的 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 代表分散式追蹤內容,並由架構和程式庫用於建立和散佈程序內外的內容,並使遙測項目相互關聯。 System.Diagnostics.DiagnosticSource 能與 Activity 搭配使用,作為架構/程式庫之間的通知機制,以通知感興趣的事件,例如傳入或傳出要求及例外狀況。

活動是 Application Insights 中的第一級公民。 自動相依性和要求收集皆高度依賴活動及 DiagnosticSource 事件。 如果您在應用程式中建立 Activity,其並不會連帶建立 Application Insights 遙測。 Application Insights 必須接收 DiagnosticSource 事件,並知道事件名稱和承載,才能將 Activity 轉譯為遙測。

每個 Application Insights 作業 (要求或相依性) 皆涉及 Activity。 呼叫 StartOperation 時,其會在下方建立 Activity。 建議使用 StartOperation 方式來手動追蹤要求或相依性遙測,並確定所有內容都相互關聯。

下一步