Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 13 additions & 7 deletions src/client/Microsoft.Identity.Client/Http/HttpManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,21 @@ private async Task<HttpResponse> ExecuteAsync(

HttpClient client = GetHttpClient(bindingCertificate, validateServerCert);

using (HttpResponseMessage responseMessage =
await client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false))
try
Comment thread
neha-bhargava marked this conversation as resolved.
{
LastRequestDurationInMs = sw.ElapsedMilliseconds;
logger.Verbose(() => $"[HttpManager] Received response. Status code: {responseMessage.StatusCode}. ");
using (HttpResponseMessage responseMessage =
await client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false))
{
logger.Verbose(() => $"[HttpManager] Received response. Status code: {responseMessage.StatusCode}. ");

HttpResponse returnValue = await CreateResponseAsync(responseMessage).ConfigureAwait(false);
returnValue.UserAgent = requestMessage.Headers.UserAgent.ToString();
return returnValue;
HttpResponse returnValue = await CreateResponseAsync(responseMessage).ConfigureAwait(false);
returnValue.UserAgent = requestMessage.Headers.UserAgent.ToString();
return returnValue;
}
}
finally
{
LastRequestDurationInMs = sw.ElapsedMilliseconds;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
AuthenticationRequestParameters.RequestContext.ApiEvent = apiEvent;
});

var requestStopwatch = Stopwatch.StartNew();
try
{
AuthenticationResult authenticationResult = null;
Expand Down Expand Up @@ -112,8 +113,14 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
}
AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(ex);

int httpStatusCode = ex is MsalServiceException serviceEx ? serviceEx.StatusCode : 0;

LogFailureTelemetryToOtel(
ex.ErrorCode, apiEvent, apiEvent.CacheInfo,
ex.ErrorCode,
apiEvent,
apiEvent.CacheInfo,
httpStatusCode,
requestStopwatch.ElapsedMilliseconds + measureTelemetryDurationResult.Milliseconds,
(ex as MsalServiceException)?.ErrorCodes?.FirstOrDefault());
throw;
}
Expand All @@ -122,7 +129,7 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
apiEvent.ApiErrorCode = ex.GetType().Name;
AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(ex);

LogFailureTelemetryToOtel(ex.GetType().Name, apiEvent, apiEvent.CacheInfo);
LogFailureTelemetryToOtel(ex.GetType().Name, apiEvent, apiEvent.CacheInfo, httpStatusCode: 0, totalDurationInMs: requestStopwatch.ElapsedMilliseconds + measureTelemetryDurationResult.Milliseconds);
Comment thread
ssmelov marked this conversation as resolved.
throw;
}
}
Expand All @@ -144,17 +151,18 @@ private void LogSuccessTelemetryToOtel(AuthenticationResult authenticationResult
authenticationResult.ExpiresOn);
}

