Skip to content

Commit 333ec36

Browse files
authored
Gallery API request count tracking by endpoint as metric (#8906)
* Preaggregated metric support. * API request count tracking. * Argument names. * Test fixes. * Replaced the general implementation with a specific one so that we can have compile time checks for supported number of metrics.
1 parent db379b0 commit 333ec36

9 files changed

Lines changed: 86 additions & 5 deletions

File tree

src/GitHubVulnerabilities2Db/Gallery/ThrowingTelemetryService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ public void TrackUserPackageDeleteExecuted(int packageKey, string packageId, str
376376
throw new NotImplementedException();
377377
}
378378

379+
public void TrackApiRequest(string endpoint)
380+
{
381+
throw new NotImplementedException();
382+
}
383+
379384
public void TrackVerifyPackageKeyEvent(string packageId, string packageVersion, User user, IIdentity identity, int statusCode)
380385
{
381386
throw new NotImplementedException();

src/NuGetGallery.Services/Telemetry/ITelemetryClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public interface ITelemetryClient
1616

1717
void TrackMetric(string metricName, double value, IDictionary<string, string> properties = null);
1818

19+
void TrackAggregatedMetric(string metricName, double value, string dimension0Name, string dimension0Value);
20+
1921
void TrackException(Exception exception, IDictionary<string, string> properties = null, IDictionary<string, double> metrics = null);
2022

2123
void TrackDependency(string dependencyTypeName,

src/NuGetGallery.Services/Telemetry/ITelemetryService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,11 @@ void TrackABTestEvaluated(
418418
/// </summary>
419419
/// <param name="uptime">The uptime to report.</param>
420420
void TrackInstanceUptime(TimeSpan uptime);
421+
422+
/// <summary>
423+
/// Tracks API request count by endpoint.
424+
/// </summary>
425+
/// <param name="endpoint"></param>
426+
void TrackApiRequest(string endpoint);
421427
}
422428
}

src/NuGetGallery.Services/Telemetry/TelemetryClientWrapper.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using Microsoft.ApplicationInsights;
78
using Microsoft.ApplicationInsights.DataContracts;
89
using Microsoft.ApplicationInsights.Extensibility;
10+
using Microsoft.ApplicationInsights.Metrics;
911
using Microsoft.Extensions.Logging;
1012

1113
namespace NuGetGallery
@@ -57,6 +59,26 @@ public void TrackMetric(string metricName, double value, IDictionary<string, str
5759
}
5860
}
5961

62+
public void TrackAggregatedMetric(string metricName, double value, string dimension0Name, string dimension0Value)
63+
{
64+
try
65+
{
66+
var metricIdentifier = new MetricIdentifier(
67+
metricNamespace: "Gallery",
68+
metricId: metricName,
69+
dimensionNames: new List<string>
70+
{
71+
dimension0Name
72+
});
73+
var metric = UnderlyingClient.GetMetric(metricIdentifier);
74+
metric.TrackValue(value, dimension0Value);
75+
}
76+
catch
77+
{
78+
// logging failed, don't allow exception to escape
79+
}
80+
}
81+
6082
public void TrackDependency(string dependencyTypeName,
6183
string target,
6284
string dependencyName,

src/NuGetGallery.Services/Telemetry/TelemetryService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public class Events
9494
public const string SymbolPackagePushDisconnect = "SymbolPackagePushDisconnect";
9595
public const string VulnerabilitiesCacheRefreshDurationMs = "VulnerabilitiesCacheRefreshDurationMs";
9696
public const string InstanceUptime = "InstanceUptimeInDays";
97+
public const string ApiRequest = "ApiRequest";
9798
}
9899

99100
private readonly IDiagnosticsSource _diagnosticsSource;
@@ -233,6 +234,8 @@ public class Events
233234
public const string TestBucket = "TestBucket";
234235
public const string TestPercentage = "TestPercentage";
235236

237+
public const string Endpoint = "Endpoint";
238+
236239
public TelemetryService(IDiagnosticsSource diagnosticsSource, ITelemetryClient telemetryClient)
237240
{
238241
_telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
@@ -1130,6 +1133,11 @@ public void TrackVulnerabilitiesCacheRefreshDuration(TimeSpan duration)
11301133
TrackMetric(Events.VulnerabilitiesCacheRefreshDurationMs, duration.TotalMilliseconds, properties => { });
11311134
}
11321135

1136+
public void TrackApiRequest(string endpoint)
1137+
{
1138+
_telemetryClient.TrackAggregatedMetric(Events.ApiRequest, 1, Endpoint, endpoint);
1139+
}
1140+
11331141
/// <summary>
11341142
/// We use <see cref="ITelemetryClient.TrackMetric(string, double, IDictionary{string, string})"/> instead of
11351143
/// <see cref="ITelemetryClient.TrackEvent(string, IDictionary{string, string}, IDictionary{string, double})"/>

src/NuGetGallery/Controllers/ODataV1FeedController.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public ODataV1FeedController(
5656
[CacheOutput(NoCache = true)]
5757
public IHttpActionResult Get(ODataQueryOptions<V1FeedPackage> options)
5858
{
59+
_telemetryService.TrackApiRequest("/api/v1/Packages");
5960
return Get(options, _featureFlagService.IsODataV1GetAllEnabled());
6061
}
6162

@@ -64,6 +65,7 @@ public IHttpActionResult Get(ODataQueryOptions<V1FeedPackage> options)
6465
[CacheOutput(NoCache = true)]
6566
public IHttpActionResult GetCount(ODataQueryOptions<V1FeedPackage> options)
6667
{
68+
_telemetryService.TrackApiRequest("/api/v1/Packages/$count");
6769
return Get(options, _featureFlagService.IsODataV1GetAllCountEnabled())
6870
.FormattedAsCountResult<V1FeedPackage>();
6971
}
@@ -107,6 +109,7 @@ private IHttpActionResult Get(ODataQueryOptions<V1FeedPackage> options, bool isN
107109
ClientTimeSpan = ODataCacheConfiguration.DefaultGetByIdAndVersionCacheTimeInSeconds)]
108110
public async Task<IHttpActionResult> Get(ODataQueryOptions<V1FeedPackage> options, string id, string version)
109111
{
112+
_telemetryService.TrackApiRequest("/api/v1/Packages(Id=,Version=)");
110113
var result = await GetCoreAsync(
111114
options,
112115
id,
@@ -126,6 +129,7 @@ public async Task<IHttpActionResult> Get(ODataQueryOptions<V1FeedPackage> option
126129
ClientTimeSpan = ODataCacheConfiguration.DefaultGetByIdAndVersionCacheTimeInSeconds)]
127130
public async Task<IHttpActionResult> FindPackagesById(ODataQueryOptions<V1FeedPackage> options, [FromODataUri]string id)
128131
{
132+
_telemetryService.TrackApiRequest("/api/v1/FindPackagesById()?id=");
129133
return await FindPackagesByIdAsync(
130134
options,
131135
id,
@@ -140,6 +144,7 @@ public async Task<IHttpActionResult> FindPackagesById(ODataQueryOptions<V1FeedPa
140144
NoCache = true)]
141145
public async Task<IHttpActionResult> FindPackagesByIdCount(ODataQueryOptions<V1FeedPackage> options, [FromODataUri] string id)
142146
{
147+
_telemetryService.TrackApiRequest("/api/v1/FindPackagesById()/$count?id=");
143148
return (await FindPackagesByIdAsync(
144149
options,
145150
id,
@@ -255,6 +260,7 @@ private async Task<IHttpActionResult> GetCoreAsync(
255260
[HttpGet]
256261
public IHttpActionResult GetPropertyFromPackages(string propertyName, string id, string version)
257262
{
263+
_telemetryService.TrackApiRequest("/api/v1/Packages(Id=,Version=)/propertyName");
258264
switch (propertyName.ToLowerInvariant())
259265
{
260266
case "id": return Ok(id);
@@ -276,6 +282,7 @@ public async Task<IHttpActionResult> Search(
276282
[FromODataUri]string searchTerm = "",
277283
[FromODataUri]string targetFramework = "")
278284
{
285+
_telemetryService.TrackApiRequest("/api/v1/Search()?searchTerm=&targetFramework=&includePrerelease=");
279286
return await SearchAsync(
280287
options,
281288
searchTerm,
@@ -294,6 +301,7 @@ public async Task<IHttpActionResult> SearchCount(
294301
[FromODataUri]string searchTerm = "",
295302
[FromODataUri]string targetFramework = "")
296303
{
304+
_telemetryService.TrackApiRequest("/api/v1/Search()/$count?searchTerm=&targetFramework=&includePrerelease=");
297305
return (await SearchAsync(
298306
options,
299307
searchTerm,

src/NuGetGallery/Controllers/ODataV2FeedController.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public async Task<IHttpActionResult> Get(
6161
ODataQueryOptions<V2FeedPackage> options,
6262
[FromUri]string semVerLevel = null)
6363
{
64+
_telemetryService.TrackApiRequest("/api/v2/Packages?semVerLevel=");
6465
return await GetAsync(
6566
options,
6667
semVerLevel,
@@ -74,6 +75,7 @@ public async Task<IHttpActionResult> GetCount(
7475
ODataQueryOptions<V2FeedPackage> options,
7576
[FromUri] string semVerLevel = null)
7677
{
78+
_telemetryService.TrackApiRequest("/api/v2/Packages/$count?semVerLevel=");
7779
return (await GetAsync(
7880
options,
7981
semVerLevel,
@@ -198,6 +200,7 @@ public async Task<IHttpActionResult> Get(
198200
string version,
199201
[FromUri] bool hijack = true)
200202
{
203+
_telemetryService.TrackApiRequest("/api/v2/Packages(Id=,Version=)");
201204
// We are defaulting to semVerLevel = "2.0.0" by design.
202205
// The client is requesting a specific package version and should support what it requests.
203206
// If not, too bad :)
@@ -226,6 +229,7 @@ public async Task<IHttpActionResult> FindPackagesById(
226229
[FromODataUri]string id,
227230
[FromUri]string semVerLevel = null)
228231
{
232+
_telemetryService.TrackApiRequest("/api/v2/FindPackagesById()?id=&semVerLevel=");
229233
return await FindPackagesByIdAsync(
230234
options,
231235
id,
@@ -244,6 +248,7 @@ public async Task<IHttpActionResult> FindPackagesByIdCount(
244248
[FromODataUri] string id,
245249
[FromUri] string semVerLevel = null)
246250
{
251+
_telemetryService.TrackApiRequest("/api/v2/FindPackagesById()/$count?semVerLevel=");
247252
return (await FindPackagesByIdAsync(
248253
options,
249254
id,
@@ -413,6 +418,7 @@ private async Task<IHttpActionResult> GetCoreAsync(
413418
[HttpGet]
414419
public IHttpActionResult GetPropertyFromPackages(string propertyName, string id, string version)
415420
{
421+
_telemetryService.TrackApiRequest("/api/v2/Packages(Id=,Version=)/propertyName");
416422
switch (propertyName.ToLowerInvariant())
417423
{
418424
case "id": return Ok(id);
@@ -436,6 +442,7 @@ public async Task<IHttpActionResult> Search(
436442
[FromODataUri]bool includePrerelease = false,
437443
[FromUri]string semVerLevel = null)
438444
{
445+
_telemetryService.TrackApiRequest("/api/v2/Search()?searchTerm=&targetFramework=&includePrerelease=");
439446
return await SearchAsync(
440447
options,
441448
searchTerm,
@@ -458,6 +465,7 @@ public async Task<IHttpActionResult> SearchCount(
458465
[FromODataUri] bool includePrerelease = false,
459466
[FromUri] string semVerLevel = null)
460467
{
468+
_telemetryService.TrackApiRequest("/api/v2/Search()/$count?searchTerm=&targetFramework=&includePrerelease=&semVerLevel=");
461469
return (await SearchAsync(
462470
options,
463471
searchTerm,
@@ -589,6 +597,7 @@ public IHttpActionResult GetUpdates(
589597
[FromODataUri]string versionConstraints = "",
590598
[FromUri]string semVerLevel = null)
591599
{
600+
_telemetryService.TrackApiRequest("/api/v2/GetUpdates()?packageIds=&versions=&includePrerelease=&includeAllVersions=&targetFrameworks=&versionConstraints=&semVerLevel=");
592601
if (string.IsNullOrEmpty(packageIds) || string.IsNullOrEmpty(versions))
593602
{
594603
return TrackedQueryResult(
@@ -684,6 +693,7 @@ public IHttpActionResult GetUpdatesCount(
684693
[FromODataUri]string versionConstraints = "",
685694
[FromUri]string semVerLevel = null)
686695
{
696+
_telemetryService.TrackApiRequest("/api/v2/GetUpdates()/$count?packageIds=&versions=&includePrerelease=&includeAllVersions=&targetFrameworks=&versionConstraints=&semVerLevel=");
687697
return GetUpdates(
688698
options,
689699
packageIds,

src/VerifyMicrosoftPackage/Fakes/FakeTelemetryService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ public void TrackUserPackageDeleteExecuted(int packageKey, string packageId, str
378378
throw new NotImplementedException();
379379
}
380380

381+
public void TrackApiRequest(string endpoint)
382+
{
383+
throw new NotImplementedException();
384+
}
385+
381386
public void TrackVerifyPackageKeyEvent(string packageId, string packageVersion, User user, IIdentity identity, int statusCode)
382387
{
383388
throw new NotImplementedException();

tests/NuGetGallery.Facts/Services/TelemetryServiceFacts.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ public static IEnumerable<object[]> TrackMetricNames_Data
355355
yield return new object[] { "InstanceUptimeInDays",
356356
(TrackAction)(s => s.TrackInstanceUptime(TimeSpan.FromSeconds(1)))
357357
};
358+
359+
yield return new object[] { "ApiRequest",
360+
(TrackAction)(s => s.TrackApiRequest("SomeEndpoint")),
361+
true
362+
};
358363
}
359364
}
360365

@@ -369,7 +374,7 @@ public void TrackEventNamesIncludesAllEvents()
369374

370375
[Theory]
371376
[MemberData(nameof(TrackMetricNames_Data))]
372-
public void TrackMetricNames(string metricName, TrackAction track)
377+
public void TrackMetricNames(string metricName, TrackAction track, bool isAggregatedMetric = false)
373378
{
374379
// Arrange
375380
var service = CreateService();
@@ -378,10 +383,20 @@ public void TrackMetricNames(string metricName, TrackAction track)
378383
track(service);
379384

380385
// Assert
381-
service.TelemetryClient.Verify(c => c.TrackMetric(metricName,
382-
It.IsAny<double>(),
383-
It.IsAny<IDictionary<string, string>>()),
384-
Times.Once);
386+
if (!isAggregatedMetric)
387+
{
388+
service.TelemetryClient.Verify(c => c.TrackMetric(metricName,
389+
It.IsAny<double>(),
390+
It.IsAny<IDictionary<string, string>>()),
391+
Times.Once);
392+
}
393+
else
394+
{
395+
service.TelemetryClient.Verify(c => c.TrackAggregatedMetric(metricName,
396+
It.IsAny<double>(),
397+
It.IsAny<string>(), It.IsAny<string>()),
398+
Times.Once);
399+
}
385400
}
386401

387402
[Fact]

0 commit comments

Comments
 (0)