Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add request count and duration telemetry #3022

Open
wants to merge 22 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<MicrosoftSourceLinkGitHubVersion>1.0.0</MicrosoftSourceLinkGitHubVersion>
<NetStandardVersion>2.0.3</NetStandardVersion>
<NewtonsoftVersion>13.0.3</NewtonsoftVersion>
<SystemDiagnosticSourceVersion>8.0.1</SystemDiagnosticSourceVersion>
westin-m marked this conversation as resolved.
Show resolved Hide resolved
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
<SystemSecurityCryptographyCngVersion>4.5.0</SystemSecurityCryptographyCngVersion>
<SystemTextJson>8.0.5</SystemTextJson>
Expand Down
1 change: 1 addition & 0 deletions build/dependenciesTest.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<MicrosoftNETTestSdkVersion>17.11.1</MicrosoftNETTestSdkVersion>
<NetStandardVersion>2.0.3</NetStandardVersion>
<NewtonsoftVersion>13.0.3</NewtonsoftVersion>
<OpenTelemetryVersion>1.6.0</OpenTelemetryVersion>
<SystemNetHttp>4.3.4</SystemNetHttp>
<SystemSecurityClaimsVersion>4.3.0</SystemSecurityClaimsVersion>
<SystemTextJson>8.0.5</SystemTextJson>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler._telemetryClient -> Microsoft.IdentityModel.Tokens.Telemetry.ITelemetryInstrumentation
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.CreateToken(string payload, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string
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
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor tokenDescriptor) -> string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
using Microsoft.IdentityModel.Abstractions;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Telemetry;
using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages;

