Skip to content

Commit 0cd8e32

Browse files
Add raw STS error code to MsalFailure metric (#5961)
* Add raw STS error code to MsalFailure metric --------- Co-authored-by: Neha Bhargava <61847233+neha-bhargava@users.noreply.github.com>
1 parent 54c602e commit 0cd8e32

7 files changed

Lines changed: 111 additions & 25 deletions

File tree

src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ public async Task<AuthenticationResult> RunAsync(CancellationToken cancellationT
106106
}
107107
AuthenticationRequestParameters.RequestContext.Logger.ErrorPii(ex);
108108

109-
LogFailureTelemetryToOtel(ex.ErrorCode, apiEvent, apiEvent.CacheInfo);
109+
LogFailureTelemetryToOtel(
110+
ex.ErrorCode, apiEvent, apiEvent.CacheInfo,
111+
(ex as MsalServiceException)?.ErrorCodes?.FirstOrDefault());
110112
throw;
111113
}
112114
catch (Exception ex)
@@ -133,7 +135,7 @@ private void LogSuccessTelemetryToOtel(AuthenticationResult authenticationResult
133135
AuthenticationRequestParameters.RequestContext.Logger);
134136
}
135137

136-
private void LogFailureTelemetryToOtel(string errorCodeToLog, ApiEvent apiEvent, CacheRefreshReason cacheRefreshReason)
138+
private void LogFailureTelemetryToOtel(string errorCodeToLog, ApiEvent apiEvent, CacheRefreshReason cacheRefreshReason, string rawStsErrorCode = null)
137139
{
138140
// Log metrics
139141
ServiceBundle.PlatformProxy.OtelInstrumentation.LogFailureMetrics(
@@ -143,7 +145,8 @@ private void LogFailureTelemetryToOtel(string errorCodeToLog, ApiEvent apiEvent,
143145
apiEvent.CallerSdkApiId,
144146
apiEvent.CallerSdkVersion,
145147
cacheRefreshReason,
146-
apiEvent.TokenType);
148+
apiEvent.TokenType,
149+
rawStsErrorCode);
147150
}
148151

149152
private Tuple<string, string> ParseScopesForTelemetry()

src/client/Microsoft.Identity.Client/Internal/Requests/SilentRequestHelper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using System.Threading;
78
using System.Threading.Tasks;
89
using Microsoft.Identity.Client.Cache.Items;
@@ -124,7 +125,8 @@ internal static void ProcessFetchInBackground(
124125
callerSdkId,
125126
callerSdkVersion,
126127
CacheRefreshReason.ProactivelyRefreshed,
127-
apiEvent.TokenType);
128+
apiEvent.TokenType,
129+
ex.ErrorCodes?.FirstOrDefault());
128130
}
129131
catch (OperationCanceledException ex)
130132
{

src/client/Microsoft.Identity.Client/Platforms/Features/OpenTelemetry/OtelInstrumentation.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33
using System;
4+
using System.Diagnostics;
45
using System.Diagnostics.Metrics;
56
using Microsoft.Identity.Client.Cache;
67
using Microsoft.Identity.Client.Core;
@@ -204,19 +205,27 @@ public void LogFailureMetrics(string platform,
204205
string callerSdkId,
205206
string callerSdkVersion,
206207
CacheRefreshReason cacheRefreshReason,
207-
int tokenType)
208+
int tokenType,
209+
string rawStsErrorCode = null)
208210
{
209-
if (s_failureCounter.Value.Enabled)
211+
if (!s_failureCounter.Value.Enabled)
212+
return;
213+
214+
var tags = new TagList
210215
{
211-
s_failureCounter.Value.Add(1,
212-
new(TelemetryConstants.MsalVersion, MsalIdHelper.GetMsalVersion()),
213-
new(TelemetryConstants.Platform, platform),
214-
new(TelemetryConstants.ErrorCode, errorCode),
215-
new(TelemetryConstants.ApiId, apiId),
216-
new(TelemetryConstants.CallerSdkId, callerSdkId ?? string.Empty + "," + callerSdkVersion ?? string.Empty),
217-
new(TelemetryConstants.CacheRefreshReason, cacheRefreshReason),
218-
new(TelemetryConstants.TokenType, tokenType));
219-
}
216+
{ TelemetryConstants.MsalVersion, MsalIdHelper.GetMsalVersion() },
217+
{ TelemetryConstants.Platform, platform },
218+
{ TelemetryConstants.ErrorCode, errorCode },
219+
{ TelemetryConstants.ApiId, apiId },
220+
{ TelemetryConstants.CallerSdkId, callerSdkId ?? string.Empty + "," + callerSdkVersion ?? string.Empty },
221+
{ TelemetryConstants.CacheRefreshReason, cacheRefreshReason },
222+
{ TelemetryConstants.TokenType, tokenType }
223+
};
224+
225+
if (!string.IsNullOrEmpty(rawStsErrorCode))
226+
tags.Add(TelemetryConstants.RawStsErrorCode, rawStsErrorCode);
227+
228+
s_failureCounter.Value.Add(1, in tags);
220229
}
221230
}
222231
}

