在 Microsoft.Identity.Web 中設定日誌記錄

Microsoft Identity.Web 與 ASP.NET Core 的日誌基礎設施整合。 利用它來診斷以下問題:

  • 認證流程 - 登入、登出、令牌驗證
  • 令牌擷取 - 令牌快取命中/未命中,MSAL 操作
  • 下游 API 呼叫 - HTTP 請求、API 權杖的取得
  • 錯誤條件 - 例外、驗證失敗

了解已記錄的元件

組件 日誌來源 Purpose
Microsoft。Identity.Web 核心認證邏輯 設定、令牌擷取、API 呼叫
MSAL.NET Microsoft.Identity.Client 令牌快取操作與授權驗證
身份模型 令牌驗證 JWT 解析、簽名驗證、理賠擷取
ASP.NET Core Auth Microsoft.AspNetCore.Authentication Cookie 操作、禁用/限制操作

開始記錄作業

最小配置

將以下日誌層級條目新增至 appsettings.json 以啟用身份識別日誌:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Identity": "Information"
    }
  }
}

這使得對 Microsoft.Identity.Web 及其相依套件(MSAL.NET、IdentityModel)啟用資訊層級日誌記錄成為可能。

開發配置

關於開發過程中的詳細診斷:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Identity": "Debug",
      "Microsoft.AspNetCore.Authentication": "Information"
    }
  },
  "AzureAd": {
    "EnablePiiLogging": true  // Development only!
  }
}

生產配置

在生產環境中,請在記錄錯誤時盡量減少日誌量。

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft": "Warning",
      "Microsoft.Identity": "Warning"
    }
  },
  "AzureAd": {
    "EnablePiiLogging": false  // Never true in production
  }
}

設定日誌過濾

基於命名空間的過濾

控制日誌詳細程度依命名空間。 以下配置為每個與身份相關的命名空間設置細度層級:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",

      // General Microsoft namespaces
      "Microsoft": "Warning",
      "Microsoft.AspNetCore": "Warning",

      // Identity-specific namespaces
      "Microsoft.Identity": "Information",
      "Microsoft.Identity.Web": "Information",
      "Microsoft.Identity.Client": "Information",

      // ASP.NET Core authentication
      "Microsoft.AspNetCore.Authentication": "Information",
      "Microsoft.AspNetCore.Authentication.JwtBearer": "Information",
      "Microsoft.AspNetCore.Authentication.OpenIdConnect": "Debug",

      // Token validation
      "Microsoft.IdentityModel": "Warning"
    }
  }
}

停用特定日誌

為了在不影響其他元件的情況下靜音雜訊元件,請將其對數電平設為 None 或 Warning:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Identity.Web": "None",  // Completely disable
      "Microsoft.Identity.Client": "Warning"  // Only errors/warnings
    }
  }
}

環境特定的設定

appsettings.{Environment}.json用於各環境設定:

appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Debug"
    }
  },
  "AzureAd": {
    "EnablePiiLogging": true
  }
}

appsettings.Production.json:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Warning"
    }
  },
  "AzureAd": {
    "EnablePiiLogging": false
  }
}

了解日誌等級

ASP.NET Core 定義了以下日誌層級。 選擇一個能平衡診斷細節與日誌量的級別。

ASP.NET Core 日誌層級

等級 使用方式 音量 生產?
追蹤 每項操作都最詳細。 非常高 No
偵錯 詳細的流程,對開發者很有用 高 No
資訊 整體流程與關鍵事件 溫和 選擇性
警告 意外但已妥善處理的狀況 低 是的
錯誤 錯誤和例外狀況 非常低 是的
重要 無法恢復的故障 非常低 是的
沒有 停用日誌 沒有 選擇性

將 MSAL.NET 映射到 ASP.NET Core 層級

MSAL.NET 級別 ASP.NET Core 等價物 說明
Verbose Debug 或 Trace 最詳細的訊息
Info Information 金鑰認證事件
Warning Warning 異常但已處理的狀況
Error Error 或 Critical 錯誤和例外狀況

請依照每個環境使用以下配置。

發展:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Debug",
      "Microsoft.Identity.Client": "Information"
    }
  }
}

