-
Notifications
You must be signed in to change notification settings - Fork 60
Add Blazor WASM telemetry docs and post-deployment observability guidance #455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Copilot
wants to merge
7
commits into
main
Choose a base branch
from
copilot/add-telemetry-dashboard-documentation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+355
−0
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
28889dc
Initial plan
Copilot 23efef3
Add Blazor WASM telemetry section and post-deployment telemetry docs
Copilot a88f5f8
Fix doc-tester issues: remove fictional API, fix package attribution,…
Copilot a44b510
Apply suggestions from code review
IEvangelist 2fdf287
Apply @alistairmatthews review suggestions: wording, proxy fix, and p…
Copilot 3aa68ca
Specified trivy.v0.35.0 to resolve security scan check failure.
alistairmatthews aa6e085
Resolved conflict by removing security-scan.yml.
alistairmatthews File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -306,7 +306,150 @@ The bundling and minification of the JavaScript code is beyond the scope of this | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| For the complete working example of how to configure the JavaScript OTEL SDK to send telemetry to the dashboard, see the [browser telemetry sample](https://github.com/microsoft/aspire/tree/main/playground/BrowserTelemetry). | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Blazor WebAssembly integration | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Blazor WebAssembly (WASM) apps run entirely in the browser using a .NET runtime compiled to WebAssembly. Like other browser apps, Blazor WASM apps use the [JavaScript OTEL SDK](https://opentelemetry.io/docs/languages/js/getting-started/browser/) to send telemetry to the Aspire dashboard via JavaScript interop. The dashboard configuration for [OTLP HTTP](#otlp-configuration) and [CORS](#cors-configuration) described earlier in this article applies to Blazor WASM apps as well. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Provide OTEL configuration to the browser | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Blazor WASM apps can't read server-side environment variables directly. When the app is hosted by an ASP.NET Core server (for example, a Blazor Web App or hosted Blazor WASM project), expose the OTEL configuration through an API endpoint on the server: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```csharp title="C# — Program.cs (Server)" | ||||||||||||||||||||||||||
| using Microsoft.AspNetCore.Authorization; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| app.MapGet("/api/telemetry-config", [Authorize] () => new | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| Endpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ?? string.Empty, | ||||||||||||||||||||||||||
| Headers = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS") ?? string.Empty, | ||||||||||||||||||||||||||
| ResourceAttributes = Environment.GetEnvironmentVariable("OTEL_RESOURCE_ATTRIBUTES") ?? string.Empty | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
Comment on lines
+320
to
+325
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| <Aside type="caution"> | ||||||||||||||||||||||||||
| This approach exposes the OTLP API key to the browser in the HTTP response. For environments where the API key must remain secret, use an [authenticated OTEL proxy](#authenticated-otel-proxy) instead. In production, consider adding authentication or authorization to restrict access to this endpoint. | ||||||||||||||||||||||||||
| </Aside> | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Initialize OTEL from a Blazor component | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| In the Blazor WASM app, call the JavaScript `initializeTelemetry` function using `IJSRuntime` after the component has rendered. Add the [JavaScript OTEL SDK initialization code](#example-browser-telemetry-code) to a JavaScript file bundled with your app, then call it from a Razor component: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```razor title="Razor — TelemetryInitializer.razor (Client)" | ||||||||||||||||||||||||||
| @inject IJSRuntime JS | ||||||||||||||||||||||||||
| @inject HttpClient Http | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @code { | ||||||||||||||||||||||||||
| protected override async Task OnAfterRenderAsync(bool firstRender) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| if (firstRender) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| var config = await Http.GetFromJsonAsync<TelemetryConfig>("/api/telemetry-config"); | ||||||||||||||||||||||||||
| if (config is { Endpoint.Length: > 0 }) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| await JS.InvokeVoidAsync( | ||||||||||||||||||||||||||
| "initializeTelemetry", | ||||||||||||||||||||||||||
| config.Endpoint, | ||||||||||||||||||||||||||
| config.Headers, | ||||||||||||||||||||||||||
| config.ResourceAttributes); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| private sealed record TelemetryConfig( | ||||||||||||||||||||||||||
| string Endpoint, | ||||||||||||||||||||||||||
| string Headers, | ||||||||||||||||||||||||||
| string ResourceAttributes); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Include this component in your `App.razor` or another top-level component so that telemetry is initialized when the app loads. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Backend-to-frontend trace correlation | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| To correlate browser spans with server-side traces, include the current trace context in the server-rendered HTML. When the Blazor WASM app is hosted within a server-rendered page, such as a Blazor Web App with prerendering, the server can write the `traceparent` meta tag during prerender: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```razor title="Razor — App.razor (Server prerender)" | ||||||||||||||||||||||||||
| @using System.Diagnostics | ||||||||||||||||||||||||||
| <!DOCTYPE html> | ||||||||||||||||||||||||||
| <html lang="en"> | ||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||
| @if (Activity.Current is { } currentActivity) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| <meta name="traceparent" content="@currentActivity.Id" /> | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| <!-- Other elements omitted for brevity... --> | ||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| The JavaScript OTEL SDK reads this `traceparent` value automatically when `DocumentLoadInstrumentation` is registered, linking the browser spans to the originating server trace. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ### Authenticated OTEL proxy | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| When the Aspire dashboard's OTLP API key must not be exposed to the browser, route telemetry through a server-side proxy endpoint. The browser sends telemetry to the proxy, and the proxy forwards it to the dashboard with the API key included as a server-side secret: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```csharp title="C# — Program.cs (Server proxy endpoint)" | ||||||||||||||||||||||||||
| // Register an HttpClient for the OTEL proxy | ||||||||||||||||||||||||||
| builder.Services.AddHttpClient("otel-proxy"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // ... | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| app.MapPost("/api/telemetry/{**path}", async ( | ||||||||||||||||||||||||||
| string path, | ||||||||||||||||||||||||||
| HttpContext context, | ||||||||||||||||||||||||||
| IHttpClientFactory httpClientFactory) => | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| var dashboardEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"); | ||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(dashboardEndpoint)) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| return Results.NotFound(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| var client = httpClientFactory.CreateClient("otel-proxy"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| using var requestBody = new StreamContent(context.Request.Body); | ||||||||||||||||||||||||||
| requestBody.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue | ||||||||||||||||||||||||||
| .Parse(context.Request.ContentType ?? "application/x-protobuf"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| using var request = new HttpRequestMessage( | ||||||||||||||||||||||||||
| HttpMethod.Post, | ||||||||||||||||||||||||||
| $"{dashboardEndpoint.TrimEnd('/')}/{path}") | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| Content = requestBody | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Copy OTLP API key from server environment to the forwarded request | ||||||||||||||||||||||||||
| var headersEnv = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS") ?? string.Empty; | ||||||||||||||||||||||||||
| foreach (var header in headersEnv.Split(',', StringSplitOptions.RemoveEmptyEntries)) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| var parts = header.Split('=', 2); | ||||||||||||||||||||||||||
| if (parts.Length == 2) | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| request.Headers.TryAddWithoutValidation(parts[0].Trim(), parts[1].Trim()); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
IEvangelist marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| var response = await client.SendAsync(request); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| context.Response.StatusCode = (int)response.StatusCode; | ||||||||||||||||||||||||||
| await response.Content.CopyToAsync(context.Response.Body); | ||||||||||||||||||||||||||
| return Results.Empty; | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Configure the JavaScript OTEL SDK in the Blazor WASM app to point to the proxy endpoint instead of the dashboard directly: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```javascript title="JavaScript — telemetry.js (Client)" | ||||||||||||||||||||||||||
| export function initializeTelemetry(resourceAttributes) { | ||||||||||||||||||||||||||
| const otlpOptions = { | ||||||||||||||||||||||||||
| url: '/api/telemetry/v1/traces' // Proxy endpoint, same origin - no CORS needed | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| // ... rest of SDK initialization | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| This pattern eliminates the need for CORS configuration on the dashboard because the browser communicates only with the same-origin server. The API key stays on the server and is never visible to the browser. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## See also | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - [Aspire dashboard configuration](/dashboard/configuration/) | ||||||||||||||||||||||||||
| - [Standalone Aspire dashboard](/dashboard/standalone/) | ||||||||||||||||||||||||||
| - [Telemetry after deployment](/dashboard/telemetry-after-deployment/) | ||||||||||||||||||||||||||
189 changes: 189 additions & 0 deletions
189
src/frontend/src/content/docs/dashboard/telemetry-after-deployment.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,189 @@ | ||||||
| --- | ||||||
| title: Telemetry after deployment | ||||||
| description: Understand how telemetry and the Aspire dashboard work after you deploy your app, and how to configure production-grade observability. | ||||||
| --- | ||||||
|
|
||||||
| import { Aside, Steps } from '@astrojs/starlight/components'; | ||||||
| import LearnMore from '@components/LearnMore.astro'; | ||||||
|
|
||||||
| The Aspire dashboard is designed for local development and short-term diagnostics. It stores telemetry in memory, which means telemetry is lost when the dashboard restarts and there are built-in limits on how much data it retains. After deploying your app to a production environment, you need to configure a persistent telemetry backend. | ||||||
|
|
||||||
| This article explains what changes when you deploy your app, how to configure production telemetry with Azure Monitor, and how to access the Aspire dashboard if it's included in your deployment. | ||||||
|
|
||||||
| ## Development versus production telemetry | ||||||
|
|
||||||
| During development, Aspire automatically starts the dashboard and configures your app's OTEL environment variables to send telemetry to it. This works well for local diagnostics but is not suitable for production for the following reasons: | ||||||
|
|
||||||
| | | Aspire dashboard | Production telemetry backend | | ||||||
| |---|---|---| | ||||||
| | **Storage** | In-memory only | Persistent (database, cloud service) | | ||||||
| | **Retention** | Lost on restart | Configurable (days, months, indefinitely) | | ||||||
| | **Telemetry limits** | Default 10,000 log entries, 10,000 traces | Configurable or unlimited | | ||||||
| | **Access** | Local or private | Secured, multi-user | | ||||||
| | **Alerting** | None | Configurable alerts and dashboards | | ||||||
|
|
||||||
| After deploying, configure your app to send telemetry to a persistent backend. Azure Monitor with Application Insights is the recommended production telemetry solution for Azure-hosted apps. | ||||||
|
|
||||||
| ## Configure Azure Monitor for production telemetry | ||||||
|
|
||||||
| [Azure Monitor](https://learn.microsoft.com/azure/azure-monitor/overview) collects, analyzes, and responds to telemetry data from your cloud applications. Aspire has built-in support for Azure Application Insights, which is the Azure Monitor feature for application telemetry. | ||||||
|
|
||||||
| ### Add Application Insights to your AppHost | ||||||
|
|
||||||
| Add the Application Insights resource to your AppHost project: | ||||||
|
|
||||||
| ```csharp title="C# — AppHost.cs" | ||||||
| var builder = DistributedApplication.CreateBuilder(args); | ||||||
|
|
||||||
| var insights = builder.AddAzureApplicationInsights("app-insights"); | ||||||
|
|
||||||
| builder.AddProject<Projects.MyApp_ApiService>("apiservice") | ||||||
| .WithReference(insights); | ||||||
|
|
||||||
| builder.AddProject<Projects.MyApp_Web>("webfrontend") | ||||||
| .WithReference(insights) | ||||||
| .WithExternalHttpEndpoints(); | ||||||
|
|
||||||
| builder.Build().Run(); | ||||||
| ``` | ||||||
|
|
||||||
| When you reference Application Insights, Aspire automatically configures the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable for each service. The `Azure.Monitor.OpenTelemetry.AspNetCore` package uses the `UseAzureMonitor()` method to read this variable and send telemetry to Application Insights. | ||||||
|
|
||||||
| <Aside type="note"> | ||||||
| The Application Insights resource provisions an Azure Application Insights component. Provisioning requires an active Azure subscription. When running locally, telemetry is sent to the Aspire dashboard as usual unless a real connection string is present. | ||||||
| </Aside> | ||||||
|
|
||||||
| ### Use OpenTelemetry with Azure Monitor | ||||||
|
|
||||||
| Add the [📦 Azure.Monitor.OpenTelemetry.AspNetCore](https://www.nuget.org/packages/Azure.Monitor.OpenTelemetry.AspNetCore) package to each service project to enable OTEL-based export to Azure Monitor: | ||||||
|
|
||||||
| ```csharp title="C# — Program.cs (service)" | ||||||
| var builder = WebApplication.CreateBuilder(args); | ||||||
|
|
||||||
| builder.AddServiceDefaults(); | ||||||
|
|
||||||
| // Add Azure Monitor OTEL export when connection string is available | ||||||
| builder.Services.AddOpenTelemetry() | ||||||
| .UseAzureMonitor(); | ||||||
| ``` | ||||||
|
|
||||||
| Aspire's service defaults already configure OpenTelemetry. The `UseAzureMonitor()` call adds Azure Monitor as an additional exporter. When the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable is set, which Aspire does automatically when you reference the resource, telemetry flows to Azure Monitor. | ||||||
|
|
||||||
| <LearnMore> | ||||||
| For more information, see [Azure Monitor OpenTelemetry documentation](https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable). | ||||||
| </LearnMore> | ||||||
|
|
||||||
| ## Troubleshoot missing logs after deployment | ||||||
|
|
||||||
| If some or all logs don't appear in your telemetry backend after deployment, check the following common causes. | ||||||
|
|
||||||
| ### OTEL environment variables not set | ||||||
|
|
||||||
| Verify that the OTEL environment variables are correctly set in your deployed containers. When deploying to Azure Container Apps, Aspire sets these variables automatically when you use `WithReference` for Application Insights or when you configure the OTEL endpoint. | ||||||
|
|
||||||
| Check for these environment variables in your deployed container: | ||||||
|
|
||||||
| - `OTEL_EXPORTER_OTLP_ENDPOINT`: The OTLP endpoint receiving telemetry. | ||||||
| - `OTEL_EXPORTER_OTLP_HEADERS`: Headers including the API key, if required. | ||||||
| - `APPLICATIONINSIGHTS_CONNECTION_STRING`: Application Insights connection string, when using Azure Monitor. | ||||||
|
|
||||||
| For Azure Container Apps deployments, verify variables in the [Azure Portal](https://portal.azure.com) by navigating to your Container app → **Containers** → **Environment variables**. | ||||||
|
|
||||||
| ### Telemetry not exported from the app | ||||||
|
|
||||||
| Verify that your app is configured to export telemetry. All service projects should call `AddServiceDefaults()` in their `Program.cs`, which sets up OpenTelemetry: | ||||||
|
|
||||||
| ```csharp title="C# — Program.cs" | ||||||
| var builder = WebApplication.CreateBuilder(args); | ||||||
|
|
||||||
| // This configures OpenTelemetry with logging, metrics, and tracing | ||||||
| builder.AddServiceDefaults(); | ||||||
| ``` | ||||||
|
|
||||||
| If you're not using Aspire service defaults, ensure your app is configured with the OpenTelemetry SDK and an appropriate exporter. | ||||||
|
|
||||||
| ### Telemetry sampling missed an event | ||||||
|
|
||||||
| To ensure that the OpenTelemetry SDK doesn't flood logs with too much data, you can configure it to sample a proportion of traces, so that not every request produces a trace. By default, Aspire's service defaults configure 100% sampling for development. In production, check your sampling configuration. | ||||||
|
|
||||||
| ### Transport protocol mismatch | ||||||
|
|
||||||
| The Aspire dashboard supports both gRPC OTLP (port 18889) and HTTP OTLP (port 18890). Most cloud-hosted OTLP endpoints require HTTP. Verify that the protocol in `OTEL_EXPORTER_OTLP_PROTOCOL` matches the endpoint: | ||||||
|
|
||||||
| - Use `grpc` for the Aspire dashboard's gRPC OTLP endpoint. | ||||||
| - Use `http/protobuf` for HTTP OTLP endpoints (required for browser apps and many cloud services). | ||||||
|
|
||||||
| ## Dashboard access after deployment | ||||||
|
|
||||||
| When you deploy an Aspire app to Azure Container Apps using `aspire deploy`, the Aspire dashboard is included as a container app in your deployment. This gives you a familiar UI for viewing telemetry from your deployed app. | ||||||
|
|
||||||
| <Aside type="caution"> | ||||||
| The deployed Aspire dashboard stores telemetry in memory. Telemetry is lost each time the dashboard container restarts. For persistent telemetry, configure Azure Monitor in addition to the dashboard. | ||||||
| </Aside> | ||||||
|
|
||||||
| ### Find the dashboard URL | ||||||
|
|
||||||
| After a successful `aspire deploy`, the deployment output includes URLs for your deployed resources. Look for a Container App named `aspire-dashboard` or similar in the output. The exact format varies depending on the environment name you configured. | ||||||
|
|
||||||
| You can also find the URL in the [Azure Portal](https://portal.azure.com) by navigating to your resource group, locating the Container App for the Aspire dashboard, and finding its **Application URL** on the **Overview** page. | ||||||
|
|
||||||
| Alternatively, use the Azure CLI: | ||||||
|
|
||||||
| ```azurecli title="Azure CLI — Get dashboard URL" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't support
Suggested change
|
||||||
| az containerapp show \ | ||||||
| --name aspire-dashboard \ | ||||||
| --resource-group my-resource-group \ | ||||||
| --query properties.configuration.ingress.fqdn \ | ||||||
| --output tsv | ||||||
| ``` | ||||||
|
|
||||||
| ### Authenticate with the login token | ||||||
|
|
||||||
| The deployed dashboard requires a login token, just like the standalone dashboard. The token is displayed in the dashboard container's logs. | ||||||
|
|
||||||
| <Steps> | ||||||
|
|
||||||
| 1. In the Azure Portal, navigate to your `aspire-dashboard` Container App. | ||||||
|
|
||||||
| 2. Select **Monitoring** → **Log stream** from the left navigation. | ||||||
|
|
||||||
| 3. Look for a log line containing `login?t=`, for example: | ||||||
|
|
||||||
| ```plaintext data-disable-copy | ||||||
| Login to the dashboard at https://aspire-dashboard.example.com/login?t=abc123... | ||||||
| ``` | ||||||
|
|
||||||
| 4. Copy the token value (the part after `t=`) and use it to log in at the dashboard URL. | ||||||
|
|
||||||
| </Steps> | ||||||
|
|
||||||
| ### Configure a fixed login token | ||||||
|
|
||||||
| By default, the dashboard generates a new token each time it starts. To set a fixed token, configure the `Dashboard:Frontend:BrowserToken` setting on the dashboard container app after deployment using the Azure CLI: | ||||||
|
|
||||||
| ```azurecli title="Azure CLI — Set dashboard token" | ||||||
| az containerapp update \ | ||||||
| --name aspire-dashboard \ | ||||||
| --resource-group my-resource-group \ | ||||||
| --set-env-vars "DASHBOARD__FRONTEND__BROWSERTOKEN=my-secret-token" | ||||||
| ``` | ||||||
|
|
||||||
| <Aside type="caution"> | ||||||
| Use a secure, randomly generated token. Anyone with the token can view sensitive telemetry data including environment variables, connection strings, and runtime data from your app. | ||||||
| </Aside> | ||||||
|
|
||||||
| ### Use OpenID Connect authentication | ||||||
|
|
||||||
| For team access to the deployed dashboard, configure OpenID Connect (OIDC) authentication instead of browser token authentication. This allows multiple users to log in with their organizational identity provider. | ||||||
|
|
||||||
| <LearnMore> | ||||||
| For more information on configuring OIDC, see [Aspire dashboard configuration: Frontend](/dashboard/configuration/#frontend). | ||||||
| </LearnMore> | ||||||
|
|
||||||
| ## See also | ||||||
|
|
||||||
| - [Aspire dashboard configuration](/dashboard/configuration/) | ||||||
| - [Aspire dashboard security considerations](/dashboard/security-considerations/) | ||||||
| - [Enable browser telemetry](/dashboard/enable-browser-telemetry/) | ||||||
| - [Deploy using the Aspire CLI](/deployment/azure/aca-deployment-aspire-cli/) | ||||||
| - [Azure Monitor OpenTelemetry documentation](https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable) | ||||||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.