Skip to content

Commit 2240c9f

Browse files
authored
tracing: add usage attributes for extension installs and runs (#6010)
Add usage events and attributes for extension installs (`ext.install`) and runs (`ext.run`). Expose both `Annotations` and `DisableTroubleshooting` in the action descriptor/options since that was needed. Fixes #5937 Contributes towards #5872
1 parent eda0618 commit 2240c9f

File tree

12 files changed

+75
-10
lines changed

12 files changed

+75
-10
lines changed

cli/azd/cmd/actions/action_descriptor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ type ActionDescriptorOptions struct {
187187
DefaultFormat output.Format
188188
// Whether or not telemetry should be disabled for the current action
189189
DisableTelemetry bool
190+
// Whether or not troubleshooting should be disabled for the current action
191+
DisableTroubleshooting bool
190192
// The logic that produces the command help
191193
HelpOptions ActionHelpOptions
192194
// Defines grouping options for the command

cli/azd/cmd/cobra_builder.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func (cb *CobraBuilder) configureActionResolver(cmd *cobra.Command, descriptor *
122122
CommandPath: cmd.CommandPath(),
123123
Aliases: cmd.Aliases,
124124
Flags: cmd.Flags(),
125+
Annotations: cmd.Annotations,
125126
Args: args,
126127
}
127128

cli/azd/cmd/extensions.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
"github.com/azure/azure-dev/cli/azd/cmd/actions"
1313
"github.com/azure/azure-dev/cli/azd/internal/grpcserver"
14+
"github.com/azure/azure-dev/cli/azd/internal/tracing"
15+
"github.com/azure/azure-dev/cli/azd/internal/tracing/fields"
1416
"github.com/azure/azure-dev/cli/azd/pkg/environment"
1517
"github.com/azure/azure-dev/cli/azd/pkg/exec"
1618
"github.com/azure/azure-dev/cli/azd/pkg/extensions"
@@ -78,8 +80,9 @@ func bindExtension(
7880
}
7981

8082
current.Add(lastPart, &actions.ActionDescriptorOptions{
81-
Command: cmd,
82-
ActionResolver: newExtensionAction,
83+
Command: cmd,
84+
ActionResolver: newExtensionAction,
85+
DisableTroubleshooting: true,
8386
GroupingOptions: actions.CommandGroupOptions{
8487
RootLevelHelp: actions.CmdGroupExtensions,
8588
},
@@ -132,6 +135,10 @@ func (a *extensionAction) Run(ctx context.Context) (*actions.ActionResult, error
132135
return nil, fmt.Errorf("failed to get extension %s: %w", extensionId, err)
133136
}
134137

138+
tracing.SetUsageAttributes(
139+
fields.ExtensionId.String(extension.Id),
140+
fields.ExtensionVersion.String(extension.Version))
141+
135142
allEnv := []string{}
136143
allEnv = append(allEnv, os.Environ()...)
137144

@@ -179,7 +186,7 @@ func (a *extensionAction) Run(ctx context.Context) (*actions.ActionResult, error
179186

180187
_, err = a.extensionRunner.Invoke(ctx, extension, options)
181188
if err != nil {
182-
os.Exit(1)
189+
return nil, err
183190
}
184191

185192
return nil, nil

cli/azd/cmd/middleware/middleware.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type Options struct {
3636
Aliases []string
3737
Flags *pflag.FlagSet
3838
Args []string
39+
Annotations map[string]string
3940
}
4041

4142
func (o *Options) IsChildAction(ctx context.Context) bool {

cli/azd/cmd/middleware/telemetry.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ func (m *TelemetryMiddleware) Run(ctx context.Context, next NextFn) (*actions.Ac
4141
// Note: CommandPath is constructed using the Use member on each command up to the root.
4242
// It does not contain user input, and is safe for telemetry emission.
4343
cmdPath := events.GetCommandEventName(m.options.CommandPath)
44+
45+
extensionId := m.options.Annotations["extension.id"]
46+
if extensionId != "" {
47+
cmdPath = events.ExtensionRunEvent
48+
}
49+
4450
spanCtx, span := tracing.Start(ctx, cmdPath)
4551

4652
log.Printf("TraceID: %s", span.SpanContext().TraceID())

cli/azd/cmd/middleware/ux.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/azure/azure-dev/cli/azd/cmd/actions"
1212
"github.com/azure/azure-dev/cli/azd/internal"
1313
"github.com/azure/azure-dev/cli/azd/pkg/alpha"
14+
"github.com/azure/azure-dev/cli/azd/pkg/extensions"
1415
"github.com/azure/azure-dev/cli/azd/pkg/input"
1516
"github.com/azure/azure-dev/cli/azd/pkg/output"
1617
"github.com/azure/azure-dev/cli/azd/pkg/output/ux"
@@ -57,11 +58,14 @@ func (m *UxMiddleware) Run(ctx context.Context, next NextFn) (*actions.ActionRes
5758
errorMessage.WriteString("\n" + suggestionErr.Suggestion)
5859
}
5960

60-
// UnsupportedServiceHostError is a special error which needs to float up without printing output here yet.
61-
// The error is bubble up for the caller to decide to show it or not
62-
var unsupportedErr *project.UnsupportedServiceHostError
6361
errMessage := errorMessage.String()
64-
if errors.As(err, &unsupportedErr) {
62+
63+
// For specific errors, we silent the output display here and let the caller handle it
64+
var unsupportedErr *project.UnsupportedServiceHostError
65+
var extensionRunErr *extensions.ExtensionRunError
66+
if errors.As(err, &extensionRunErr) {
67+
return actionResult, err
68+
} else if errors.As(err, &unsupportedErr) {
6569
// set the error message so the caller can use it if needed
6670
unsupportedErr.ErrorMessage = errMessage
6771
return actionResult, err

cli/azd/cmd/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,9 @@ func NewRootCmd(
379379
root.
380380
UseMiddleware("debug", middleware.NewDebugMiddleware).
381381
UseMiddleware("ux", middleware.NewUxMiddleware).
382-
UseMiddleware("error", middleware.NewErrorMiddleware).
382+
UseMiddlewareWhen("error", middleware.NewErrorMiddleware, func(descriptor *actions.ActionDescriptor) bool {
383+
return !descriptor.Options.DisableTroubleshooting
384+
}).
383385
UseMiddlewareWhen("telemetry", middleware.NewTelemetryMiddleware, func(descriptor *actions.ActionDescriptor) bool {
384386
return !descriptor.Options.DisableTelemetry
385387
}).

cli/azd/internal/cmd/errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/azure/azure-dev/cli/azd/pkg/auth"
1919
"github.com/azure/azure-dev/cli/azd/pkg/azapi"
2020
"github.com/azure/azure-dev/cli/azd/pkg/exec"
21+
"github.com/azure/azure-dev/cli/azd/pkg/extensions"
2122
"go.opentelemetry.io/otel/attribute"
2223
"go.opentelemetry.io/otel/codes"
2324
)
@@ -31,6 +32,7 @@ func MapError(err error, span tracing.Span) {
3132
var armDeployErr *azapi.AzureDeploymentError
3233
var toolExecErr *exec.ExitError
3334
var authFailedErr *auth.AuthFailedError
35+
var extensionRunErr *extensions.ExtensionRunError
3436
if errors.As(err, &respErr) {
3537
serviceName := "other"
3638
statusCode := -1
@@ -80,6 +82,8 @@ func MapError(err error, span tracing.Span) {
8082
}
8183

8284
errCode = "service.arm.deployment.failed"
85+
} else if errors.As(err, &extensionRunErr) {
86+
errCode = "ext.run.failed"
8387
} else if errors.As(err, &toolExecErr) {
8488
toolName := "other"
8589
cmdName := cmdAsName(toolExecErr.Cmd)

cli/azd/internal/tracing/events/events.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ const PackBuildEvent = "tools.pack.build"
2020

2121
// AgentTroubleshootEvent is the name of the event which tracks agent troubleshoot operations.
2222
const AgentTroubleshootEvent = "agent.troubleshoot"
23+
24+
// Extension related events.
25+
const (
26+
ExtensionRunEvent = "ext.run"
27+
ExtensionInstallEvent = "ext.install"
28+
)

cli/azd/internal/tracing/fields/fields.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,11 @@ const (
287287
// Number of auto-fix.attempts
288288
AgentFixAttempts = attribute.Key("agent.fix.attempts")
289289
)
290+
291+
// Extension related fields
292+
const (
293+
// The identifier of the extension.
294+
ExtensionId = attribute.Key("extension.id")
295+
// The version of the extension.
296+
ExtensionVersion = attribute.Key("extension.version")
297+
)

0 commit comments

Comments
 (0)