记一个ASP.Net Core配置文件问题

Posted 顺其自然

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一个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 深入了解配置文件加载过程

以上是关于记一个ASP.Net Core配置文件问题的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net core 配置文件

asp.net core1.x/asp.net core2.0中如何加载多个配置文件

ASP.NET Core 读取配置 IOptions 控制器未触发

ASP.NET Core实现类库项目读取配置文件

Asp.Net Core 轻松学-玩转配置文件

为 ASP.NET Core 配置引用 appsettings.json 中的另一个 json 文件