舞台設計:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Information",
      "Microsoft.Identity.Client": "Warning"
    }
  }
}

製作:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Warning",
      "Microsoft.Identity.Client": "Error"
    }
  }
}

設定個人識別資訊(PII)記錄

預設是 Microsoft。Identity.Web 會從日誌中刪除個人識別資訊(PII)。 僅在開發環境中啟用個人識別資訊(PII)記錄,以查看完整使用者資料。

什麼是個人識別資訊(PII)?

個人識別資訊(PII) 包括:

  • 用戶名稱、電子郵件地址
  • 顯示名稱
  • 物件 ID、租戶 ID
  • IP 位址
  • 代幣價值與主張

安全性警告

警告:您和您的應用程式需自行遵守所有適用的法規要求,包括 GDPR所規定的規定。 在啟用個人識別資料記錄前,請確保你能安全處理這些可能高度敏感的資料。

啟用個人識別資訊(PII)紀錄(僅限開發階段)

在你的開發設定檔中設定 EnablePiiLogging 為 true :

appsettings.Development.json:

{
  "AzureAd": {
    "EnablePiiLogging": true  //  Development/Testing ONLY
  },
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity": "Debug"
    }
  }
}

以程式方式控制個人識別資訊(PII)記錄

根據主機環境切換 PII 記錄:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<MicrosoftIdentityOptions>(options =>
{
    // Only enable PII in Development
    options.EnablePiiLogging = builder.Environment.IsDevelopment();
});

啟用個人識別碼後會有什麼改變?

不記錄個人身份資訊(PII):

[Information] Token validation succeeded for user '{hidden}'
[Information] Acquired token from cache for scopes '{hidden}'

啟用個人識別資訊後:

[Information] Token validation succeeded for user 'john.doe@contoso.com'
[Information] Acquired token from cache for scopes 'user.read api://my-api/.default'

日誌中的個人識別資訊(PII)刪除

當 PII 記錄被停用時,敏感資料會被替換為:

  • {hidden} - 隱藏使用者識別碼
  • {hash:XXXX} - 顯示雜湊值而非實際值
  • *** - 遮蔽標記

使用相關性識別碼

相關識別碼追蹤跨服務的認證請求。 將這些資訊納入日誌和支援工單,以加快問題解決。

什麼是相關識別碼?

相關 ID 是一種 GUID ,能唯一識別以下區域的認證或憑證取得請求:

  • 您的應用程式
  • Microsoft Identity 平台
  • MSAL.NET 函式庫
  • Microsoft 後端服務

取得相關識別碼

方法一:來自 AuthenticationResult

成功取得代幣後,從中 AuthenticationResult 提取相關ID:

using Microsoft.Identity.Web;

public class TodoController : ControllerBase
{
    private readonly ITokenAcquisition _tokenAcquisition;
    private readonly ILogger<TodoController> _logger;

    public TodoController(
        ITokenAcquisition tokenAcquisition,
        ILogger<TodoController> logger)
    {
        _tokenAcquisition = tokenAcquisition;
        _logger = logger;
    }

    [HttpGet]
    public async Task<IActionResult> GetTodos()
    {
        var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(
            new[] { "user.read" });

        _logger.LogInformation(
            "Token acquired. CorrelationId: {CorrelationId}, Source: {TokenSource}",
            result.CorrelationId,
            result.AuthenticationResultMetadata.TokenSource);

        return Ok(result.CorrelationId);
    }
}

方法二:來自 MsalServiceException

擷取當代幣取得失敗時的關聯 ID MsalServiceException:

using Microsoft.Identity.Client;

try
{
    var token = await _tokenAcquisition.GetAccessTokenForUserAsync(
        new[] { "user.read" });
}
catch (MsalServiceException ex)
{
    _logger.LogError(ex,
        "Token acquisition failed. CorrelationId: {CorrelationId}, ErrorCode: {ErrorCode}",
        ex.CorrelationId,
        ex.ErrorCode);

    // Return correlation ID to user for support
    return StatusCode(500, new {
        error = "authentication_failed",
        correlationId = ex.CorrelationId
    });
}

方法三:設定自訂關聯 ID

指派自訂關聯 ID 來連結應用程式追蹤與 Microsoft Entra ID 請求:

