Sanjay Kumar
Nov 19, 2025
  649
(3 votes)

Operational observability using application insights to trace checkout end to end

You can’t fix what you can’t see. In a modern, distributed e‑commerce system, the checkout flow touches multiple services from the front-end UI to backend APIs, payment gateways, and inventory systems. Failures, performance issues, or unexpected latencies often hide in the gaps between these components.

This is where operational observability becomes critical. By leveraging Azure Application Insights together with your existing structured logging, you can achieve actionable, end-to-end visibility into every checkout transaction.

What to trace

  • Page/controller actions: enter/exit, validation failures.
  • Payment orchestration: gateway called, result, latency, timeouts.
  • Business events: cart created, booking info saved, order created.
  • Correlation keys: cartReference, paymentMethod, user/device attributes.

Let's Configure and Code

  • Application Insights is configured:
ApplicationInsights":{
  "ConnectionString": "InstrumentationKey={my_key};IngestionEndpoint= {applicationinsights_endpoint}"
}
  • Checkout logs key breadcrumbs with a cart reference (great for correlation)
    Note: In this implementation, cartReference is used as a meta‑key to identify and track each cart / checkout session across telemetry. You are free to replace it with any custom key / value that suits your domain model (e.g., orderId, sessionId, checkoutId).

Entry:

_fluentLogger
    .Data(("CartReference", cart.GetCartReference()),
          ("BookingInfo", model.BookingInfo))
    .LogInfo("Booking calendar");
return View(GetViewPath(currentPage), model);

Exit:

_fluentLogger
    .Data((nameof(checkoutBookingInfo), checkoutBookingInfo),
          ("CartReference", cart.GetCartReference()))
    .LogInfo("Booking POST");

Add rich telemetry with correlation

Use cartReference as your primary correlation property across logs, traces, and metrics. If you’re already using ILogger (or Serilog) with AI export, enrich the scope; otherwise, send custom events via TelemetryClient.New code (example pattern):

using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

public class CheckoutTelemetryService
{
    private readonly TelemetryClient _telemetryClient;

    public CheckoutTelemetryService(TelemetryClient telemetryClient)
    {
        _telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
    }

    /// <summary>
    /// Tracks a specific step in the checkout process.
    /// </summary>
    /// <param name="stepName">Name of the checkout step (e.g., "cartValidation").</param>
    /// <param name="cartReference">Unique identifier for the cart.</param>
    /// <param name="properties">Optional custom properties.</param>
    /// <param name="durationMs">Optional duration of the step in milliseconds.</param>
    public void TrackCheckoutStep(
        string stepName, 
        string cartReference, 
        IDictionary<string, string>? properties = null, 
        double? durationMs = null)
    {
        var telemetryEvent = new EventTelemetry($"checkout_{stepName}");
        telemetryEvent.Properties["cartReference"] = cartReference;

        if (properties != null)
        {
            foreach (var kvp in properties)
            {
                telemetryEvent.Properties[kvp.Key] = kvp.Value;
            }
        }

        if (durationMs.HasValue)
        {
            telemetryEvent.Metrics["duration_ms"] = durationMs.Value;
        }

        _telemetryClient.TrackEvent(telemetryEvent);
    }

    /// <summary>
    /// Starts a dependency telemetry operation, such as payment or inventory check.
    /// </summary>
    /// <param name="dependencyName">Name of the dependency operation.</param>
    /// <param name="cartReference">Cart reference ID.</param>
    /// <param name="dependency">Outputs the created DependencyTelemetry object.</param>
    /// <returns>IDisposable to stop the operation when disposed.</returns>
    public IDisposable StartDependencyOperation(
        string dependencyName, 
        string cartReference, 
        out DependencyTelemetry dependency)
    {
        dependency = new DependencyTelemetry("checkoutDependency", dependencyName, DateTimeOffset.UtcNow, TimeSpan.Zero, success: true);
        dependency.Properties["cartReference"] = cartReference;

        return new TelemetryOperation(_telemetryClient, dependency);
    }

