Skip to content

Latest commit

 

History

History
219 lines (161 loc) · 9.26 KB

feature-telemetry.md

File metadata and controls

219 lines (161 loc) · 9.26 KB

Feature Telemetry Guide — Adding Telemetry to New Features

End-to-end guide for instrumenting telemetry when building new azd features. Ensures telemetry is designed alongside the feature, not bolted on after.

Note

This is the public telemetry documentation. A Microsoft-internal companion set of docs (data pipeline, Kusto/Power BI reporting, runnable queries) is maintained separately for internal maintainers.

Why This Matters

Telemetry is not a separate system — it's part of the feature. When you ship a feature without telemetry:

  • Product can't measure adoption or success
  • Engineering can't diagnose failures in production
  • Dashboards show gaps that require scrambling to fill later

This guide covers defining events, fields, and wiring them into your code. Start with the privacy review checklist: explain the question you want to answer and the values you plan to send.

Microsoft-internal data pipelines (GDPR classification, Kusto functions, Power BI reports) are documented separately for internal maintainers.

Step 1: Define Your Events

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

Add a constant for your feature's event name(s). Follow the naming conventions:

Pattern When to Use Example
cmd.<command.path> Automatically generated for commands cmd.deploy (via GetCommandEventName)
<domain>.<action> Non-command operations container.publish, hooks.exec
<domain>.<scope> Scoped events arm.deploy.subscription
// In events.go — add your event constant
const (
    // MyFeatureEvent tracks the execution of the my-feature operation.
    MyFeatureEvent = "myfeature.execute"
)

Note: Command events (cmd.*) are created automatically by the telemetry middleware via events.GetCommandEventName(...). You only need to define explicit event constants for non-command operations (sub-spans, background work, etc.).

Important

Repository metadata tooling discovers events from exported string constants whose Go identifier contains Event (e.g. MyFeatureEvent). A constant that omits Event from its identifier is not included in telemetry metadata, even if it is emitted. End the identifier with Prefix (e.g. MyFeatureEventPrefix) to register a prefix group that classifies every event name starting with that prefix. See Telemetry Schema → Event declaration contract.

Step 2: Define Your Fields

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

Add exported, package-level AttributeKey variables for any new properties your feature emits. Repository metadata tooling uses these declarations; see the Field declaration contract. Every field must have:

  1. A key name — descriptive, dot-separated, lowercase
  2. A classification — what kind of data is this (see Data Classifications)
  3. A purpose — why are we collecting it (see Purposes)
  4. IsMeasurement: true if the value is numeric (goes to the Measurements column, not Properties)
  5. Endpoint, when required by the classification and declaration contract. It is an identifier-type tag, not a network address; ask a maintainer which type applies.
// In fields.go — add your field keys
var (
    // The strategy used by my feature.
    MyFeatureStrategyKey = AttributeKey{
        Key:            attribute.Key("myfeature.strategy"),
        Classification: SystemMetadata,
        Purpose:        FeatureInsight,
    }

    // The number of items processed.
    MyFeatureItemCountKey = AttributeKey{
        Key:            attribute.Key("myfeature.item.count"),
        Classification: SystemMetadata,
        Purpose:        PerformanceAndHealth,
        IsMeasurement:  true,
    }
)

Data classifications

Use the schema's classification reference and privacy concepts. Propose a classification based on the actual values, not their string type or hashing. Ask a maintainer if the value is a name or identifier, or if no classification clearly fits.

Purposes

Use the schema's purpose reference to choose why the field is needed.

Hashing values

Follow the field's hashing and omission rules. Hashing is not a way to make customer content acceptable. For an existing field that requires hashing:

// In your code, use StringHashed instead of regular attribute setting
tracing.SetUsageAttributes(
    fields.StringHashed(fields.EnvNameKey, envName),
)

Step 3: Instrument Your Code

Important

Always emit through a fields.AttributeKey method — never a raw attribute.* call with a literal key. Use fields.MyFeatureStrategyKey.String(v), not attribute.String("myfeature.strategy", v). Metadata comes from the field declarations, not arbitrary instrumentation calls. Core tests reject raw literal keys. First-party extension ext.* keys have their own declarations and go test ./extensions/telemetry check.

For Command Actions

The telemetry middleware (cmd/middleware/telemetry.go) automatically creates a span for every command. You just need to add your feature-specific attributes:

func (a *myAction) Run(ctx context.Context) (*actions.ActionResult, error) {
    // Set usage attributes — these get attached to the command span
    tracing.SetUsageAttributes(
        fields.MyFeatureStrategyKey.String("incremental"),
        fields.MyFeatureItemCountKey.Int(len(items)),
    )

    // ... do work ...

    return &actions.ActionResult{...}, nil
}

For Sub-Operations (Child Spans)

If your feature has distinct sub-operations worth tracking separately:

func (s *myService) ProcessItems(ctx context.Context, items []Item) (err error) {
    ctx, span := tracing.Start(ctx, events.MyFeatureEvent)
    defer func() { span.EndWithStatus(err) }()

    // Set attributes on this span
    span.SetAttributes(
        fields.MyFeatureItemCountKey.Int(len(items)),
    )

    // ... do work ...

    return err
}

EndWithStatus records classified error information instead of raw error text. Do not pass err.Error() to SetStatus: the status description is exported as the Application Insights response code.

For Extension Commands

Before adding extension usage attributes, follow the extension attribute naming rules.

Extension commands automatically get ext.run events. To add structured error reporting:

// Extensions report structured errors back to the host
return &azdext.ServiceError{
    Message:     fmt.Sprintf("deployment failed: %s", resp.Error.Message),
    ErrorCode:   fmt.Sprintf("deploy.%s", resp.Error.Code),
    StatusCode:  resp.StatusCode,
    ServiceName: "arm",
}

This maps to result codes like ext.service.arm.500 in telemetry. See cli/azd/pkg/azdext/extension_error.go for the full structured error types.

Step 4: Update the Telemetry Schema Doc

File: docs/specs/metrics-audit/telemetry-schema.md

Add your new events and fields to the canonical schema reference. This document is the source of truth for what telemetry azd collects and is reviewed during privacy audits.

Step 5: Check privacy and approval coverage

Follow the privacy review checklist and copy its PR template. The author describes the changed values; a maintainer records whether existing approval covers them and coordinates any required formal review.

Step 6: Product Review

Before merging your feature PR:

  1. Share the telemetry spec with your PM — explain what events/fields you're adding and why
  2. Show sample queries — demonstrate how the data answers product questions
  3. Update the data reference — add your feature's fields to docs/reference/telemetry-data.md

This ensures product can provide feedback during development, not scramble after launch.

Quick Reference: Where Things Live

What File/Path
Event name constants cli/azd/internal/tracing/events/events.go
Field key definitions cli/azd/internal/tracing/fields/fields.go
Hashing helpers cli/azd/internal/tracing/fields/key.go
Telemetry middleware cli/azd/cmd/middleware/telemetry.go
Telemetry pipeline init cli/azd/internal/telemetry/telemetry.go
Error classification cli/azd/internal/cmd/errors.go (MapError)
Canonical schema docs/specs/metrics-audit/telemetry-schema.md
Privacy review checklist docs/specs/metrics-audit/privacy-review-checklist.md

See Also