[HttpGet("{id}")]
public async Task<IActionResult> GetTodo(int id)
{
    // Use request trace ID as correlation ID
    var correlationId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

    var todo = await _downstreamApi.GetForUserAsync<Todo>(
        "TodoListService",
        options =>
        {
            options.RelativePath = $"api/todolist/{id}";
            options.TokenAcquisitionOptions = new TokenAcquisitionOptions
            {
                CorrelationId = Guid.Parse(correlationId)
            };
        });

    _logger.LogInformation(
        "Called downstream API. TraceId: {TraceId}, CorrelationId: {CorrelationId}",
        HttpContext.TraceIdentifier,
        correlationId);

    return Ok(todo);
}

提供相關ID以供支援

聯絡 Microsoft 客服時,請提供以下資訊:

  1. 相關性 ID - 來自日誌或例外
  2. 時間戳 記 - 錯誤發生時間(UTC)
  3. 租戶編號 - 您的Microsoft Entra ID租戶
  4. 錯誤代碼 - 如適用(例如, AADSTS50058)

支援請求範例:

Subject: Token acquisition failing for user.read scope

Correlation ID: 12345678-1234-1234-1234-123456789012
Timestamp: 2025-01-15 14:32:45 UTC
Tenant ID: contoso.onmicrosoft.com
Error Code: AADSTS50058

啟用令牌快取記錄

令牌快取記錄幫助你了解快取的命中/失行為,並診斷分散式快取的效能問題。

啟用令牌快取診斷

對於使用分散式令牌快取的 .NET Framework 或 .NET Core 應用程式,請設定詳細記錄:

using Microsoft.Extensions.Logging;
using Microsoft.Identity.Web.TokenCacheProviders;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDistributedTokenCaches();

// Enable detailed token cache logging
builder.Services.AddLogging(configure =>
{
    configure.AddConsole();
    configure.AddDebug();
})
.Configure<LoggerFilterOptions>(options =>
{
    options.MinLevel = LogLevel.Debug;  // Detailed cache operations
});

令牌快取日誌範例

快取命中:

[Debug] Token cache: Token found in cache for scopes 'user.read'
[Information] Token source: Cache

快取未命中:

[Debug] Token cache: No token found in cache for scopes 'user.read'
[Information] Token source: IdentityProvider
[Debug] Token cache: Token stored in cache

排除分散式快取問題

啟用提供者專屬的日誌功能,以診斷快取連線與效能問題。

Redis 快取:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration["Redis:ConnectionString"];
});

// Enable Redis logging
builder.Services.AddLogging(configure =>
{
    configure.AddFilter("Microsoft.Extensions.Caching", LogLevel.Debug);
});

SQL Server cache:

配置帶有日誌功能的 SQL Server 分散式快取:

builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration["SqlCache:ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TokenCache";
});

// Enable SQL cache logging
builder.Services.AddLogging(configure =>
{
    configure.AddFilter("Microsoft.Extensions.Caching.SqlServer", LogLevel.Information);
});

排除常見問題

請使用以下情境來診斷頻繁的認證與授權問題。

常見的伐木情境

情境一:令牌驗證失敗

症狀: 401 未經授權的回應

啟用詳細記錄:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.Authentication.JwtBearer": "Debug",
      "Microsoft.IdentityModel": "Information"
    }
  }
}

尋找:

[Information] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:
  Failed to validate the token.
[Debug] Microsoft.IdentityModel.Tokens: IDX10230: Lifetime validation failed.
  The token is expired.

情境二:代幣取得失敗

症狀:MsalServiceException 或 MsalUiRequiredException

啟用詳細記錄:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity.Web": "Debug",
      "Microsoft.Identity.Client": "Information"
    }
  }
}

尋找:

[Error] Microsoft.Identity.Web: Token acquisition failed.
  ErrorCode: invalid_grant, CorrelationId: {guid}
[Information] Microsoft.Identity.Client: MSAL returned exception:
  AADSTS50058: Silent sign-in failed.

情境三:下游 API 呼叫失敗

症狀: HTTP 502 或呼叫下游 API 時的逾時錯誤

