Skip to content

Commit 9e91a64

Browse files
westin-mGeoKjennyf19keegan-caruso
authored
Add request count and duration telemetry (#3022)
* Add request count and duration telemetry * Add request count and duration telemetry * small fix * Feedback, remodel timing and counters * Use interface for logging, move constants to class * some interface changes, rework tests * fix naming * address interface feedback * Interface and namespace changes * avoid friend assemblies * add metadata * doc comments for tags * PR feedback * Apply suggestions from code review Co-authored-by: Keegan <[email protected]> * change class name and add clarifying comments * Change namespace, move package reference --------- Co-authored-by: George Krechar <[email protected]> Co-authored-by: jennyf19 <[email protected]> Co-authored-by: Keegan <[email protected]>
1 parent 075a627 commit 9e91a64

File tree

22 files changed

+708
-8
lines changed

22 files changed

+708
-8
lines changed

build/dependencies.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<MicrosoftSourceLinkGitHubVersion>1.0.0</MicrosoftSourceLinkGitHubVersion>
99
<NetStandardVersion>2.0.3</NetStandardVersion>
1010
<NewtonsoftVersion>13.0.3</NewtonsoftVersion>
11+
<SystemDiagnosticSourceVersion>6.0.2</SystemDiagnosticSourceVersion>
1112
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
1213
<SystemSecurityCryptographyCngVersion>4.5.0</SystemSecurityCryptographyCngVersion>
1314
<SystemTextJson>8.0.5</SystemTextJson>

build/dependenciesTest.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<MicrosoftNETTestSdkVersion>17.11.1</MicrosoftNETTestSdkVersion>
88
<NetStandardVersion>2.0.3</NetStandardVersion>
99
<NewtonsoftVersion>13.0.3</NewtonsoftVersion>
10+
<OpenTelemetryVersion>1.6.0</OpenTelemetryVersion>
1011
<SystemNetHttp>4.3.4</SystemNetHttp>
1112
<SystemSecurityClaimsVersion>4.3.0</SystemSecurityClaimsVersion>
1213
<SystemTextJson>8.0.5</SystemTextJson>

src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler._telemetryClient -> Microsoft.IdentityModel.Telemetry.ITelemetryClient
12
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateToken(string payload, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string
23
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary<string, object> additionalHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> string
34
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
using Microsoft.IdentityModel.Abstractions;
1111
using Microsoft.IdentityModel.Logging;
1212
using Microsoft.IdentityModel.Tokens;
13+
using Microsoft.IdentityModel.Telemetry;
1314
using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;
1415

1516
namespace Microsoft.IdentityModel.JsonWebTokens
1617
{
1718
/// <remarks>This partial class contains methods and logic related to the validation of tokens.</remarks>
1819
public partial class JsonWebTokenHandler : TokenHandler
1920
{
21+
internal Telemetry.ITelemetryClient _telemetryClient = new TelemetryClient();
22+
2023
/// <summary>
2124
/// Returns a value that indicates if this handler can validate a <see cref="SecurityToken"/>.
2225
/// </summary>
@@ -511,6 +514,10 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration)
511514
// where a new valid configuration was somehow published during validation time.
512515
if (currentConfiguration != null)
513516
{
517+
_telemetryClient.IncrementConfigurationRefreshRequestCounter(
518+
validationParameters.ConfigurationManager.MetadataAddress,
519+
TelemetryConstants.Protocols.Lkg);
520+
514521
validationParameters.ConfigurationManager.RequestRefresh();
515522
validationParameters.RefreshBeforeValidation = true;
516523
var lastConfig = currentConfiguration;

src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.IdentityModel.Logging;
1010
using Microsoft.IdentityModel.Protocols.Configuration;
1111
using Microsoft.IdentityModel.Tokens;
12+
using Microsoft.IdentityModel.Telemetry;
1213

1314
namespace Microsoft.IdentityModel.Protocols
1415
{
@@ -45,6 +46,7 @@ public class ConfigurationManager<T> : BaseConfigurationManager, IConfigurationM
4546
private int _configurationRetrieverState = ConfigurationRetrieverIdle;
4647

4748
private readonly TimeProvider _timeProvider = TimeProvider.System;
49+
internal ITelemetryClient TelemetryClient = new TelemetryClient();
4850

4951
// If a refresh is requested, then do the refresh as a blocking operation
5052
// not on a background thread. RequestRefresh signals that the app is explicitly
@@ -53,6 +55,7 @@ public class ConfigurationManager<T> : BaseConfigurationManager, IConfigurationM
5355
// refresh interval has passed.
5456
bool _refreshRequested;
5557

58+
5659
/// <summary>
5760
/// Instantiates a new <see cref="ConfigurationManager{T}"/> that manages automatic and controls refreshing on configuration data.
5861
/// </summary>
@@ -190,7 +193,7 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
190193
try
191194
{
192195
// Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation.
193-
// The transport should have it's own timeouts, etc.
196+
// The transport should have its own timeouts, etc.
194197
T configuration = await _configRetriever.GetConfigurationAsync(
195198
MetadataAddress,
196199
_docRetriever,
@@ -201,18 +204,29 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
201204
ConfigurationValidationResult result = _configValidator.Validate(configuration);
202205
// in this case we have never had a valid configuration, so we will throw an exception if the validation fails
203206
if (!result.Succeeded)
204-
throw LogHelper.LogExceptionMessage(
205-
new InvalidConfigurationException(
206-
LogHelper.FormatInvariant(
207-
LogMessages.IDX20810,
208-
result.ErrorMessage)));
207+
{
208+
var ex = new InvalidConfigurationException(
209+
LogHelper.FormatInvariant(
210+
LogMessages.IDX20810,
211+
result.ErrorMessage));
212+
213+
throw LogHelper.LogExceptionMessage(ex);
214+
}
209215
}
210216

217+
TelemetryClient.IncrementConfigurationRefreshRequestCounter(
218+
MetadataAddress,
219+
TelemetryConstants.Protocols.FirstRefresh);
220+
211221
UpdateConfiguration(configuration);
212222
}
213223
catch (Exception ex)
214224
{
215225
fetchMetadataFailure = ex;
226+
TelemetryClient.IncrementConfigurationRefreshRequestCounter(
227+
MetadataAddress,
228+
TelemetryConstants.Protocols.FirstRefresh,
229+
ex);
216230

217231
LogHelper.LogExceptionMessage(
218232
new InvalidOperationException(
@@ -234,11 +248,22 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
234248
{
235249
if (_refreshRequested)
236250
{
251+
// Log as manual because RequestRefresh was called
252+
TelemetryClient.IncrementConfigurationRefreshRequestCounter(
253+
MetadataAddress,
254+
TelemetryConstants.Protocols.Manual);
255+
237256
UpdateCurrentConfiguration();
238257
_refreshRequested = false;
239258
}
240259
else
260+
{
261+
TelemetryClient.IncrementConfigurationRefreshRequestCounter(
262+
MetadataAddress,
263+
TelemetryConstants.Protocols.Automatic);
264+
241265
_ = Task.Run(UpdateCurrentConfiguration, CancellationToken.None);
266+
}
242267
}
243268
}
244269

@@ -264,13 +289,20 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
264289
private void UpdateCurrentConfiguration()
265290
{
266291
#pragma warning disable CA1031 // Do not catch general exception types
292+
long startTimestamp = _timeProvider.GetTimestamp();
293+
267294
try
268295
{
269296
T configuration = _configRetriever.GetConfigurationAsync(
270297
MetadataAddress,
271298
_docRetriever,
272299
CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
273300

301+
var elapsedTime = _timeProvider.GetElapsedTime(startTimestamp);
302+
TelemetryClient.LogConfigurationRetrievalDuration(
303+
MetadataAddress,
304+
elapsedTime);
305+
274306
if (_configValidator == null)
275307
{
276308
UpdateConfiguration(configuration);
@@ -291,6 +323,12 @@ private void UpdateCurrentConfiguration()
291323
}
292324
catch (Exception ex)
293325
{
326+
var elapsedTime = _timeProvider.GetElapsedTime(startTimestamp);
327+
TelemetryClient.LogConfigurationRetrievalDuration(
328+
MetadataAddress,
329+
elapsedTime,
330+
ex);
331+
294332
LogHelper.LogExceptionMessage(
295333
new InvalidOperationException(
296334
LogHelper.FormatInvariant(
@@ -336,7 +374,7 @@ ref Unsafe.As<DateTime, long>(ref _lastRequestRefresh),
336374
/// Obtains an updated version of Configuration.
337375
/// </summary>
338376
/// <param name="cancel">CancellationToken</param>
339-
/// <returns>Configuration of type BaseConfiguration .</returns>
377+
/// <returns>Configuration of type BaseConfiguration.</returns>
340378
/// <remarks>If the time since the last call is less than <see cref="BaseConfigurationManager.AutomaticRefreshInterval"/> then <see cref="IConfigurationRetriever{T}.GetConfigurationAsync"/> is not called and the current Configuration is returned.</remarks>
341379
public override async Task<BaseConfiguration> GetBaseConfigurationAsync(CancellationToken cancel)
342380
{
@@ -353,6 +391,7 @@ public override async Task<BaseConfiguration> GetBaseConfigurationAsync(Cancella
353391
public override void RequestRefresh()
354392
{
355393
DateTime now = _timeProvider.GetUtcNow().UtcDateTime;
394+
356395
if (now >= DateTimeUtil.Add(LastRequestRefresh, RefreshInterval) || _isFirstRefreshRequest)
357396
{
358397
_isFirstRefreshRequest = false;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.TelemetryClient -> Microsoft.IdentityModel.Telemetry.ITelemetryClient
2+
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.TimeProvider -> System.TimeProvider
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]

0 commit comments

Comments
 (0)