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.
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.
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 viaevents.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.
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:
- A key name — descriptive, dot-separated, lowercase
- A classification — what kind of data is this (see Data Classifications)
- A purpose — why are we collecting it (see Purposes)
IsMeasurement: trueif the value is numeric (goes to theMeasurementscolumn, notProperties)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,
}
)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.
Use the schema's purpose reference to choose why the field is needed.
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),
)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.
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
}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.
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.
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.
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.
Before merging your feature PR:
- Share the telemetry spec with your PM — explain what events/fields you're adding and why
- Show sample queries — demonstrate how the data answers product questions
- 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.
| 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 |
- Architecture — How the telemetry system works end-to-end
- Data Reference — Complete schema, events, fields
- Privacy Review Checklist — When to do privacy reviews