diff --git a/sdk/identity/Azure.Identity/CHANGELOG.md b/sdk/identity/Azure.Identity/CHANGELOG.md index 88e919c3f009a..753b954bbfc98 100644 --- a/sdk/identity/Azure.Identity/CHANGELOG.md +++ b/sdk/identity/Azure.Identity/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bugs Fixed ### Other Changes +- The logging level passed to MSAL now correlates to the log level configured on your configured `AzureEventSourceListener`. Previously, the log level was always set to `Microsoft.Identity.Client.LogLevel.Info`. ## 1.12.0 (2024-06-17) diff --git a/sdk/identity/Azure.Identity/src/AzureIdentityEventSource.cs b/sdk/identity/Azure.Identity/src/AzureIdentityEventSource.cs index 98f63740ce042..180576c0f31c7 100644 --- a/sdk/identity/Azure.Identity/src/AzureIdentityEventSource.cs +++ b/sdk/identity/Azure.Identity/src/AzureIdentityEventSource.cs @@ -8,11 +8,12 @@ using System.Text; using Azure.Core; using Azure.Core.Diagnostics; +using Microsoft.IdentityModel.Abstractions; namespace Azure.Identity { [EventSource(Name = EventSourceName)] - internal sealed class AzureIdentityEventSource : AzureEventSource + internal sealed class AzureIdentityEventSource : AzureEventSource, IIdentityLogger { private const string EventSourceName = "Azure-Identity"; @@ -26,6 +27,8 @@ internal sealed class AzureIdentityEventSource : AzureEventSource private const int MsalLogInfoEvent = 8; private const int MsalLogWarningEvent = 9; private const int MsalLogErrorEvent = 10; + private const int MsalLogCriticalEvent = 23; + private const int MsalLogAlwaysEvent = 24; private const int InteractiveAuthenticationThreadPoolExecutionEvent = 11; private const int InteractiveAuthenticationInlineExecutionEvent = 12; private const int DefaultAzureCredentialCredentialSelectedEvent = 13; @@ -50,6 +53,45 @@ private AzureIdentityEventSource() : base(EventSourceName) { } public static AzureIdentityEventSource Singleton { get; } = new AzureIdentityEventSource(); + public bool IsEnabled(EventLogLevel eventLogLevel) + { + return eventLogLevel switch + { + EventLogLevel.Critical => IsEnabled(EventLevel.Critical, EventKeywords.All), + EventLogLevel.Error => IsEnabled(EventLevel.Error, EventKeywords.All), + EventLogLevel.Warning => IsEnabled(EventLevel.Warning, EventKeywords.All), + EventLogLevel.Informational => IsEnabled(EventLevel.Informational, EventKeywords.All), + EventLogLevel.Verbose => IsEnabled(EventLevel.Verbose, EventKeywords.All), + EventLogLevel.LogAlways => IsEnabled(EventLevel.LogAlways, EventKeywords.All), + _ => false, + }; + } + + public void Log(LogEntry entry) + { + switch (entry.EventLogLevel) + { + case EventLogLevel.Critical when IsEnabled(EventLevel.Critical, EventKeywords.All): + LogMsalCritical(entry.Message); + break; + case EventLogLevel.Error when IsEnabled(EventLevel.Error, EventKeywords.All): + LogMsalError(entry.Message); + break; + case EventLogLevel.Warning when IsEnabled(EventLevel.Warning, EventKeywords.All): + LogMsalWarning(entry.Message); + break; + case EventLogLevel.Informational when IsEnabled(EventLevel.Informational, EventKeywords.All): + LogMsalInformational(entry.Message); + break; + case EventLogLevel.Verbose when IsEnabled(EventLevel.Verbose, EventKeywords.All): + LogMsalVerbose(entry.Message); + break; + case EventLogLevel.LogAlways when IsEnabled(EventLevel.LogAlways, EventKeywords.All): + LogMsalAlways(entry.Message); + break; + } + } + [NonEvent] public void GetToken(string method, TokenRequestContext context) { @@ -197,6 +239,12 @@ public void LogMsal(Microsoft.Identity.Client.LogLevel level, string message) } } + [Event(MsalLogCriticalEvent, Level = EventLevel.Critical, Message = "{0}")] + public void LogMsalCritical(string message) + { + WriteEvent(MsalLogCriticalEvent, message); + } + [Event(MsalLogErrorEvent, Level = EventLevel.Error, Message = "{0}")] public void LogMsalError(string message) { @@ -221,6 +269,12 @@ public void LogMsalVerbose(string message) WriteEvent(MsalLogVerboseEvent, message); } + [Event(MsalLogAlwaysEvent, Level = EventLevel.LogAlways, Message = "{0}")] + public void LogMsalAlways(string message) + { + WriteEvent(MsalLogAlwaysEvent, message); + } + [NonEvent] private static string FormatStringArray(string[] array) { diff --git a/sdk/identity/Azure.Identity/src/MsalConfidentialClient.cs b/sdk/identity/Azure.Identity/src/MsalConfidentialClient.cs index e9d5318f653d4..68fccd2e70f6f 100644 --- a/sdk/identity/Azure.Identity/src/MsalConfidentialClient.cs +++ b/sdk/identity/Azure.Identity/src/MsalConfidentialClient.cs @@ -73,7 +73,7 @@ protected virtual async ValueTask CreateClientCo ConfidentialClientApplicationBuilder confClientBuilder = ConfidentialClientApplicationBuilder.Create(ClientId) .WithHttpClientFactory(new HttpPipelineClientFactory(Pipeline.HttpPipeline)) - .WithLogging(LogMsal, enablePiiLogging: IsSupportLoggingEnabled); + .WithLogging(AzureIdentityEventSource.Singleton, enablePiiLogging: IsSupportLoggingEnabled); // Special case for using appTokenProviderCallback, authority validation and instance metadata discovery should be disabled since we're not calling the STS // The authority matches the one configured in the CredentialOptions. diff --git a/sdk/identity/Azure.Identity/src/MsalPublicClient.cs b/sdk/identity/Azure.Identity/src/MsalPublicClient.cs index 37df7f88673f1..3dcf9185cf9d1 100644 --- a/sdk/identity/Azure.Identity/src/MsalPublicClient.cs +++ b/sdk/identity/Azure.Identity/src/MsalPublicClient.cs @@ -46,7 +46,7 @@ protected virtual ValueTask CreateClientCoreAsync(bool .Create(ClientId) .WithAuthority(authorityUri) .WithHttpClientFactory(new HttpPipelineClientFactory(Pipeline.HttpPipeline)) - .WithLogging(LogMsal, enablePiiLogging: IsSupportLoggingEnabled); + .WithLogging(AzureIdentityEventSource.Singleton, enablePiiLogging: IsSupportLoggingEnabled); if (!string.IsNullOrEmpty(RedirectUrl)) { diff --git a/sdk/identity/Azure.Identity/tests/CredentialTestBase.cs b/sdk/identity/Azure.Identity/tests/CredentialTestBase.cs index 10ab25adb30cc..64ac83a9670f0 100644 --- a/sdk/identity/Azure.Identity/tests/CredentialTestBase.cs +++ b/sdk/identity/Azure.Identity/tests/CredentialTestBase.cs @@ -109,6 +109,55 @@ public async Task RespectsIsSupportLoggingEnabled([Values(true, false)] bool isS Assert.True(_listener.EventData.Any(d => d.Payload.Any(p => p.ToString().StartsWith($"{expectedPrefix} MSAL")))); } + [Test] + [TestCase(EventLevel.Informational)] + [TestCase(EventLevel.Verbose)] + public async Task ListenerEventLevelControlsMsalLogLevel(EventLevel eventLevel) + { + using var _listener = new TestEventListener(); + _listener.EnableEvents(AzureIdentityEventSource.Singleton, eventLevel); + + var token = Guid.NewGuid().ToString(); + TransportConfig transportConfig = new() + { + TokenFactory = req => token + }; + var factory = MockTokenTransportFactory(transportConfig); + var mockTransport = new MockTransport(factory); + + var config = new CommonCredentialTestConfig() + { + TransportConfig = transportConfig, + Transport = mockTransport, + TenantId = TenantId, + IsUnsafeSupportLoggingEnabled = true + }; + var credential = GetTokenCredential(config); + if (!CredentialTestHelpers.IsMsalCredential(credential)) + { + Assert.Ignore($"{credential.GetType().Name} is not an MSAL credential."); + } + transportConfig.IsPubClient = CredentialTestHelpers.IsCredentialTypePubClient(credential); + AccessToken actualToken = await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default, null), default); + + Assert.AreEqual(token, actualToken.Token); + + Assert.True(_listener.EventData.Any(d => d.Level == EventLevel.Informational && d.EventName == "LogMsalInformational")); + + switch (eventLevel) + { + case EventLevel.Informational: + Assert.False(_listener.EventData.Any(d => d.Level == EventLevel.Verbose && d.EventName == "LogMsalVerbose")); + break; + case EventLevel.Verbose: + Assert.True(_listener.EventData.Any(d => d.Level == EventLevel.Verbose && d.EventName == "LogMsalVerbose")); + break; + default: + Assert.Fail("Unexpected event level"); + break; + } + } + [Test] [NonParallelizable] public async Task DisableInstanceMetadataDiscovery([Values(true, false)] bool disable)