Microsoft Graph SDK has the ability to log out complete HTTP Requests and Reponses as documented here. The way this logging mechanism works is by implementing a custom HttpClient Message handler to intercept every HTTP Request and Response between the client application and the Microsoft Graph Service. Besides hooking into GraphServiceClient’s processing pipeline to do request and response tracing, one can also configure proxy info. See Customize the Microsoft Graph SDK service client – Microsoft Graph | Microsoft Docs for more detail. MSAL.Net is used in the GraphSerivceClient’s Authentication Provider. Consequently, logging in this library can provide valuable insight into authentication failure. In this post I will show how to enable both MSAL.Net logging and MS Graph SDK logging. The sample application I use here is a .Net Core 3.0 console application.

Logging techniques

For MSAL.Net logging, I use the Azure Blob Storage client SDK (v12) to write log entries into an Azure Blob, and for MS Graph SDK logging, I use Serilog library. Serilog has a number of logging providers (they are called “sinks”), which can cater to different logging environment including Application Insight, DocumentDB, Event Hub, etc.. In this blog I use 3 different logging sinks (providers): console logger, file logger, and Azure Blob Storage logger. These different logging providers are configured once at the beginning below. You don’t have to use all 3 and can add new one(s) or comment out the provider(s) you don’t need.

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                // Serilog console logging
                .WriteTo.Console()
                // Serilog file logging
                .WriteTo.File(LoggingLocalPath, rollingInterval: RollingInterval.Day)
                // Serilog Azure Blob Storage logging
                .WriteTo.AzureBlobStorage(AzureStorageConnection, Serilog.Events.LogEventLevel.Verbose, MSGraphAzureBlobContainerName, MSGraphAzureBlobName)
                .CreateLogger();

Pre-requisites:

You will need the following for this blog:

Application Registration

Have an application registered in Azure AD. Configure ‘http://localhost’ as a redirect URI under ‘Mobile and desktop applications’ platform. This specific redirect URI is required for .Net Core applications.

For API permission, configure these delegated MS Graph permissions: User.Read.All and Application.ReadWrite.All and grant Admin Consent to these permissions since the sample will attempt to use the following MS Graph requests to get the logged in user information and create an App Registation:

GET https://graph.microsoft.com/beta/me and POST https://graph.microsoft.com/beta/applications

Note: the sample attempts to create an application so make sure the logged-in user is in one of the following Administrative roles otherwise the Application creation may fail due to insufficient permission: Application Developer, Application Administrator, Cloud Application Administrator, and Global Administrator. See Azure AD built-in roles – Azure Active Directory | Microsoft Docs for a list of built-in Azure AD roles.

Azure Storage

Create an Azure Storage for storing both MSAL and MS Graph SDK logs as Azure Blobs. For read/write access to Azure Storage you can use the Connection string in either key 1 or key 2 ‘Access keys’ blade:

The Application Code

The complete sample for this can be downloaded from this github repositiory. All the configuration options for this sample is in appsettings.json file below. The sample uses the code snippet from Ray’s blog to read in the configuration settings from the appsettings.json file. The sample uses MS Graph beta endpoint, so I use the Microsoft.Graph.Beta package. For v1 endpoint use Microsoft.Graph package instead of. Refer to Use the Microsoft Graph SDKs with the beta API – Microsoft Graph | Microsoft Docs for more info.

{
  "Azure": {
    "ClientId": "<Application ID>",
    "TenantId": "<Tenant ID>",
    "MSALAzureBlobContainerName": "msallogs", //Blob Container for MSAL log
    "MSALAzureBlobName": "MsalLog.txt", // blob file containing MSAL log
    "MSGraphAzureBlobContainerName": "msgraphlogs",  // Blob Container for MS Graph log
    "MSGraphAzureBlobName": "{yyyy}-{MM}-{dd} - msgraphlog.txt", // blob file containing MS Graph log
    "LoggingLocalPath": "C:/temp/msgraphlog.txt", // path for local file logging
    "AzureStorageConnection": "<Access key connection string for Azure Storage>",
    "Scopes": [ "https://graph.microsoft.com/.default" ]
  }
}

This sample uses both a GET and POST requests to show that you can log out the complete HTTP Requests and Responses including both the Headers and Body. For convenience I use the following 4 Helper Functions to get the information for each part so you can comment out the appropriate one should that info not be needed:

The above helper functions are used in the Logging HttpClient Message Handler shown below. Requests are logged before calling the DelegatingHandler’s SendAsync method and responses are logged after the call.

public class SeriLoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = null;
            try
            {
                Log.Information("sending Graph Request");
                Log.Debug(GetRequestHeader(httpRequest));
                Log.Debug(GetRequestBody(httpRequest));
                response = await base.SendAsync(httpRequest, cancellationToken);
                Log.Information("Receiving Response:");
                Log.Debug(GetResponseHeader(response));
                Log.Debug(GetResponseBody(response));
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Something went wrong");
                if (response.Content != null)
                {
                   await response.Content.ReadAsByteArrayAsync();// Drain response content to free connections.
                }
            }
            return response;
        }
}

The following code shows how to set up the GraphServcieClient to use the Logging Handler.

            IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
                .Create(ClientId)
                .WithTenantId(TenantId)
                // Enable MSAL logging
                .WithLogging(MSALlogger, Microsoft.Identity.Client.LogLevel.Verbose, true)
                .WithRedirectUri("http://localhost")
                .Build();

            InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(publicClientApplication, Scopes);

            // get the default list of handlers and add the logging handler to the list
            var handlers = GraphClientFactory.CreateDefaultHandlers(authProvider);

            // Remove Compression handler
            var compressionHandler =
                handlers.Where(h => h is CompressionHandler).FirstOrDefault();
            handlers.Remove(compressionHandler);

            // Add SeriLog logger
            handlers.Add(new SeriLoggingHandler());

            InitializeBlobStorageForMSAL().Wait();

            var httpClient = GraphClientFactory.Create(handlers);
            GraphServiceClient graphClient = new GraphServiceClient(httpClient);

This sample will log out verbose sensitive information including PII Info and Access Tokens info so be cautious when sharing the debug logs. The debugging technique here can help troubleshooting problems in environments where an HTTP Fiddler trace may not easily be obtained. I use different logging techniques to write to Azure Blob Storage for both MSAL logging and MS Graph SDK logging since in my testing I have encountered a problem during authentication with the authentication prompt window not appearing when Serilog Azure Blob Storage sink is used for both scenarios.

Running the sample

the MS Graph Requests and Responses should appear in both the console window and the configured Directory location in appsettings.json. The name of the files should contain the date as below:

For Azure Blob Storage logging, the logs are in the following msallogs and msgraphlogs container:

One Thought to “How to perform logging for both MSAL.Net and Microsoft Graph SDK”

  1. Kamus

    Since the Microsoft.Graph.Auth library has been deprecated, could you please demo how to handle the logging without the library and use of InteractiveAuthenticationProvider?

Leave a Comment