Skip to content

Commit 1f0c98b

Browse files
authored
Identity metrics clean up (#62671)
1 parent b9f43c1 commit 1f0c98b

File tree

3 files changed

+45
-43
lines changed

3 files changed

+45
-43
lines changed

src/Identity/Core/src/SignInManagerMetrics.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ internal sealed class SignInManagerMetrics : IDisposable
1414
public const string RememberTwoFactorCounterName = "aspnetcore.identity.sign_in.remember_two_factor";
1515
public const string ForgetTwoFactorCounterName = "aspnetcore.identity.sign_in.forget_two_factor";
1616
public const string CheckPasswordCounterName = "aspnetcore.identity.sign_in.check_password";
17-
public const string SignInUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_in_principal";
18-
public const string SignOutUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_out_principal";
17+
public const string SignInUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_in";
18+
public const string SignOutUserPrincipalCounterName = "aspnetcore.identity.sign_in.sign_out";
1919

2020
private readonly Meter _meter;
2121
private readonly Counter<long> _authenticateCounter;
@@ -29,12 +29,12 @@ public SignInManagerMetrics(IMeterFactory meterFactory)
2929
{
3030
_meter = meterFactory.Create(MeterName);
3131

32-
_authenticateCounter = _meter.CreateCounter<long>(AuthenticateCounterName, "count", "The number of authenticate attempts. The authenticate counter is incremented by sign in methods such as PasswordSignInAsync and TwoFactorSignInAsync.");
33-
_rememberTwoFactorClientCounter = _meter.CreateCounter<long>(RememberTwoFactorCounterName, "count", "The number of two factor clients remembered.");
34-
_forgetTwoFactorCounter = _meter.CreateCounter<long>(ForgetTwoFactorCounterName, "count", "The number of two factor clients forgotten.");
35-
_checkPasswordCounter = _meter.CreateCounter<long>(CheckPasswordCounterName, "count", "The number of check password attempts. Checks that the account is in a state that can log in and that the password is valid using the UserManager.CheckPasswordAsync method.");
36-
_signInUserPrincipalCounter = _meter.CreateCounter<long>(SignInUserPrincipalCounterName, "count", "The number of calls to sign in user principals.");
37-
_signOutUserPrincipalCounter = _meter.CreateCounter<long>(SignOutUserPrincipalCounterName, "count", "The number of calls to sign out user principals.");
32+
_authenticateCounter = _meter.CreateCounter<long>(AuthenticateCounterName, "{count}", "The number of authenticate attempts. The authenticate counter is incremented by sign in methods such as PasswordSignInAsync and TwoFactorSignInAsync.");
33+
_rememberTwoFactorClientCounter = _meter.CreateCounter<long>(RememberTwoFactorCounterName, "{count}", "The number of two factor clients remembered.");
34+
_forgetTwoFactorCounter = _meter.CreateCounter<long>(ForgetTwoFactorCounterName, "{count}", "The number of two factor clients forgotten.");
35+
_checkPasswordCounter = _meter.CreateCounter<long>(CheckPasswordCounterName, "{check}", "The number of check password attempts. Checks that the account is in a state that can log in and that the password is valid using the UserManager.CheckPasswordAsync method.");
36+
_signInUserPrincipalCounter = _meter.CreateCounter<long>(SignInUserPrincipalCounterName, "{sign_in}", "The number of calls to sign in user principals.");
37+
_signOutUserPrincipalCounter = _meter.CreateCounter<long>(SignOutUserPrincipalCounterName, "{sign_out}", "The number of calls to sign out user principals.");
3838
}
3939

4040
internal void CheckPasswordSignIn(string userType, SignInResult? result, Exception? exception = null)
@@ -49,7 +49,7 @@ internal void CheckPasswordSignIn(string userType, SignInResult? result, Excepti
4949
{ "aspnetcore.identity.user_type", userType },
5050
};
5151
AddSignInResult(ref tags, result);
52-
AddExceptionTags(ref tags, exception);
52+
AddErrorTag(ref tags, exception);
5353

5454
_checkPasswordCounter.Add(1, tags);
5555
}
@@ -69,7 +69,7 @@ internal void AuthenticateSignIn(string userType, string authenticationScheme, S
6969
};
7070
AddIsPersistent(ref tags, isPersistent);
7171
AddSignInResult(ref tags, result);
72-
AddExceptionTags(ref tags, exception);
72+
AddErrorTag(ref tags, exception);
7373

