在 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"
    }
  }
}

停用特定日誌

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

{
  "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 DebugTrace 最詳細的訊息
Info Information 金鑰認證事件
Warning Warning 異常但已處理的狀況
Error ErrorCritical 錯誤和例外狀況

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

發展:

{
  "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)紀錄(僅限開發階段)

在你的開發設定檔中設定 EnablePiiLoggingtrue

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.

情境二:代幣取得失敗

症狀:MsalServiceExceptionMsalUiRequiredException

啟用詳細記錄:

{
  "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"