src/client/Microsoft.Identity.Client/TelemetryCore/OpenTelemetry/IOtelInstrumentation.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ internal void IncrementSuccessCounter(string platform,
3131
ILoggerAdapter logger,
3232
int TokenType);
3333

34-
internal void LogFailureMetrics(string platform,
35-
string errorCode,
34+
internal void LogFailureMetrics(string platform,
35+
string errorCode,
3636
ApiEvent.ApiIds apiId,
3737
string callerSdkId,
3838
string callerSdkVersion,
3939
CacheRefreshReason cacheRefreshReason,
40-
int tokenType);
40+
int tokenType,
41+
string rawStsErrorCode = null);
4142
}
4243
}

src/client/Microsoft.Identity.Client/TelemetryCore/OpenTelemetry/NullOtelInstrumentation.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ public void LogSuccessMetrics(
2727
// No op
2828
}
2929

30-
public void LogFailureMetrics(string platform,
31-
string errorCode,
32-
ApiEvent.ApiIds apiId,
30+
public void LogFailureMetrics(string platform,
31+
string errorCode,
32+
ApiEvent.ApiIds apiId,
3333
string callerSdkId,
3434
string callerSdkVersion,
3535
CacheRefreshReason cacheRefreshReason,
36-
int tokenType)
36+
int tokenType,
37+
string rawStsErrorCode = null)
3738
{
3839
// No op
3940
}

src/client/Microsoft.Identity.Client/TelemetryCore/TelemetryConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal static class TelemetryConstants
2626
public const string CacheInfoTelemetry = "CacheInfoTelemetry";
2727
public const string CacheRefreshReason = "CacheRefreshReason";
2828
public const string ErrorCode = "ErrorCode";
29-
public const string StsErrorCode = "StsErrorCode";
29+
public const string RawStsErrorCode = "RawStsErrorCode";
3030
public const string ErrorMessage = "ErrorMessage";
3131
public const string Duration = "Duration";
3232
public const string DurationInUs = "DurationInUs";

tests/Microsoft.Identity.Test.Unit/TelemetryTests/OTelInstrumentationTests.cs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,74 @@ private void CreateApplication()
415415
.BuildConcrete();
416416
}
417417