7474
_authenticateCounter.Add(1, tags);
7575
}
@@ -87,7 +87,7 @@ internal void SignInUserPrincipal(string userType, string authenticationScheme,
8787
{ "aspnetcore.identity.authentication_scheme", authenticationScheme },
8888
};
8989
AddIsPersistent(ref tags, isPersistent);
90-
AddExceptionTags(ref tags, exception);
90+
AddErrorTag(ref tags, exception);
9191

9292
_signInUserPrincipalCounter.Add(1, tags);
9393
}
@@ -104,7 +104,7 @@ internal void SignOutUserPrincipal(string userType, string authenticationScheme,
104104
{ "aspnetcore.identity.user_type", userType },
105105
{ "aspnetcore.identity.authentication_scheme", authenticationScheme },
106106
};
107-
AddExceptionTags(ref tags, exception);
107+
AddErrorTag(ref tags, exception);
108108

109109
_signOutUserPrincipalCounter.Add(1, tags);
110110
}
@@ -121,7 +121,7 @@ internal void RememberTwoFactorClient(string userType, string authenticationSche
121121
{ "aspnetcore.identity.user_type", userType },
122122
{ "aspnetcore.identity.authentication_scheme", authenticationScheme }
123123
};
124-
AddExceptionTags(ref tags, exception);
124+
AddErrorTag(ref tags, exception);
125125

126126
_rememberTwoFactorClientCounter.Add(1, tags);
127127
}
@@ -138,7 +138,7 @@ internal void ForgetTwoFactorClient(string userType, string authenticationScheme
138138
{ "aspnetcore.identity.user_type", userType },
139139
{ "aspnetcore.identity.authentication_scheme", authenticationScheme }
140140
};
141-
AddExceptionTags(ref tags, exception);
141+
AddErrorTag(ref tags, exception);
142142

143143
_forgetTwoFactorCounter.Add(1, tags);
144144
}
@@ -164,7 +164,7 @@ private static void AddSignInResult(ref TagList tags, SignInResult? result)
164164
}
165165
}
166166

167-
private static void AddExceptionTags(ref TagList tags, Exception? exception)
167+
private static void AddErrorTag(ref TagList tags, Exception? exception)
168168
{
169169
if (exception != null)
170170
{
@@ -182,7 +182,7 @@ private static string GetSignInType(SignInType signInType)
182182
SignInType.TwoFactor => "two_factor",
183183
SignInType.External => "external",
184184
SignInType.Passkey => "passkey",
185-
_ => "_UNKNOWN"
185+
_ => "_OTHER"
186186
};
187187
}
188188

src/Identity/Extensions.Core/src/UserManagerMetrics.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ internal sealed class UserManagerMetrics : IDisposable
3232
public UserManagerMetrics(IMeterFactory meterFactory)
3333
{
3434
_meter = meterFactory.Create(MeterName);
35-
_createCounter = _meter.CreateCounter<long>(CreateCounterName, "count", "The number of users created.");
36-
_updateCounter = _meter.CreateCounter<long>(UpdateCounterName, "count", "The number of user updates.");
37-
_deleteCounter = _meter.CreateCounter<long>(DeleteCounterName, "count", "The number of users deleted.");
38-
_checkPasswordCounter = _meter.CreateCounter<long>(CheckPasswordCounterName, "count", "The number of check password attempts. Only checks whether the password is valid and not whether the user account is in a state that can log in.");
39-
_verifyTokenCounter = _meter.CreateCounter<long>(VerifyTokenCounterName, "count", "The number of token verification attempts.");
40-
_generateTokenCounter = _meter.CreateCounter<long>(GenerateTokenCounterName, "count", "The number of token generation attempts.");
35+
_createCounter = _meter.CreateCounter<long>(CreateCounterName, "{user}", "The number of users created.");
36+
_updateCounter = _meter.CreateCounter<long>(UpdateCounterName, "{user}", "The number of users updated.");
37+
_deleteCounter = _meter.CreateCounter<long>(DeleteCounterName, "{user}", "The number of users deleted.");
38+
_checkPasswordCounter = _meter.CreateCounter<long>(CheckPasswordCounterName, "{check}", "The number of check password attempts. Only checks whether the password is valid and not whether the user account is in a state that can log in.");
39+
_verifyTokenCounter = _meter.CreateCounter<long>(VerifyTokenCounterName, "{count}", "The number of token verification attempts.");
40+
_generateTokenCounter = _meter.CreateCounter<long>(GenerateTokenCounterName, "{count}", "The number of token generation attempts.");
4141
}
4242

