Skip to content

Commit ee762d4

Browse files
authored
Fix populating module name in telemetry policy (#20967) (#20971)
SDKs that contain one or more clients in sub-packages could have their module name incorrectly set in the telemetry string.
1 parent 0243175 commit ee762d4

File tree

5 files changed

+72
-28
lines changed

5 files changed

+72
-28
lines changed

sdk/azcore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Bugs Fixed
66
* Retry policy always clones the underlying `*http.Request` before invoking the next policy.
77
* Added some non-standard error codes to the list of error codes for unregistered resource providers.
8+
* Fixed an issue in `azcore.NewClient()` and `arm.NewClient()` that could cause an incorrect module name to be used in telemetry.
89

910
## 1.6.0 (2023-05-04)
1011

sdk/azcore/arm/client.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ type Client struct {
2828

2929
// NewClient creates a new Client instance with the provided values.
3030
// This client is intended to be used with Azure Resource Manager endpoints.
31-
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
31+
// - clientName - the fully qualified name of the client ("module/package.Client"); this is used by the telemetry policy and tracing provider.
32+
// if module and package are the same value, the "module/" prefix can be omitted.
3233
// - moduleVersion - the version of the containing module; used by the telemetry policy
3334
// - cred - the TokenCredential used to authenticate the request
3435
// - options - optional client configurations; pass nil to accept the default values
3536
func NewClient(clientName, moduleVersion string, cred azcore.TokenCredential, options *ClientOptions) (*Client, error) {
36-
pkg, err := shared.ExtractPackageName(clientName)
37+
mod, client, err := shared.ExtractModuleName(clientName)
3738
if err != nil {
3839
return nil, err
3940
}
@@ -52,12 +53,12 @@ func NewClient(clientName, moduleVersion string, cred azcore.TokenCredential, op
5253
if c, ok := options.Cloud.Services[cloud.ResourceManager]; ok {
5354
ep = c.Endpoint
5455
}
55-
pl, err := armruntime.NewPipeline(pkg, moduleVersion, cred, runtime.PipelineOptions{}, options)
56+
pl, err := armruntime.NewPipeline(mod, moduleVersion, cred, runtime.PipelineOptions{}, options)
5657
if err != nil {
5758
return nil, err
5859
}
5960

60-
tr := options.TracingProvider.NewTracer(clientName, moduleVersion)
61+
tr := options.TracingProvider.NewTracer(client, moduleVersion)
6162
return &Client{ep: ep, pl: pl, tr: tr}, nil
6263
}
6364

sdk/azcore/core.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ type Client struct {
7676
}
7777

7878
// NewClient creates a new Client instance with the provided values.
79-
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
79+
// - clientName - the fully qualified name of the client ("module/package.Client"); this is used by the telemetry policy and tracing provider.
80+
// if module and package are the same value, the "module/" prefix can be omitted.
8081
// - moduleVersion - the semantic version of the containing module; used by the telemetry policy
8182
// - plOpts - pipeline configuration options; can be the zero-value
8283
// - options - optional client configurations; pass nil to accept the default values
8384
func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions, options *ClientOptions) (*Client, error) {
84-
pkg, err := shared.ExtractPackageName(clientName)
85+
mod, client, err := shared.ExtractModuleName(clientName)
8586
if err != nil {
8687
return nil, err
8788
}
@@ -96,9 +97,9 @@ func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions,
9697
}
9798
}
9899

99-
pl := runtime.NewPipeline(pkg, moduleVersion, plOpts, options)
100+
pl := runtime.NewPipeline(mod, moduleVersion, plOpts, options)
100101

101-
tr := options.TracingProvider.NewTracer(clientName, moduleVersion)
102+
tr := options.TracingProvider.NewTracer(client, moduleVersion)
102103
return &Client{pl: pl, tr: tr}, nil
103104
}
104105

sdk/azcore/internal/shared/shared.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"reflect"
1414
"regexp"
1515
"strconv"
16-
"strings"
1716
"time"
1817
)
1918

@@ -79,14 +78,26 @@ func ValidateModVer(moduleVersion string) error {
7978
return nil
8079
}
8180

