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

記一個ASP.Net Core配置文件問題

lap 測試日誌 png 官方 pub var ast pac spa

  最近排查一個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 深入了解配置文件加載過程

記一個ASP.Net Core配置文件問題