File: Telemetry\TelemetryExtensions.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Evaluation.Console\Microsoft.Extensions.AI.Evaluation.Console.csproj (Microsoft.Extensions.AI.Evaluation.Console)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.AI.Evaluation.Console.Utilities;
using Microsoft.Extensions.AI.Evaluation.Utilities;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Text;
 
namespace Microsoft.Extensions.AI.Evaluation.Console.Telemetry;
 
internal static class TelemetryExtensions
{
    internal static string ToTelemetryPropertyValue(this bool value) =>
        value ? TelemetryConstants.PropertyValues.True : TelemetryConstants.PropertyValues.False;
 
    internal static string ToTelemetryPropertyValue(this int value) =>
        value.ToInvariantString();
 
    internal static string ToTelemetryPropertyValue(this long value) =>
        value.ToInvariantString();
 
    internal static string ToTelemetryPropertyValue(this string? value, string defaultValue) =>
        string.IsNullOrWhiteSpace(value) ? defaultValue : value;
 
    internal static void ReportOperation(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Action operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            TimeSpan duration = TimingHelper.ExecuteWithTiming(operation);
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
    internal static TResult ReportOperation<TResult>(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Func<TResult> operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            (TResult result, TimeSpan duration) = TimingHelper.ExecuteWithTiming(operation);
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
            return result;
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
#pragma warning disable EA0014 // The async method doesn't support cancellation.
    internal static async ValueTask ReportOperationAsync(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Func<Task> operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            TimeSpan duration = await TimingHelper.ExecuteWithTimingAsync(operation).ConfigureAwait(false);
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
    internal static async ValueTask ReportOperationAsync(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Func<ValueTask> operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            TimeSpan duration = await TimingHelper.ExecuteWithTimingAsync(operation).ConfigureAwait(false);
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
    internal static async ValueTask<TResult> ReportOperationAsync<TResult>(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Func<Task<TResult>> operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            (TResult result, TimeSpan duration) =
                await TimingHelper.ExecuteWithTimingAsync(operation).ConfigureAwait(false);
 
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
            return result;
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
    internal static async ValueTask<TResult> ReportOperationAsync<TResult>(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Func<ValueTask<TResult>> operation,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        try
        {
            (TResult result, TimeSpan duration) =
                await TimingHelper.ExecuteWithTimingAsync(operation).ConfigureAwait(false);
 
            telemetryHelper.ReportOperationSuccess(operationName, duration, properties, metrics, logger);
            return result;
        }
        catch (Exception ex)
        {
            telemetryHelper.ReportOperationFailure(operationName, ex, properties, metrics, logger);
            throw;
        }
    }
 
    private static void ReportOperationSuccess(
        this TelemetryHelper telemetryHelper,
        string operationName,
        TimeSpan duration,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        void Report()
        {
            string durationInMilliseconds = duration.ToMillisecondsText();
 
            properties ??= new Dictionary<string, string>();
            properties.Add(TelemetryConstants.PropertyNames.Success, TelemetryConstants.PropertyValues.True);
            properties.Add(TelemetryConstants.PropertyNames.DurationInMilliseconds, durationInMilliseconds);
 
            telemetryHelper.ReportEvent(eventName: operationName, properties, metrics);
        }
 
        if (logger is null)
        {
            try
            {
                Report();
            }
            catch
            {
                // Ignore exceptions encountered when trying to report telemetry.
            }
        }
        else
        {
            // Log and ignore exceptions encountered when trying to report telemetry.
            logger.ExecuteWithCatch(Report, swallowUnhandledExceptions: true);
        }
    }
 
    private static void ReportOperationFailure(
        this TelemetryHelper telemetryHelper,
        string operationName,
        Exception exception,
        IDictionary<string, string>? properties = null,
        IDictionary<string, double>? metrics = null,
        ILogger? logger = null)
    {
        void Report()
        {
            properties ??= new Dictionary<string, string>();
            properties.Add(TelemetryConstants.PropertyNames.Success, TelemetryConstants.PropertyValues.False);
 
            telemetryHelper.ReportEvent(eventName: operationName, properties, metrics);
            telemetryHelper.ReportException(exception, properties, metrics);
        }
 
        if (logger is null)
        {
            try
            {
                Report();
            }
            catch
            {
                // Ignore exceptions encountered when trying to report telemetry.
            }
        }
        else
        {
            // Log and ignore exceptions encountered when trying to report telemetry.
            logger.ExecuteWithCatch(Report, swallowUnhandledExceptions: true);
        }
    }
#pragma warning restore EA0014
}