    /// <summary>
    /// Helper class to manage the lifetime of telemetry operations.
    /// </summary>
    private sealed class TelemetryOperation : IDisposable
    {
        private readonly TelemetryClient _telemetryClient;
        private readonly IOperationHolder<DependencyTelemetry> _operationHolder;

        public TelemetryOperation(TelemetryClient telemetryClient, DependencyTelemetry telemetry)
        {
            _telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
            _operationHolder = _telemetryClient.StartOperation(telemetry);
        }

        public void Dispose()
        {
            _telemetryClient.StopOperation(_operationHolder);
        }
    }
}

Use it in checkout/payment paths:

var cartRef = cart.GetCartReference();
_checkoutTelemetry.TrackCheckoutStep("shipping_viewed", cartRef, new Dictionary<string,string> {
    ["device"] = Request.Headers["User-Agent"].ToString().Contains("Mobile") ? "mobile" : "desktop"
});

Around a payment call

using (_checkoutTelemetry.StartDependencyOperation("bank_charge", cartRef, out var dep))
{
    var sw = Stopwatch.StartNew();
    var result = await _paymentService.ProcessBankCheckoutAsync(chargeId, transactionId, cartRef);
    sw.Stop();
    dep.Duration = sw.Elapsed;
    dep.Success = result.Success;
    dep.Properties["paymentMethod"] = "CreditCard";
    dep.Properties["timeout"] = result.Timeout.ToString();
    _checkoutTelemetry.TrackStep("payment_processed", cartRef, new Dictionary<string,string>{
        ["paymentMethod"] = "CreditCard",
        ["errorType"] = result.ErrorType.ToString()
    }, sw.Elapsed.TotalMilliseconds);
}

 

Kusto queries (copy‑paste in Logs) examples:  

  • End‑to‑end for a single cart:
union traces, customEvents, dependencies, requests
| extend cartReference = tostring(customDimensions.cartReference)
| where isnotempty(cartReference) and cartReference == "<CART_REF>"
| project timestamp, itemType = itemType, name, message, resultCode, success, duration, cartReference, customDimensions
| order by timestamp asc
  • Payment gateway health:
dependencies
| where type == "payment"
| summarize count(), failures = countif(success == false), p95=percentile(duration,95) by name, bin(timestamp, 5m)
| extend failureRate = todouble(failures) / todouble(count())
  • Checkout bottlenecks by step:
customEvents
| where name startswith "checkout_"
| summarize p95=percentile(todouble(tostring(customMeasurements["duration_ms"])),95)
          , count() by name
| order by p95 desc

Guardrails

  • Don’t log PII, whitelist properties. Treat cartReference as non‑PII.
  • Sample high‑volume events if needed; never drop error paths.
  • Secure the AI connection string via secrets/Key Vault; avoid plain text in config.

Outcomes

  • Fast root cause: correlate a customer’s failed payment through controller, dependency, and custom events.
  • Trend insights: detect gateway degradation and timeouts before conversion drops.
  • Measurable improvements: reducing MTTR (Mean Time To Resolve/Recover) and payment‑related abandonment with targeted fixes.



Nov 19, 2025

Comments

Please login to comment.
Latest blogs
From Prompting to Production: Optimizely Opal University Cohort and the Future of Agentic MarTech

Most organizations today are still playing with AI. They experiment with prompts, test ideas in isolated chats, and occasionally automate a task or...

Augusto Davalos | Apr 28, 2026

Six Compelling Reasons for Upgrading to CMS 13

Most software updates ask you to keep up. Optimizely CMS 13 asks something different — it asks whether your digital strategy is built for a world...

Muhammad Talha | Apr 28, 2026

Optimizely CMS 13 breaking changes: GetContentTypePropertyDisplayName

When upgrading from CMS 12 to 13, resolving property display names may not work as before. Here’s what changed.

Tomas Hensrud Gulla | Apr 27, 2026 |

Accelerate Optimizely DAM Adoption: Unlocking Business Value with Metadata Bulk Import

Accelerating Optimizely DAM Adoption How a Metadata-Driven Bulk Import Utility Unlocks Real Business Value Executive Summary For enterprises runnin...

Vaibhav | Apr 27, 2026