82-
// ExtractPackageName returns "package" from "package.Client".
81+
// ExtractModuleName returns "module", "package.Client" from "module/package.Client" or
82+
// "package", "package.Client" from "package.Client" when there's no "module/" prefix.
8383
// If clientName is malformed, an error is returned.
84-
func ExtractPackageName(clientName string) (string, error) {
85-
pkg, client, ok := strings.Cut(clientName, ".")
86-
if !ok {
87-
return "", fmt.Errorf("missing . in clientName %s", clientName)
88-
} else if pkg == "" || client == "" {
89-
return "", fmt.Errorf("malformed clientName %s", clientName)
84+
func ExtractModuleName(clientName string) (string, string, error) {
85+
// uses unnamed capturing for "module", "package.Client", and "package"
86+
regex, err := regexp.Compile(`^(?:([a-z0-9]+)/)?(([a-z0-9]+)\.(?:[A-Za-z0-9]+))$`)
87+
if err != nil {
88+
return "", "", err
9089
}
91-
return pkg, nil
90+
91+
matches := regex.FindStringSubmatch(clientName)
92+
if len(matches) < 4 {
93+
return "", "", fmt.Errorf("malformed clientName %s", clientName)
94+
}
95+
96+
// the first match is the entire string, the second is "module", the third is
97+
// "package.Client" and the fourth is "package".
98+
// if there was no "module/" prefix, the second match will be the empty string
99+
if matches[1] != "" {
100+
return matches[1], matches[2], nil
101+
}
102+
return matches[3], matches[2], nil
92103
}

sdk/azcore/internal/shared/shared_test.go

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,24 +85,54 @@ func TestValidateModVer(t *testing.T) {
8585
require.Error(t, ValidateModVer("v1.2"))
8686
}
8787

88-
func TestExtractPackageName(t *testing.T) {
89-
pkg, err := ExtractPackageName("package.Client")
88+
func TestExtractModuleName(t *testing.T) {
89+
mod, client, err := ExtractModuleName("module/package.Client")
9090
require.NoError(t, err)
91-
require.Equal(t, "package", pkg)
91+
require.Equal(t, "module", mod)
92+
require.Equal(t, "package.Client", client)
9293

93-
pkg, err = ExtractPackageName("malformed")
94+
mod, client, err = ExtractModuleName("malformed/")
9495
require.Error(t, err)
95-
require.Empty(t, pkg)
96+
require.Empty(t, mod)
97+
require.Empty(t, client)
9698

97-
pkg, err = ExtractPackageName(".malformed")
99+
mod, client, err = ExtractModuleName("malformed/malformed")
98100
require.Error(t, err)
99-
require.Empty(t, pkg)
101+
require.Empty(t, mod)
102+
require.Empty(t, client)
100103

101-
pkg, err = ExtractPackageName("malformed.")
104+
mod, client, err = ExtractModuleName("malformed/malformed.")
102105
require.Error(t, err)
103-
require.Empty(t, pkg)
106+
require.Empty(t, mod)
107+
require.Empty(t, client)
104108

105-
pkg, err = ExtractPackageName("")
109+
mod, client, err = ExtractModuleName("malformed/.malformed")
106110
require.Error(t, err)
107-
require.Empty(t, pkg)
111+
require.Empty(t, mod)
112+
require.Empty(t, client)
113+
114+
mod, client, err = ExtractModuleName("package.Client")
115+
require.NoError(t, err)
116+
require.Equal(t, "package", mod)
117+
require.Equal(t, "package.Client", client)
118+
119+
mod, client, err = ExtractModuleName("malformed")
120+
require.Error(t, err)
121+
require.Empty(t, mod)
122+
require.Empty(t, client)
123+
124+
mod, client, err = ExtractModuleName(".malformed")
125+
require.Error(t, err)
126+
require.Empty(t, mod)
127+
require.Empty(t, client)
128+
129+
mod, client, err = ExtractModuleName("malformed.")
130+
require.Error(t, err)
131+
require.Empty(t, mod)
132+
require.Empty(t, client)
133+
134+
mod, client, err = ExtractModuleName("")
135+
require.Error(t, err)
136+
require.Empty(t, mod)
137+
require.Empty(t, client)
108138
}

0 commit comments

Comments
 (0)