Skip to content

Commit 60ab977

Browse files
Copilotjeffkl
andcommitted
Add HasNonstandardId telemetry for non-standard package ID characters per feed source
Co-authored-by: jeffkl <[email protected]>
1 parent 61942fa commit 60ab977

9 files changed

Lines changed: 112 additions & 4 deletions

File tree

src/NuGet.Clients/NuGet.VisualStudio.Common/Telemetry/PackageSourceTelemetry.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.Generic;
99
using System.Globalization;
1010
using System.Linq;
11+
using System.Text.RegularExpressions;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using NuGet.Common;
@@ -29,6 +30,8 @@ public sealed class PackageSourceTelemetry : IDisposable
2930

3031
internal const string EventName = "PackageSourceDiagnostics";
3132

33+
private static readonly Regex s_nonstandardIdCharRegex = new Regex(@"[^A-Za-z0-9.\-]", RegexOptions.Compiled);
34+
3235
public enum TelemetryAction
3336
{
3437
Unknown = 0,
@@ -203,9 +206,19 @@ internal static void AddNupkgCopiedData(ProtocolDiagnosticNupkgCopiedEvent ncEve
203206
{
204207
data.NupkgCount++;
205208
data.NupkgSize += ncEvent.FileSize;
209+
210+
if (!data.HasNonstandardId && ncEvent.PackageId != null && HasNonstandardCharacters(ncEvent.PackageId))
211+
{
212+
data.HasNonstandardId = true;
213+
}
206214
}
207215
}
208216

217+
private static bool HasNonstandardCharacters(string packageId)
218+
{
219+
return s_nonstandardIdCharRegex.IsMatch(packageId);
220+
}
221+
209222
public void Dispose()
210223
{
211224
ProtocolDiagnostics.HttpEvent -= ProtocolDiagnostics_HttpEvent;
@@ -261,6 +274,7 @@ internal static async Task<TelemetryEvent> ToTelemetryAsync(Data data, SourceRep
261274
telemetry[PropertyNames.Duration.Total] = data.Resources.Values.Sum(r => r.duration.TotalMilliseconds);
262275
telemetry[PropertyNames.Nupkgs.Copied] = data.NupkgCount;
263276
telemetry[PropertyNames.Nupkgs.Bytes] = data.NupkgSize;
277+
telemetry[PropertyNames.Nupkgs.HasNonstandardId] = data.HasNonstandardId;
264278
AddResourceProperties(telemetry, data.Resources);
265279

266280
if (data.Http.Requests > 0)
@@ -426,6 +440,7 @@ internal class Data
426440
internal HttpData Http { get; }
427441
internal int NupkgCount { get; set; }
428442
internal long NupkgSize { get; set; }
443+
internal bool HasNonstandardId { get; set; }
429444

430445
internal Data()
431446
{
@@ -475,6 +490,7 @@ internal static class Nupkgs
475490
{
476491
internal const string Copied = "nupkgs.copied";
477492
internal const string Bytes = "nupkgs.bytes";
493+
internal const string HasNonstandardId = "nupkgs.hasnonstandard_id";
478494
}
479495

480496
internal static class Resources

src/NuGet.Core/NuGet.Protocol/Events/ProtocolDiagnosticNupkgCopiedEvent.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,26 @@ public sealed class ProtocolDiagnosticNupkgCopiedEvent
1010
public string Source { get; }
1111
public long FileSize { get; }
1212

13+
/// <summary>
14+
/// Gets the package ID of the copied nupkg, or <see langword="null"/> if not available.
15+
/// </summary>
16+
public string PackageId { get; }
17+
1318
public ProtocolDiagnosticNupkgCopiedEvent(
1419
string source,
1520
long fileSize)
21+
: this(source, fileSize, packageId: null)
22+
{
23+
}
24+
25+
public ProtocolDiagnosticNupkgCopiedEvent(
26+
string source,
27+
long fileSize,
28+
string packageId)
1629
{
1730
Source = source;
1831
FileSize = fileSize;
32+
PackageId = packageId;
1933
}
2034
}
2135
}

src/NuGet.Core/NuGet.Protocol/LocalPackageArchiveDownloader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public async Task<bool> CopyNupkgFileToAsync(string destinationFilePath, Cancell
189189

190190
await source.CopyToAsync(destination, bufferSize, cancellationToken);
191191

192-
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(Source, destination.Length));
192+
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(Source, destination.Length, _packageIdentity.Id));
193193

194194
return true;
195195
}

src/NuGet.Core/NuGet.Protocol/LocalRepositories/LocalV2FindPackageByIdResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public override async Task<bool> CopyNupkgToStreamAsync(
176176
using (var fileStream = File.OpenRead(info.Path))
177177
{
178178
await fileStream.CopyToAsync(destination, cancellationToken);
179-
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_source, destination.Length));
179+
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_source, destination.Length, id));
180180
return true;
181181
}
182182
}