private void LogFailureTelemetryToOtel(string errorCodeToLog, ApiEvent apiEvent, CacheRefreshReason cacheRefreshReason, string rawStsErrorCode = null)
private void LogFailureTelemetryToOtel(string errorCodeToLog, ApiEvent apiEvent, CacheRefreshReason cacheRefreshReason, int httpStatusCode, long totalDurationInMs, string rawStsErrorCode = null)
{
// Log metrics
ServiceBundle.PlatformProxy.OtelInstrumentation.LogFailureMetrics(
ServiceBundle.PlatformProxy.GetProductName(),
errorCodeToLog,
apiEvent.ApiId,
apiEvent,
apiEvent.CallerSdkApiId,
apiEvent.CallerSdkVersion,
cacheRefreshReason,
apiEvent.TokenType,
httpStatusCode,
totalDurationInMs,
rawStsErrorCode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ internal static void ProcessFetchInBackground(
apiEvent.ApiId,
callerSdkId,
callerSdkVersion,
TokenSource.IdentityProvider,
CacheRefreshReason.ProactivelyRefreshed,
TokenSource.IdentityProvider,
CacheRefreshReason.ProactivelyRefreshed,
Cache.CacheLevel.None,
logger,
apiEvent.TokenType);
Expand All @@ -114,6 +114,11 @@ internal static void ProcessFetchInBackground(
CacheRefreshReason.ProactivelyRefreshed,
apiEvent.TokenType,
authResult.ExpiresOn);

serviceBundle.PlatformProxy.OtelInstrumentation.LogSuccessHttpDuration(
serviceBundle.PlatformProxy.GetProductName(),
apiEvent.ApiId,
authResult.AuthenticationResultMetadata);
}
catch (MsalServiceException ex)
{
Expand All @@ -127,43 +132,48 @@ internal static void ProcessFetchInBackground(
logger.ErrorPiiWithPrefix(ex, logMsg);
}

serviceBundle.PlatformProxy.OtelInstrumentation.LogFailureMetrics(
serviceBundle.PlatformProxy.GetProductName(),
ex.ErrorCode,
apiEvent.ApiId,
callerSdkId,
callerSdkVersion,
CacheRefreshReason.ProactivelyRefreshed,
apiEvent.TokenType,
ex.ErrorCodes?.FirstOrDefault());
LogBackgroundFailureTelemetry(serviceBundle, apiEvent, callerSdkId, callerSdkVersion,
ex.ErrorCode, ex.StatusCode, ex.ErrorCodes?.FirstOrDefault());
}
catch (OperationCanceledException ex)
{
logger.WarningPiiWithPrefix(ex, ProactiveRefreshCancellationError);
serviceBundle.PlatformProxy.OtelInstrumentation.LogFailureMetrics(
serviceBundle.PlatformProxy.GetProductName(),
ex.GetType().Name,
apiEvent.ApiId,
callerSdkId,
callerSdkVersion,
CacheRefreshReason.ProactivelyRefreshed,
apiEvent.TokenType);
LogBackgroundFailureTelemetry(serviceBundle, apiEvent, callerSdkId, callerSdkVersion,
ex.GetType().Name, httpStatusCode: 0);
}
catch (Exception ex)
{
logger.ErrorPiiWithPrefix(ex, ProactiveRefreshGeneralError);
serviceBundle.PlatformProxy.OtelInstrumentation.LogFailureMetrics(
serviceBundle.PlatformProxy.GetProductName(),
ex.GetType().Name,
apiEvent.ApiId,
callerSdkId,
callerSdkVersion,
CacheRefreshReason.ProactivelyRefreshed,
apiEvent.TokenType);
LogBackgroundFailureTelemetry(serviceBundle, apiEvent, callerSdkId, callerSdkVersion,
ex.GetType().Name, httpStatusCode: 0);
}
});
}

// Records telemetry for a fire-and-forget background refresh failure: increments the
// failure counter and records V2 HTTP duration when an HTTP exchange happened.
// Total duration is deliberately not recorded — the foreground user already received
// their token from cache, so this latency is not user-facing.
private static void LogBackgroundFailureTelemetry(
IServiceBundle serviceBundle,
ApiEvent apiEvent,
string callerSdkId,
string callerSdkVersion,
string errorCode,
int httpStatusCode,
string rawStsErrorCode = null)
{
var otel = serviceBundle.PlatformProxy.OtelInstrumentation;
var platform = serviceBundle.PlatformProxy.GetProductName();

otel.IncrementFailureCounter(
platform, errorCode, apiEvent.ApiId, callerSdkId, callerSdkVersion,
CacheRefreshReason.ProactivelyRefreshed, apiEvent.TokenType, rawStsErrorCode);

otel.LogFailureHttpDuration(
platform, apiEvent, httpStatusCode);
}

private static Random s_random = new Random();
private static DateTimeOffset? GetRefreshOnWithJitter(MsalAccessTokenCacheItem msalAccessTokenCacheItem)
{
Expand Down
5 changes: 5 additions & 0 deletions src/client/Microsoft.Identity.Client/OAuth2/OAuth2Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ internal async Task<T> ExecuteRequestAsync<T>(
}
catch (Exception ex)
{
if (requestContext.ApiEvent != null)
{
requestContext.ApiEvent.DurationInHttpInMs += _httpManager.LastRequestDurationInMs;
Comment thread
ssmelov marked this conversation as resolved.
}

if (ex is TaskCanceledException && requestContext.UserCancellationToken.IsCancellationRequested)
{
throw;
Expand Down
Loading
Loading