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 25 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c5e5d91
Add request count and duration telemetry
westin-m Nov 19, 2024
c8bdbdf
Add request count and duration telemetry
westin-m Nov 19, 2024
f08864b
Merge branch 'westin/clientTelemetry' of https://github.com/AzureAD/a…
westin-m Nov 21, 2024
2a739a3
Merge branch 'dev' into westin/clientTelemetry
westin-m Nov 21, 2024
49790e3
small fix
westin-m Nov 21, 2024
ebc0740
Merge branch 'westin/clientTelemetry' of https://github.com/AzureAD/a…
westin-m Nov 21, 2024
09c3b49
Feedback, remodel timing and counters
westin-m Dec 4, 2024
57c1a90
Use interface for logging, move constants to class
westin-m Dec 5, 2024
3262cff
some interface changes, rework tests
westin-m Dec 6, 2024
4a36bdd
fix naming
westin-m Dec 6, 2024
7a1847a
address interface feedback
westin-m Dec 9, 2024
44b97fa
Interface and namespace changes
westin-m Dec 10, 2024
7b3d16d
avoid friend assemblies
westin-m Dec 10, 2024
8fd3b88
merge w main
westin-m Dec 11, 2024
5e917c9
add metadata
westin-m Dec 12, 2024
ec46da3
Merge branch 'dev' into westin/clientTelemetry
westin-m Dec 13, 2024
4011af9
doc comments for tags
westin-m Dec 14, 2024
0a6fdfb
Merge branch 'westin/clientTelemetry' of https://github.com/AzureAD/a…
westin-m Dec 14, 2024
ded6b4c
Merge branch 'dev' into westin/clientTelemetry
GeoK Dec 16, 2024
779931e
Merge branch 'dev' into westin/clientTelemetry
jennyf19 Dec 20, 2024
f71d13a
PR feedback
westin-m Jan 6, 2025
fecb571
Merge branch 'dev' into westin/clientTelemetry
westin-m Jan 6, 2025
07119c1
Merge branch 'dev' into westin/clientTelemetry
westin-m Jan 7, 2025
a7f85cf
Apply suggestions from code review
westin-m Jan 7, 2025
63c019b
change class name and add clarifying comments
westin-m Jan 7, 2025
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>6.0.2</SystemDiagnosticSourceVersion>
<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.ITelemetryClient
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 Tokens.Telemetry.ITelemetryClient _telemetryClient = new TelemetryClient();

/// <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 ITelemetryClient TelemetryClient = new TelemetryClient();

/// <summary>
/// Instantiates a new <see cref="ConfigurationManager{T}"/> that manages automatic and controls refreshing on configuration data.
/// </summary>
Expand Down Expand Up @@ -172,7 +176,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 +187,29 @@ 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));

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,6 +229,10 @@ 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);
}
}
Expand All @@ -240,13 +259,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 +293,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 +325,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 +343,10 @@ public override void RequestRefresh()
{
DateTimeOffset now = DateTimeOffset.UtcNow;

TelemetryClient.IncrementConfigurationRefreshRequestCounter(
MetadataAddress,
TelemetryConstants.Protocols.Manual);

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.ITelemetryClient
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