1. 程式人生 > >記一個ASP.Net Core配置檔案問題

記一個ASP.Net Core配置檔案問題

  最近排查一個ASP.Net Core專案的Bug,用LogInformation()記錄一些執行日誌,本地測試日誌記錄正常,然後發到RC環境測試,結果發現死活沒有日誌資訊。


  首先想到就是LogLevel設定有問題。檢查了基礎的配置檔案(appsettings.json)沒有問題,而RC環境的配置檔案(appsettings.RC.json)未配置Logging節點,也就不會覆蓋。

 1 "Logging": {
 2   "IncludeScopes": false,
 3   "LogLevel": {
 4     "Default": "Information
", 5 "System": "Warning", 6 "Microsoft": "Warning" 7 }, 8 "Console": { 9 "LogLevel": { 10 "Default": "Warning" 11 } 12 } 13 }
appsettings.json

  然後檢查了涉及appsettings.json的程式碼。在Startup.cs中,會根據“environment.json”檔案中配置的“EnvironmentName”值,來載入不同配置檔案。

 1 private
readonly ILoggerFactory m_LoggerFactory; 2 private readonly IConfiguration m_Configuration; 3 4 public Startup(IHostingEnvironment env, ILoggerFactory loggerFactory) 5 { 6 m_LoggerFactory = loggerFactory; 7 8 var environmentName = GetEnvironmentName(); 9 var builder = new ConfigurationBuilder()
10 .SetBasePath(env.ContentRootPath) 11 .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 12 .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true); 13 14 m_Configuration = builder.Build(); 15 } 16 17 private static string GetEnvironmentName() 18 { 19 var configuration = new ConfigurationBuilder() 20 .AddJsonFile("environment.json", optional: true) 21 .Build(); 22 return configuration["EnvironmentName"] ?? string.Empty; 23 }
Startup

   可是environment.json配置也沒問題,而其中的m_Configuration變數只是會注入IoC中供業務程式碼讀取配置使用,並沒有對Logging配置做任何修改。

 

  之後又想到,所有環境的配置檔案都是放在同一個目錄下,是否是串檔案了呢?而其中appsettings.Production.json中確實有配置Logging.LogLevel為“Warning”,如果載入了這個配置,那LogInformation()就不會輸出日誌了。因而調整了下appsettings.Production.json中的Loggind.LogLevel為“Information”,然後再測試,嘿,有日記資訊了!也就是說,Production的配置檔案確實被載入了。

  再次檢查程式碼發現並未有明確載入Production檔案,那該不會是某個系統方法通過環境變數ASPNETCORE_ENVIRONMENT載入的吧?因為未設定該值(確實沒設定)預設就會是Production[1]

 

  於是立馬修改ASPNETCORE_ENVIRONMENT=RC(注意是1個“_”),還原appsettings.Production.json,然後重啟服務測試,不出所料,日誌正常記錄。那麼,接下來就是找到那個“系統方法”了。

  仔細翻了翻官方文件,找到了以下內容[2]

  CreateDefaultBuilder方法(2.0新增[4])會呼叫2次AddJsonFile(),第1次載入appsettings.json,第2次載入appsettings.{Environment}.json,而Environment取至IHostingEnvironment.EnvironmentName,即環境值。對應原始碼[5]

 1 builder.UseKestrel((builderContext, options) =>
 2 {
 3     options.Configure(builderContext.Configuration.GetSection("Kestrel"));
 4 })
 5 .ConfigureAppConfiguration((hostingContext, config) =>
 6 {
 7     var env = hostingContext.HostingEnvironment;
 8 
 9     config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
10           .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
11 
12     if (env.IsDevelopment())
13     {
14         var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
15         if (appAssembly != null)
16         {
17             config.AddUserSecrets(appAssembly, optional: true);
18         }
19     }
20 
21     config.AddEnvironmentVariables();
22 
23     if (args != null)
24     {
25         config.AddCommandLine(args);
26     }
27 })
CreateDefaultBuilder

 

  好嘛,真相大白:

  1. 未設定環境變數ASPNETCORE_ENVIRONMENT,則預設為Production
  2. 呼叫CreateDefaultBuilder方法構建WebHost,自動載入appsettings.Production.json
  3. 最終,Logging.LogLevel被設定為了Production配置的值“Warning”,因而LogInformation()失效

  那麼,只要正確設定ASPNETCORE_ENVIRONMENT值即可解決問題咯。

  但是,還記得上面提到的“environment.json”檔案嗎?這個檔案目的本就是為了方便切換不同環境的配置檔案而建立的,Logging的配置理應由它來決定,如果通過ASPNETCORE_ENVIRONMENT來設定,就多此一舉了。那怎麼才能讓在Startup構造方法中構建的m_Configuration物件對Logging生效呢?官方也給出了方案:ConfigureAppConfiguration方法[2]!是不是覺得眼熟?在上面的CreateDefaultBuilder方法中正是通過ConfigureAppConfiguration()來載入預設配置的。

  最終,程式碼修正如下: 

 1 private static IWebHost BuildWebHost(string[] args)
 2 {
 3     return WebHost.CreateDefaultBuilder(args)
 4         .CaptureStartupErrors(true)
 5         .UseSetting(WebHostDefaults.DetailedErrorsKey, "true")
 6         .ConfigureAppConfiguration((hostingContext, config) =>
 7         {
 8             config.Sources.Clear();
 9             config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
10                 .AddJsonFile($"appsettings.{GetEnvironmentName()}.json", optional: true, reloadOnChange: true);
11         })
12         .UseStartup<Startup>()
13         .UseNLog()
14         .Build();
15 }
16 
17 private static string GetEnvironmentName()
18 {
19     var configuration = new ConfigurationBuilder()
20         .AddJsonFile("environment.json", optional: true)
21         .Build();
22     return configuration["EnvironmentName"] ?? string.Empty;
23 }
BuildWebHost

   構建m_Configuration的程式碼,由Startup.cs轉移到了Program.cs,而在Startup.cs中,Configuration物件可直接注入:

1 private readonly ILoggerFactory m_LoggerFactory;
2 private readonly IConfiguration m_Configuration;
3 
4 public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
5 {
6     m_LoggerFactory = loggerFactory;
7     m_Configuration = configuration;
8 }
Startup

 

  至此,告一段落!

 

  參考文件

  1. 官方文件:配置Environment
  2. 官方文件:配置Configuration
  3. 官方文件:配置Logging
  4. ASP.Net Core 1.x遷移至2.0
  5. CreateDefaultBuilder原始碼
  6. asp.netcore 深入瞭解配置檔案載入過程