src/NuGet.Core/NuGet.Protocol/LocalRepositories/LocalV3FindPackageByIdResource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public override async Task<bool> CopyNupkgToStreamAsync(
210210
using (var fileStream = File.OpenRead(packagePath))
211211
{
212212
await fileStream.CopyToAsync(destination, cancellationToken);
213-
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_source, destination.Length));
213+
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_source, destination.Length, id));
214214
return true;
215215
}
216216
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
~NuGet.Protocol.Events.ProtocolDiagnosticNupkgCopiedEvent.PackageId.get -> string
3+
~NuGet.Protocol.Events.ProtocolDiagnosticNupkgCopiedEvent.ProtocolDiagnosticNupkgCopiedEvent(string source, long fileSize, string packageId) -> void
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
~NuGet.Protocol.Events.ProtocolDiagnosticNupkgCopiedEvent.PackageId.get -> string
3+
~NuGet.Protocol.Events.ProtocolDiagnosticNupkgCopiedEvent.ProtocolDiagnosticNupkgCopiedEvent(string source, long fileSize, string packageId) -> void

src/NuGet.Core/NuGet.Protocol/Utility/FindPackagesByIdNupkgDownloader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public async Task<bool> CopyNupkgToStreamAsync(
137137
try
138138
{
139139
await stream.CopyToAsync(destination, token);
140-
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_httpSource.PackageSource, destination.Length));
140+
ProtocolDiagnostics.RaiseEvent(new ProtocolDiagnosticNupkgCopiedEvent(_httpSource.PackageSource, destination.Length, identity.Id));
141141
}
142142
catch when (!token.IsCancellationRequested)
143143
{

test/NuGet.Clients.Tests/NuGet.VisualStudio.Common.Test/Telemetry/PackageSourceTelemetryTests.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,79 @@ public void AddNupkgCopiedData_MultipleEvents_AccumulatesCorrectly()
137137
Assert.Equal(sizes.Sum(), result.NupkgSize);
138138
}
139139

140+
[Theory]
141+
[InlineData("Newtonsoft.Json")]
142+
[InlineData("NuGet.Protocol")]
143+
[InlineData("My-Package.1")]
144+
[InlineData("ALLCAPS")]
145+
[InlineData("alllower")]
146+
[InlineData("123Numeric")]
147+
[InlineData("a")]
148+
public void AddNupkgCopiedData_StandardPackageId_HasNonstandardIdIsFalse(string packageId)
149+
{
150+
// Arrange
151+
var data = CreateDataDictionary(SampleSource);
152+
var nce = new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000, packageId);
153+
154+
// Act
155+
PackageSourceTelemetry.AddNupkgCopiedData(nce, data);
156+
157+
// Assert
158+
var result = Assert.Single(data).Value;
159+
Assert.False(result.HasNonstandardId);
160+
}
161+
162+
[Theory]
163+
[InlineData("My_Package")]
164+
[InlineData("[email protected]")]
165+
[InlineData("Ünïcödé")]
166+
[InlineData("Package Name")]
167+
[InlineData("package+extra")]
168+
public void AddNupkgCopiedData_NonstandardPackageId_HasNonstandardIdIsTrue(string packageId)
169+
{
170+
// Arrange
171+
var data = CreateDataDictionary(SampleSource);
172+
var nce = new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000, packageId);
173+
174+
// Act
175+
PackageSourceTelemetry.AddNupkgCopiedData(nce, data);
176+
177+
// Assert
178+
var result = Assert.Single(data).Value;
179+
Assert.True(result.HasNonstandardId);
180+
}
181+
182+
[Fact]
183+
public void AddNupkgCopiedData_MultiplePackagesOneNonstandard_HasNonstandardIdIsTrue()
184+
{
185+
// Arrange
186+
var data = CreateDataDictionary(SampleSource);
187+
188+
// Act
189+
PackageSourceTelemetry.AddNupkgCopiedData(new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000, "Standard.Package"), data);
190+
PackageSourceTelemetry.AddNupkgCopiedData(new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000, "Nonstandard_Package"), data);
191+
PackageSourceTelemetry.AddNupkgCopiedData(new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000, "Another.Standard"), data);
192+
193+
// Assert
194+
var result = Assert.Single(data).Value;
195+
Assert.True(result.HasNonstandardId);
196+
}
197+
198+
[Fact]
199+
public void AddNupkgCopiedData_NullPackageId_HasNonstandardIdIsFalse()
200+
{
201+
// Arrange
202+
var data = CreateDataDictionary(SampleSource);
203+
var nce = new ProtocolDiagnosticNupkgCopiedEvent(SampleSource, fileSize: 1000);
204+
205+
// Act
206+
PackageSourceTelemetry.AddNupkgCopiedData(nce, data);
207+
208+
// Assert
209+
var result = Assert.Single(data).Value;
210+
Assert.False(result.HasNonstandardId);
211+
}
212+
140213
[Fact]
141214
public async Task AddData_IsThreadSafe()
142215
{
@@ -308,6 +381,7 @@ public async Task ToTelemetry_WithData_CreatesTelemetryProperties(string package
308381

309382
Assert.Equal(data.NupkgCount, result[PackageSourceTelemetry.PropertyNames.Nupkgs.Copied]);
310383
Assert.Equal(data.NupkgSize, result[PackageSourceTelemetry.PropertyNames.Nupkgs.Bytes]);
384+
Assert.Equal(data.HasNonstandardId, result[PackageSourceTelemetry.PropertyNames.Nupkgs.HasNonstandardId]);
311385

312386
Assert.Equal(data.Resources.Sum(r => r.Value.count), result[PackageSourceTelemetry.PropertyNames.Resources.Calls]);
313387
foreach (var resource in data.Resources)

0 commit comments

Comments
 (0)