File: Extensions.cs
Web Access
Project: src\playground\BlazorStandalone\BlazorStandalone.ClientServiceDefaults\BlazorStandalone.ClientServiceDefaults.csproj (BlazorStandalone.ClientServiceDefaults)
using Microsoft.AspNetCore.Components.Infrastructure;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
 
namespace Microsoft.Extensions.Hosting;
 
public static class BlazorClientExtensions
{
    public static WebAssemblyHostBuilder AddBlazorClientServiceDefaults(this WebAssemblyHostBuilder builder)
    {
        ComponentsMetricsServiceCollectionExtensions.AddComponentsMetrics(builder.Services);
        ComponentsMetricsServiceCollectionExtensions.AddComponentsTracing(builder.Services);
 
        builder.ConfigureBlazorClientOpenTelemetry();
 
        builder.Services.AddServiceDiscovery();
 
        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            http.AddServiceDiscovery();
        });
 
        return builder;
    }
 
    private static WebAssemblyHostBuilder ConfigureBlazorClientOpenTelemetry(this WebAssemblyHostBuilder builder)
    {
        // Without an OTLP path base, there's nowhere to export telemetry in WASM.
        var otlpPathBase = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
        if (string.IsNullOrEmpty(otlpPathBase))
        {
            return builder;
        }
 
        var serviceName = builder.Configuration["OTEL_SERVICE_NAME"]!;
 
        // Resolve the OTLP path against the page's origin so telemetry goes through
        // the same origin the user navigated to, avoiding cross-origin issues.
        var baseAddress = new Uri(builder.HostEnvironment.BaseAddress);
        var otlpEndpoint = new Uri(baseAddress, $"{otlpPathBase}/");
 
        builder.Services.AddOpenTelemetry()
            // Use a fixed instanceId so all browser tabs report as a single service instance
            // in the dashboard rather than spawning separate entries per tab.
            .ConfigureResource(r => r.AddService(serviceName, serviceInstanceId: serviceName))
            .WithLogging(logging =>
            {
                logging.AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint, "v1/logs"));
            }, options =>
            {
                options.IncludeFormattedMessage = true;
                options.IncludeScopes = true;
            })
            .WithMetrics(metrics =>
            {
                metrics.AddMeter("Microsoft.AspNetCore.Components");
                metrics.AddMeter("Microsoft.AspNetCore.Components.Lifecycle");
                metrics.AddHttpClientInstrumentation();
                metrics.AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint, "v1/metrics"));
            })
            .WithTracing(tracing =>
            {
                tracing.AddSource("Microsoft.AspNetCore.Components")
                    .AddHttpClientInstrumentation();
                tracing.AddOtlpExporter(o => o.Endpoint = new Uri(otlpEndpoint, "v1/traces"));
            });
 
        return builder;
    }
}