4343
internal void CreateUser(string userType, IdentityResult? result, Exception? exception = null)
@@ -52,7 +52,7 @@ internal void CreateUser(string userType, IdentityResult? result, Exception? exc
5252
{ "aspnetcore.identity.user_type", userType }
5353
};
5454
AddIdentityResultTags(ref tags, result);
55-
AddExceptionTags(ref tags, exception);
55+
AddErrorTag(ref tags, exception, result: result);
5656

5757
_createCounter.Add(1, tags);
5858
}
@@ -70,7 +70,7 @@ internal void UpdateUser(string userType, IdentityResult? result, UserUpdateType
7070
{ "aspnetcore.identity.user.update_type", GetUpdateType(updateType) },
7171
};
7272
AddIdentityResultTags(ref tags, result);
73-
AddExceptionTags(ref tags, exception);
73+
AddErrorTag(ref tags, exception, result: result);
7474

7575
_updateCounter.Add(1, tags);
7676
}
@@ -87,7 +87,7 @@ internal void DeleteUser(string userType, IdentityResult? result, Exception? exc
8787
{ "aspnetcore.identity.user_type", userType }
8888
};
8989
AddIdentityResultTags(ref tags, result);
90-
AddExceptionTags(ref tags, exception);
90+
AddErrorTag(ref tags, exception, result: result);
9191

9292
_deleteCounter.Add(1, tags);
9393
}
@@ -105,9 +105,9 @@ internal void CheckPassword(string userType, bool? userMissing, PasswordVerifica
105105
};
106106
if (userMissing != null || result != null)
107107
{
108-
tags.Add("aspnetcore.identity.user.password_result", GetPasswordResult(result, passwordMissing: null, userMissing));
108+
tags.Add("aspnetcore.identity.password_check_result", GetPasswordResult(result, passwordMissing: null, userMissing));
109109
}
110-
AddExceptionTags(ref tags, exception);
110+
AddErrorTag(ref tags, exception);
111111

112112
_checkPasswordCounter.Add(1, tags);
113113
}
@@ -128,7 +128,7 @@ internal void VerifyToken(string userType, bool? result, string purpose, Excepti
128128
{
129129
tags.Add("aspnetcore.identity.token_verified", result == true ? "success" : "failure");
130130
}
131-
AddExceptionTags(ref tags, exception);
131+
AddErrorTag(ref tags, exception);
132132

133133
_verifyTokenCounter.Add(1, tags);
134134
}
@@ -145,14 +145,14 @@ internal void GenerateToken(string userType, string purpose, Exception? exceptio
145145
{ "aspnetcore.identity.user_type", userType },
146146
{ "aspnetcore.identity.token_purpose", GetTokenPurpose(purpose) },
147147
};
148-
AddExceptionTags(ref tags, exception);
148+
AddErrorTag(ref tags, exception);
149149

150150
_generateTokenCounter.Add(1, tags);
151151
}
152152

153153
private static string GetTokenPurpose(string purpose)
154154
{
155-
// Purpose could be any value and can't be used as a tag value. However, there are known purposes
155+
// Purpose could be any value and can't be used directly as a tag value. However, there are known purposes
156156
// on UserManager that we can detect and use as a tag value. Some could have a ':' in them followed by user data.
157157
// We need to trim them to content before ':' and then match to known values.
158158
ReadOnlySpan<char> trimmedPurpose = purpose;
@@ -161,15 +161,16 @@ private static string GetTokenPurpose(string purpose)
161161
{
162162
trimmedPurpose = purpose.AsSpan(0, colonIndex);
163163
}
164-
164+
165+
// These are known purposes that are specified in ASP.NET Core Identity.
165166
return trimmedPurpose switch
166167
{
167168
"ResetPassword" => "reset_password",
168169
"ChangePhoneNumber" => "change_phone_number",
169170
"EmailConfirmation" => "email_confirmation",
170171
"ChangeEmail" => "change_email",
171172
"TwoFactor" => "two_factor",
172-
_ => "_UNKNOWN"
173+
_ => "_OTHER"
173174
};
174175
}
175176

@@ -183,15 +184,16 @@ private static void AddIdentityResultTags(ref TagList tags, IdentityResult? resu
183184
tags.Add("aspnetcore.identity.result", result.Succeeded ? "success" : "failure");
184185
if (!result.Succeeded && result.Errors.FirstOrDefault()?.Code is { Length: > 0 } code)
185186
{
186-
tags.Add("aspnetcore.identity.result_error_code", code);
187+
tags.Add("aspnetcore.identity.error_code", code);
187188
}
188189
}
189190