namespace Microsoft.IdentityModel.JsonWebTokens
{
/// <remarks>This partial class contains methods and logic related to the validation of tokens.</remarks>
public partial class JsonWebTokenHandler : TokenHandler
{
internal ITelemetryInstrumentation _telemetryClient = new TelemetryInstrumentation();

/// <summary>
/// Returns a value that indicates if this handler can validate a <see cref="SecurityToken"/>.
/// </summary>
Expand Down Expand Up @@ -511,6 +514,10 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration)
// where a new valid configuration was somehow published during validation time.
if (currentConfiguration != null)
{
_telemetryClient.IncrementConfigurationRefreshRequestCounter(
validationParameters.ConfigurationManager.MetadataAddress,
TelemetryConstants.Protocols.LKG);

validationParameters.ConfigurationManager.RequestRefresh();
validationParameters.RefreshBeforeValidation = true;
var lastConfig = currentConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
</None>
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework' or '$(TargetFramework)' == 'netstandard2.0' ">
Copy link
Member

@brentschmaltz brentschmaltz Dec 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reference should be in the assembly where the types are used.
Currently this is M.IM.Tokens, which includes this reference, which allows the project to build.

I think the telemetry stuff should be in M.IM.Logging.

<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticSourceVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.IdentityModel.Abstractions\Microsoft.IdentityModel.Abstractions.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Protocols.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Tokens.Telemetry;

namespace Microsoft.IdentityModel.Protocols
{
Expand Down Expand Up @@ -35,6 +36,9 @@ public class ConfigurationManager<T> : BaseConfigurationManager, IConfigurationM
private const int ConfigurationRetrieverRunning = 1;
private int _configurationRetrieverState = ConfigurationRetrieverIdle;

internal TimeProvider _timeProvider = TimeProvider.System;
internal ITelemetryInstrumentation _telemetryClient = new TelemetryInstrumentation();
westin-m marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Instantiates a new <see cref="ConfigurationManager{T}"/> that manages automatic and controls refreshing on configuration data.
/// </summary>
Expand Down Expand Up @@ -148,7 +152,9 @@ public async Task<T> GetConfigurationAsync()
public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
{
if (_currentConfiguration != null && _syncAfter > DateTimeOffset.UtcNow)
{
westin-m marked this conversation as resolved.
Show resolved Hide resolved
return _currentConfiguration;
}

Exception fetchMetadataFailure = null;

Expand All @@ -172,7 +178,7 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
try
{
// Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation.
// The transport should have it's own timeouts, etc.
// The transport should have its own timeouts, etc.
T configuration = await _configRetriever.GetConfigurationAsync(
MetadataAddress,
_docRetriever,
Expand All @@ -183,18 +189,34 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
ConfigurationValidationResult result = _configValidator.Validate(configuration);
// in this case we have never had a valid configuration, so we will throw an exception if the validation fails
if (!result.Succeeded)
throw LogHelper.LogExceptionMessage(
new InvalidConfigurationException(
LogHelper.FormatInvariant(
LogMessages.IDX20810,
result.ErrorMessage)));
{
var ex = new InvalidConfigurationException(
LogHelper.FormatInvariant(
LogMessages.IDX20810,
result.ErrorMessage));

_telemetryClient.IncrementConfigurationRefreshRequestCounter(
westin-m marked this conversation as resolved.
Show resolved Hide resolved
MetadataAddress,
TelemetryConstants.Protocols.FirstRefresh,
ex);

throw LogHelper.LogExceptionMessage(ex);
}
}

_telemetryClient.IncrementConfigurationRefreshRequestCounter(
MetadataAddress,
TelemetryConstants.Protocols.FirstRefresh);

UpdateConfiguration(configuration);
}
catch (Exception ex)
{
fetchMetadataFailure = ex;
_telemetryClient.IncrementConfigurationRefreshRequestCounter(
MetadataAddress,
TelemetryConstants.Protocols.FirstRefresh,
westin-m marked this conversation as resolved.
Show resolved Hide resolved
ex);

LogHelper.LogExceptionMessage(
new InvalidOperationException(
Expand All @@ -214,13 +236,19 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
{
if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverRunning, ConfigurationRetrieverIdle) == ConfigurationRetrieverIdle)
{
_telemetryClient.IncrementConfigurationRefreshRequestCounter(
MetadataAddress,
TelemetryConstants.Protocols.Automatic);

_ = Task.Run(UpdateCurrentConfiguration, CancellationToken.None);
}
}

// If metadata exists return it.
if (_currentConfiguration != null)
{
westin-m marked this conversation as resolved.
Show resolved Hide resolved
return _currentConfiguration;
}

throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
Expand All @@ -240,13 +268,20 @@ public virtual async Task<T> GetConfigurationAsync(CancellationToken cancel)
private void UpdateCurrentConfiguration()
{
#pragma warning disable CA1031 // Do not catch general exception types
long startTimestamp = _timeProvider.GetTimestamp();

try
{
T configuration = _configRetriever.GetConfigurationAsync(
MetadataAddress,
_docRetriever,
CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();

var elapsedTime = _timeProvider.GetElapsedTime(startTimestamp);
_telemetryClient.LogConfigurationRetrievalDuration(
MetadataAddress,
elapsedTime);

if (_configValidator == null)
{
UpdateConfiguration(configuration);
Expand All @@ -267,6 +302,12 @@ private void UpdateCurrentConfiguration()
}
catch (Exception ex)
{
var elapsedTime = _timeProvider.GetElapsedTime(startTimestamp);
_telemetryClient.LogConfigurationRetrievalDuration(
MetadataAddress,
elapsedTime,
ex);

LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(
Expand All @@ -293,7 +334,7 @@ private void UpdateConfiguration(T configuration)
/// Obtains an updated version of Configuration.
/// </summary>
/// <param name="cancel">CancellationToken</param>
/// <returns>Configuration of type BaseConfiguration .</returns>
/// <returns>Configuration of type BaseConfiguration.</returns>
/// <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>
public override async Task<BaseConfiguration> GetBaseConfigurationAsync(CancellationToken cancel)
{
Expand All @@ -311,6 +352,10 @@ public override void RequestRefresh()
{
DateTimeOffset now = DateTimeOffset.UtcNow;

_telemetryClient.IncrementConfigurationRefreshRequestCounter(
MetadataAddress,
TelemetryConstants.Protocols.Direct);
westin-m marked this conversation as resolved.
Show resolved Hide resolved

if (now >= DateTimeUtil.Add(_lastRequestRefresh.UtcDateTime, RefreshInterval) || _isFirstRefreshRequest)
{
_isFirstRefreshRequest = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>._telemetryClient -> Microsoft.IdentityModel.Tokens.Telemetry.ITelemetryInstrumentation
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>._timeProvider -> System.TimeProvider
4 changes: 4 additions & 0 deletions src/Microsoft.IdentityModel.Protocols/InternalsVisibleTo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
Loading
Loading