File: src\Shared\OtlpEndpointResolver.cs
Web Access
Project: src\src\Aspire.Hosting.Maui\Aspire.Hosting.Maui.csproj (Aspire.Hosting.Maui)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Extensions.Configuration;
 
namespace Aspire.Hosting;
 
/// <summary>
/// Resolves OTLP endpoint configuration (URL, scheme, port, and protocol) from configuration.
/// </summary>
internal static class OtlpEndpointResolver
{
    private const int DashboardOtlpUrlDefaultPort = 18889;
    private static readonly string s_dashboardOtlpUrlDefaultValue = $"http://localhost:{DashboardOtlpUrlDefaultPort}";
 
    /// <summary>
    /// Resolves the OTLP endpoint URL and protocol from configuration.
    /// </summary>
    /// <param name="configuration">The configuration to read from.</param>
    /// <param name="requiredProtocol">The required protocol, or null to use preference logic (gRPC preferred over HTTP).</param>
    /// <returns>A tuple containing the endpoint URL and protocol string ("grpc" or "http/protobuf").</returns>
    /// <exception cref="InvalidOperationException">Thrown when <paramref name="requiredProtocol"/> requires HTTP but no HTTP endpoint is configured.</exception>
    public static (string Url, string Protocol) ResolveOtlpEndpoint(IConfiguration configuration, OtlpProtocol? requiredProtocol = null)
    {
        var dashboardOtlpGrpcUrl = configuration.GetString(KnownConfigNames.DashboardOtlpGrpcEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpGrpcEndpointUrl);
        var dashboardOtlpHttpUrl = configuration.GetString(KnownConfigNames.DashboardOtlpHttpEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpHttpEndpointUrl);
 
        // Check if a specific protocol is required
        if (requiredProtocol is OtlpProtocol.Grpc)
        {
            return (dashboardOtlpGrpcUrl ?? s_dashboardOtlpUrlDefaultValue, "grpc");
        }
        else if (requiredProtocol is OtlpProtocol.HttpProtobuf)
        {
            if (dashboardOtlpHttpUrl is null)
            {
                throw new InvalidOperationException("OtlpExporter is configured to require http/protobuf, but no endpoint was configured for ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL");
            }
            return (dashboardOtlpHttpUrl, "http/protobuf");
        }
        else
        {
            // No specific protocol required, use the existing preference logic
            // The dashboard can support OTLP/gRPC and OTLP/HTTP endpoints at the same time, but it can
            // only tell resources about one of the endpoints via environment variables.
            // If both OTLP/gRPC and OTLP/HTTP are available then prefer gRPC.
            if (dashboardOtlpGrpcUrl is not null)
            {
                return (dashboardOtlpGrpcUrl, "grpc");
            }
            else if (dashboardOtlpHttpUrl is not null)
            {
                return (dashboardOtlpHttpUrl, "http/protobuf");
            }
            else
            {
                // No endpoints provided to host. Use default value for URL.
                return (s_dashboardOtlpUrlDefaultValue, "grpc");
            }
        }
    }
 
    /// <summary>
    /// Resolves the OTLP endpoint scheme and port from configuration.
    /// </summary>
    /// <param name="configuration">The configuration to read from.</param>
    /// <returns>A tuple of (scheme, port) for the OTLP endpoint.</returns>
    public static (string Scheme, int Port) ResolveSchemeAndPort(IConfiguration configuration)
    {
        var (url, _) = ResolveOtlpEndpoint(configuration);
 
        if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
        {
            return (uri.Scheme, uri.Port);
        }
 
        // Fallback to default (should not normally reach here as ResolveOtlpEndpoint always returns a valid URL)
        return ("http", DashboardOtlpUrlDefaultPort);
    }
}