cli/azd/cmd/monitor.go (161 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. package cmd import ( "context" "errors" "fmt" "github.com/azure/azure-dev/cli/azd/cmd/actions" "github.com/azure/azure-dev/cli/azd/internal" "github.com/azure/azure-dev/cli/azd/pkg/account" "github.com/azure/azure-dev/cli/azd/pkg/alpha" "github.com/azure/azure-dev/cli/azd/pkg/apphost" "github.com/azure/azure-dev/cli/azd/pkg/azapi" "github.com/azure/azure-dev/cli/azd/pkg/azure" "github.com/azure/azure-dev/cli/azd/pkg/cloud" "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext" "github.com/azure/azure-dev/cli/azd/pkg/infra" "github.com/azure/azure-dev/cli/azd/pkg/input" "github.com/azure/azure-dev/cli/azd/pkg/output" "github.com/spf13/cobra" "github.com/spf13/pflag" ) type monitorFlags struct { monitorLive bool monitorLogs bool monitorOverview bool global *internal.GlobalCommandOptions internal.EnvFlag } func (m *monitorFlags) Bind(local *pflag.FlagSet, global *internal.GlobalCommandOptions) { local.BoolVar( &m.monitorLive, "live", false, "Open a browser to Application Insights Live Metrics. Live Metrics is currently not supported for Python apps.", ) local.BoolVar(&m.monitorLogs, "logs", false, "Open a browser to Application Insights Logs.") local.BoolVar(&m.monitorOverview, "overview", false, "Open a browser to Application Insights Overview Dashboard.") m.EnvFlag.Bind(local, global) m.global = global } func newMonitorFlags(cmd *cobra.Command, global *internal.GlobalCommandOptions) *monitorFlags { flags := &monitorFlags{} flags.Bind(cmd.Flags(), global) return flags } func newMonitorCmd() *cobra.Command { return &cobra.Command{ Use: "monitor", Short: fmt.Sprintf("Monitor a deployed application. %s", output.WithWarningFormat("(Beta)")), } } type monitorAction struct { azdCtx *azdcontext.AzdContext env *environment.Environment subResolver account.SubscriptionTenantResolver resourceManager infra.ResourceManager resourceService *azapi.ResourceService console input.Console flags *monitorFlags portalUrlBase string alphaFeaturesManager *alpha.FeatureManager } func newMonitorAction( azdCtx *azdcontext.AzdContext, env *environment.Environment, subResolver account.SubscriptionTenantResolver, resourceManager infra.ResourceManager, resourceService *azapi.ResourceService, console input.Console, flags *monitorFlags, cloud *cloud.Cloud, alphaFeatureManager *alpha.FeatureManager, ) actions.Action { return &monitorAction{ azdCtx: azdCtx, env: env, resourceManager: resourceManager, resourceService: resourceService, console: console, flags: flags, subResolver: subResolver, portalUrlBase: cloud.PortalUrlBase, alphaFeaturesManager: alphaFeatureManager, } } func (m *monitorAction) Run(ctx context.Context) (*actions.ActionResult, error) { if !m.flags.monitorLive && !m.flags.monitorLogs && !m.flags.monitorOverview { m.flags.monitorOverview = true } if m.env.GetSubscriptionId() == "" { return nil, errors.New( "infrastructure has not been provisioned. Run `azd provision`", ) } aspireDashboard := apphost.AspireDashboardUrl(ctx, m.env, m.alphaFeaturesManager) if aspireDashboard != nil { openWithDefaultBrowser(ctx, m.console, aspireDashboard.Link) return nil, nil } resourceGroups, err := m.resourceManager.GetResourceGroupsForEnvironment(ctx, m.env.GetSubscriptionId(), m.env.Name()) if err != nil { return nil, fmt.Errorf("discovering resource groups from deployment: %w", err) } var insightsResources []*azapi.ResourceExtended var portalResources []*azapi.ResourceExtended for _, resourceGroup := range resourceGroups { resources, err := m.resourceService.ListResourceGroupResources( ctx, azure.SubscriptionFromRID(resourceGroup.Id), resourceGroup.Name, nil) if err != nil { return nil, fmt.Errorf("listing resources: %w", err) } for _, resource := range resources { switch resource.Type { case string(azapi.AzureResourceTypePortalDashboard): portalResources = append(portalResources, resource) case string(azapi.AzureResourceTypeAppInsightComponent): insightsResources = append(insightsResources, resource) } } } if len(insightsResources) == 0 && (m.flags.monitorLive || m.flags.monitorLogs) { return nil, fmt.Errorf("application does not contain an Application Insights resource") } if len(portalResources) == 0 && m.flags.monitorOverview { return nil, fmt.Errorf("application does not contain an Application Insights dashboard") } tenantId, err := m.subResolver.LookupTenant(ctx, m.env.GetSubscriptionId()) if err != nil { return nil, err } for _, insightsResource := range insightsResources { if m.flags.monitorLive { openWithDefaultBrowser(ctx, m.console, fmt.Sprintf("%s/#@%s/resource%s/quickPulse", m.portalUrlBase, tenantId, insightsResource.Id)) } if m.flags.monitorLogs { openWithDefaultBrowser(ctx, m.console, fmt.Sprintf("%s/#@%s/resource%s/logs", m.portalUrlBase, tenantId, insightsResource.Id)) } } for _, portalResource := range portalResources { if m.flags.monitorOverview { openWithDefaultBrowser(ctx, m.console, fmt.Sprintf("%s/#@%s/dashboard/arm%s", m.portalUrlBase, tenantId, portalResource.Id)) } } return nil, nil } func getCmdMonitorHelpDescription(*cobra.Command) string { return generateCmdHelpDescription( fmt.Sprintf("Monitor a deployed application %s. For more information, go to: %s.", output.WithWarningFormat("(Beta)"), output.WithLinkFormat("https://aka.ms/azure-dev/monitor")), nil) } func getCmdMonitorHelpFooter(c *cobra.Command) string { return generateCmdHelpSamplesBlock(map[string]string{ "Open Application Insights Overview Dashboard.": output.WithHighLightFormat("azd monitor --overview"), "Open Application Insights Live Metrics.": output.WithHighLightFormat("azd monitor --live"), "Open Application Insights Logs.": output.WithHighLightFormat("azd monitor --logs"), }) }