190-
private static void AddExceptionTags(ref TagList tags, Exception? exception)
191+
private static void AddErrorTag(ref TagList tags, Exception? exception, IdentityResult? result = null)
191192
{
192-
if (exception != null)
193+
var value = exception?.GetType().FullName ?? result?.Errors.FirstOrDefault()?.Code;
194+
if (value != null)
193195
{
194-
tags.Add("error.type", exception.GetType().FullName!);
196+
tags.Add("error.type", value);
195197
}
196198
}
197199

@@ -204,7 +206,7 @@ private static string GetPasswordResult(PasswordVerificationResult? result, bool
204206
(PasswordVerificationResult.Failed, false, false) => "failure",
205207
(null, true, false) => "password_missing",
206208
(null, false, true) => "user_missing",
207-
_ => "_UNKNOWN"
209+
_ => "_OTHER"
208210
};
209211
}
210212

@@ -244,7 +246,7 @@ private static string GetUpdateType(UserUpdateType updateType)
244246
UserUpdateType.RedeemTwoFactorRecoveryCode => "redeem_two_factor_recovery_code",
245247
UserUpdateType.SetPasskey => "set_passkey",
246248
UserUpdateType.RemovePasskey => "remove_passkey",
247-
_ => "_UNKNOWN"
249+
_ => "_OTHER"
248250
};
249251
}
250252

src/Identity/test/Identity.Test/UserManagerTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public async Task DeleteCallsStore_Failure()
174174
[
175175
KeyValuePair.Create<string, object>("aspnetcore.identity.user_type", "Microsoft.AspNetCore.Identity.Test.PocoUser"),
176176
KeyValuePair.Create<string, object>("aspnetcore.identity.result", "failure"),
177-
KeyValuePair.Create<string, object>("aspnetcore.identity.result_error_code", "ConcurrencyFailure")
177+
KeyValuePair.Create<string, object>("aspnetcore.identity.error_code", "ConcurrencyFailure")
178178
]));
179179
}
180180

@@ -664,7 +664,7 @@ public async Task CheckPasswordWillRehashPasswordWhenNeeded()
664664
Assert.Collection(checkPassword.GetMeasurementSnapshot(),
665665
m => MetricsHelpers.AssertContainsTags(m.Tags,
666666
[
667-
KeyValuePair.Create<string, object>("aspnetcore.identity.user.password_result", "success_rehash_needed")
667+
KeyValuePair.Create<string, object>("aspnetcore.identity.password_check_result", "success_rehash_needed")
668668
]));
669669
}
670670

@@ -871,7 +871,7 @@ public async Task CheckPasswordWithNullUserReturnsFalse()
871871
Assert.Collection(checkPassword.GetMeasurementSnapshot(),
872872
m => MetricsHelpers.AssertContainsTags(m.Tags,
873873
[
874-
KeyValuePair.Create<string, object>("aspnetcore.identity.user.password_result", "user_missing")
874+
KeyValuePair.Create<string, object>("aspnetcore.identity.password_check_result", "user_missing")
875875
]));
876876
}
877877

@@ -952,13 +952,13 @@ await Assert.ThrowsAsync<NotSupportedException>(
952952
Assert.Collection(generateToken.GetMeasurementSnapshot(),
953953
m => MetricsHelpers.AssertContainsTags(m.Tags,
954954
[
955-
KeyValuePair.Create<string, object>("aspnetcore.identity.token_purpose", "_UNKNOWN"),
955+
KeyValuePair.Create<string, object>("aspnetcore.identity.token_purpose", "_OTHER"),
956956
KeyValuePair.Create<string, object>("error.type", "System.NotSupportedException"),
957957
]));
958958
Assert.Collection(verifyToken.GetMeasurementSnapshot(),
959959
m => MetricsHelpers.AssertContainsTags(m.Tags,
960960
[
961-
KeyValuePair.Create<string, object>("aspnetcore.identity.token_purpose", "_UNKNOWN"),
961+
KeyValuePair.Create<string, object>("aspnetcore.identity.token_purpose", "_OTHER"),
962962
KeyValuePair.Create<string, object>("error.type", "System.NotSupportedException"),
963963
]));
964964
}

0 commit comments

Comments
 (0)