418-
private void VerifyMetrics(int expectedMetricCount, List<Metric> exportedMetrics,
418+
[TestMethod]
419+
public async Task MsalFailure_ServiceException_RawStsErrorCodeTag_IncludedAsync()
420+
{
421+
using (_harness = CreateTestHarness())
422+
{
423+
CreateApplication();
424+
425+
_harness.HttpManager.AddInstanceDiscoveryMockHandler();
426+
_harness.HttpManager.AddTokenResponse(TokenResponseType.InvalidClient);
427+
428+
MsalServiceException ex = await AssertException.TaskThrowsAsync<MsalServiceException>(
429+
() => _cca.AcquireTokenForClient(TestConstants.s_scopeForAnotherResource)
430+
.WithExtraQueryParameters(extraQueryParams)
431+
.WithTenantId(TestConstants.Utid)
432+
.ExecuteAsync(CancellationToken.None)).ConfigureAwait(false);
433+
434+
Assert.IsNotNull(ex.ErrorCodes, "ErrorCodes should be populated from IDP response.");
435+
436+
s_meterProvider.ForceFlush();
437+
438+
var failureMetric = _exportedMetrics.First(m => m.Name == "MsalFailure");
439+
foreach (var metricPoint in failureMetric.GetMetricPoints())
440+
{
441+
var tags = GetTagDictionary(metricPoint.Tags);
442+
Assert.IsTrue(tags.ContainsKey(TelemetryConstants.RawStsErrorCode),
443+
"RawStsErrorCode tag should be present when the IDP response contains error_codes.");
444+
Assert.AreEqual(ex.ErrorCodes.FirstOrDefault(), tags[TelemetryConstants.RawStsErrorCode]);
445+
}
446+
}
447+
}
448+
449+
[TestMethod]
450+
public async Task MsalFailure_ClientException_RawStsErrorCodeTag_NotIncludedAsync()
451+
{
452+
using (_harness = CreateTestHarness())
453+
{
454+
CreateApplication();
455+
456+
// Null scope triggers MsalClientException before any HTTP call — no ErrorCodes
457+
MsalClientException ex = await AssertException.TaskThrowsAsync<MsalClientException>(
458+
() => _cca.AcquireTokenForClient(null)
459+
.WithExtraQueryParameters(extraQueryParams)
460+
.WithTenantId(TestConstants.Utid)
461+
.ExecuteAsync(CancellationToken.None)).ConfigureAwait(false);
462+
463+
Assert.IsNotNull(ex);
464+
465+
s_meterProvider.ForceFlush();
466+
467+
var failureMetric = _exportedMetrics.First(m => m.Name == "MsalFailure");
468+
foreach (var metricPoint in failureMetric.GetMetricPoints())
469+
{
470+
var tags = GetTagDictionary(metricPoint.Tags);
471+
Assert.IsFalse(tags.ContainsKey(TelemetryConstants.RawStsErrorCode),
472+
"RawStsErrorCode tag should not be present for non-service exceptions.");
473+
}
474+
}
475+
}
476+
477+
private static IDictionary<string, object> GetTagDictionary(ReadOnlyTagCollection tags)
478+
{
479+
var dict = new Dictionary<string, object>();
480+
foreach (var tag in tags)
481+
dict[tag.Key] = tag.Value;
482+
return dict;
483+
}
484+
485+
private void VerifyMetrics(int expectedMetricCount, List<Metric> exportedMetrics,
419486
long expectedSuccessfulRequests, long expectedFailedRequests)
420487
{
421488
Assert.HasCount(expectedMetricCount, exportedMetrics, "Count of metrics recorded is not as expected.");
@@ -467,7 +534,10 @@ private void VerifyMetrics(int expectedMetricCount, List<Metric> exportedMetrics
467534
foreach (var metricPoint in exportedItem.GetMetricPoints())
468535
{
469536
totalFailedRequests += metricPoint.GetSumLong();
470-
AssertTags(metricPoint.Tags, expectedTags, true);
537+
var pointExpectedTags = new List<string>(expectedTags);
538+
if (GetTagDictionary(metricPoint.Tags).ContainsKey(TelemetryConstants.RawStsErrorCode))
539+
pointExpectedTags.Add(TelemetryConstants.RawStsErrorCode);
540+
AssertTags(metricPoint.Tags, pointExpectedTags, true);
471541
}
472542

473543
Assert.AreEqual(expectedFailedRequests, totalFailedRequests);

0 commit comments

Comments
 (0)