啟用詳細記錄:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.Identity.Abstractions": "Debug",
      "System.Net.Http": "Information"
    }
  }
}

在控制器中加入自訂日誌以捕捉下游 API 錯誤:

[HttpGet]
public async Task<IActionResult> GetUserProfile()
{
    try
    {
        _logger.LogInformation("Acquiring token for Microsoft Graph");

        var user = await _downstreamApi.GetForUserAsync<User>(
            "MicrosoftGraph",
            options => options.RelativePath = "me");

        _logger.LogInformation(
            "Successfully retrieved user profile for {UserPrincipalName}",
            user.UserPrincipalName);

        return Ok(user);
    }
    catch (MsalUiRequiredException ex)
    {
        _logger.LogWarning(ex,
            "User interaction required. CorrelationId: {CorrelationId}",
            ex.CorrelationId);
        return Challenge();
    }
    catch (HttpRequestException ex)
    {
        _logger.LogError(ex, "Failed to call Microsoft Graph API");
        return StatusCode(502, "Downstream API error");
    }
}

解讀對數模式

以下範例展示了常見認證事件的典型日誌輸出。

認證流程成功:

[Info] Authentication scheme OpenIdConnect: Authorization response received
[Debug] Correlation id: {guid}
[Info] Authorization code received
[Info] Token validated successfully
[Info] Authentication succeeded for user: {user}

需同意:

[Warning] Microsoft.Identity.Web: Incremental consent required
[Info] AADSTS65001: User consent is required for scopes: {scopes}
[Info] Redirecting to consent page

代幣更新:

[Debug] Token expired, attempting silent token refresh
[Info] Token source: IdentityProvider
[Info] Token refreshed successfully

與外部供應商一起聚合日誌

將身份日誌轉發至集中式日誌平台以進行監控與警示。

Application Insights 整合:

將識別遙測資料傳送至 Application Insights,並增強其相關性 ID:

using Microsoft.ApplicationInsights.Extensibility;

builder.Services.AddApplicationInsightsTelemetry();

// Enrich telemetry with correlation IDs
builder.Services.AddSingleton<ITelemetryInitializer, CorrelationIdTelemetryInitializer>();

Serilog 整合:

設定 Serilog 將身份日誌擷取至主控台及滾動檔案輸出:

using Serilog;

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft.Identity", Serilog.Events.LogEventLevel.Debug)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/identity-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

builder.Host.UseSerilog();

遵循伐木最佳實務

運用這些做法,確保您的身份記錄安全、實用且具效能。

應該做的事

1. 使用結構化記錄:

將數值以命名參數傳遞,讓日誌聚合器能夠索引並查詢:

_logger.LogInformation(
    "Token acquired for user {UserId} with scopes {Scopes}",
    userId, string.Join(" ", scopes));

2. 日誌相關ID:

錯誤日誌中請務必包含相關ID,以簡化支援調查:

_logger.LogError(ex,
    "Operation failed. CorrelationId: {CorrelationId}",
    ex.CorrelationId);

3. 使用適當的日誌級別:

將日誌級別與嚴重程度及受眾對應:

_logger.LogDebug("Detailed diagnostic info");      // Development
_logger.LogInformation("Key application events");  // Selective production
_logger.LogWarning("Unexpected but handled");      // Production
_logger.LogError(ex, "Operation failed");          // Production

4. 在生產環境中清洗日誌:

在寫入生產日誌前,先遮蔽敏感值:

var sanitizedEmail = environment.IsProduction()
    ? MaskEmail(email)
    : email;
_logger.LogInformation("Processing request for {Email}", sanitizedEmail);

禁忌事项

1. 不要在生產環境中啟用個人識別資訊(PII):

//  Wrong
"EnablePiiLogging": true  // In production config!

//  Correct
"EnablePiiLogging": false

2. 不要記錄秘密:

//  Wrong
_logger.LogInformation("Token: {Token}", accessToken);

//  Correct
_logger.LogInformation("Token acquired, expires: {ExpiresOn}", expiresOn);

3. 不要在生產環境中使用冗長日誌:

//  Wrong - production appsettings.json
"Microsoft.Identity": "Debug"

//  Correct
"Microsoft.Identity": "Warning"