上一部分预备知识在这 http://www.cnblogs.com/cgzl/p/9010978.html
如果您对ASP.NET Core很了解的话,可以不看本文, 本文基本都是官方文档的内容。
ASP.NET Core 预备知识
项目配置
假设在项目的根目录有这样一个json文件, 在ASP.NET Core项目里我们可以使用IConfigurationRoot来使用该json文件作为配置文件, 而IConfigurationRoot是使用ConfigurationBuilder来创建的:
可以看到ConfigurationBuilder加载了firstConfig.json文件, 使用的是AddJsonFile这个扩展方法. 调用builder的Build方法会得到一个IConfigurationRoot的实例, 它实现了IConfiguration接口, 随后我们便可以通过遍历它的键值对.
其中json文件里的结构数据都最为键值对被扁平化到IConfiguration里了, 我们可以通过它的key找到对应的值:
像childkey1这种带层次结构的值可以使用冒号 : 作为层次分隔符.
配置文件总会包含这种多层结构的, 更好的办法是把类似的配置进行分组获取, 可以使用IConfiguration的GetSection()方法来获取局部的配置:
当有多个配置文件的时候, 配置数据的加载和它们在程序中指定的顺序是一样的, 如果多个文件都有同一个键的话, 那么最后加载的值将会覆盖先前加载的值.
下面是另一个配置文件:
在firstConfig后加载secondConfig:
最后key1的值是后加载的secondConfig里面的值.
当然了, 如果firstConfig里面有而secondConfig却没有的键, 它的值肯定来自firstConfig.
配置提供商
配置数据可以来自多种数据源, 它们可能是不同格式的.
ASP.NET Core 默认支持从下列方式获得配置:
- 文件格式(INI, JSON, XML)
- 命令行参数
- 环境变量
- 内存中的.NET对象
- 未加密的Secret管理存储
- 加密的用户存储, 例如Azure秘钥库
- 自定义的提供商
这些东西还是看官方文档吧, 本文使用JSON格式的就够用了.
强类型的配置
ASP.NET Core允许把配置数据映射到一个对象类上面.
针对上面的firstConfig.json文件, 我们创建以下这个类:
然后调用IConfiguration的Bind扩展方法来把键值对集合对值映射到这个强类型对POCO实例里:
在标准的ASP.NET Core 2.0的项目模版里, 加载配置文件的步骤被封装了, 默认或加载appSettings.json 以及 appSettings.{环境}.json.
我记得是封装在这里了:
我把firstConfig.json改名为appSettings.json.
然后在Startup里面可以获得IConfiguration:
从打印结果可以看到, 加载的不只是appSettings里面的内容, 还有系统环境变量的值.
这种情况下, 使用IServiceCollection的Configure扩展方法可以把配置映射到指定的类上面:
同时这也允许在程序的任何地方注入IOptions<FirstConfig>了:
这个Configure方法不仅仅可以映射ConfigurationRoot, 还可以映射配置的一部分:
配置变化
在项目运行的时候, 项目的配置信息可能会发生变化.
当采用的是基于文件的配置时, 如果配置数据有变化了, 我们应该让配置模型重新加载, 这就需要把AddJsonFile里面的配置属性 ReloadOnChange 设置为 true:
这时, 无论在哪各地方使用了IConfigurationRoot和IConfiguration, 它们都会反映出最新的值, 但是IOptions<T>却不行. 即使文件变化了并且配置模型也通过文件提供商进行了更新, IOptions<T>的实例仍然包含的是原始值.
为了让配置数据可以在这种强类型映射的类上体现, 就需要使用IOptionsSnapshot<T>:
IOptionsSnapshot<T> 的开销很小, 可以放心使用
日志
ASP.NET Core 提供了6个内置的日志提供商。
需要使用日志的话,只需注入一个ILogger对象即可,不过该对象首先要在DI容器中注册。
这个ILogger接口主要是提供了Log方法:
记录Log的时候使用Log方法即可:
不过可以看到,该方法参数很多,用起来还是略显麻烦的。
幸运的是,针对Log还有几个扩展方法,他们就简单了很多:
- LogCritical,用来记录严重的事情
- LogDebug,记录调试信息
- LogError,记录异常
- LogInformation,记录信息性的事情
- LogTrace,记录追踪信息
- LogWarning,记录警告信息
在项目中配置和使用Log,只需在Program.cs里调用IWebHostBuilder的ConfigureLogging扩展方法即可:
本例中,我们把log配置成在控制台输出。
如果只是输出到控制台,其实我们就多此一举了,因为CreateDefaultBuilder这个方法里已经做了一些Log的配置,看一下反编译的源码:
可以看到logging的一些配置数据是从整体配置的Logging部分取出来的,然后配置了使用输出到控制台和Debug窗口的提供商。
记录Log的时候,通常情况下使用那几个扩展方法就足够了:
请注意,这里我注入的是ILogger<T>类型的logger,其中T可以用来表示日志的分类,它可以是任何类型,但通常是记录日志时所在的类。
运行项目后,可以看到我记录的日志:
同样也可以在一个类里面把记录的日志分为不同的分类,这时候你可以使用ILoggerFactory,这样就可以随时创建logger了,并把它绑定到特定的区域:
不知道您有没有发现上面这几个例子中日志输出的时候都有个数字 [0], 它是事件的标识符。因为上面的例子中我们没有指定事件的ID,所以就取默认值0。使用事件ID还是可以帮助我们区分和关联记录的日志的。
每次写日志的时候, 都需要通过不同的方式指明LogLevel, LogLevel表明的是严重性.
下面是ASP.NET Core里面定义的LogLevel(它是个枚举), 按严重性从低到高排序的:
Trace = 0, 它可以包含敏感拘束, 默认在生产环境中它是被禁用掉的.
Debug = 1, 也是在调试使用, 应该在生产环境中禁用, 但是遇到问题需要调试可以临时启用.
Information = 2, 用来追踪应用程序的总体流程.
Warning = 3, 通常用于记录非正常或意外的事件, 也可以包括不会导致应用程序停止的错误和其他事件, 例如验证错误等.
Error = 4, 用于记录无法处理的错误和异常, 这些信息意味着当前的活动或操作发生了错误, 但不是应用程序级别的错误.
Critical = 5, 用于记录需要立即处理的事件, 例如数据丢失或磁盘空间不足.
None = 6, 如果你不想输出日志, 你可以把程序的最低日志级别设置为None, 此外还可以用来过滤日志.
记录的日志信息是可以带参数的, 使用消息模板(也就是消息主题和参数分开), 格式如下:
同样也支持字符串插值:
第二种方式代码的可读性更强一些, 而且它们输出的结果没有什么区别:
但是对于日志系统来说, 这两种方式是不一样的. 通过消息模板的方式(消息和参数分开的方式), 日志提供商可以实现语义日志或叫做结构化日志, 它们可以把参数单独的出入到日志系统里面进行单独存储, 不仅仅是格式化的日志信息.
此外, 用重载的方法, 记录日志时也可以包含异常对象.
日志分组
我们可以使用相同的日志信息来表示一组操作, 这需要使用scope, scope继承了IDisposable接口, 通过ILogger.BeginScope<TState>可以得到scope:
使用scope, 还有一点需要注意, 需要在日志提供商上把IncludeScopes属性设置为true:
您可以发现, 日志被输出了两遍, 这是因为WebHost.CreateDefaultBuilder方法里面已经配置使用了AddConsole()方法, 我再配置一遍的话就相当于又添加了一个输出到控制台的日志提供商.
所以, 我可以不采用这个构建模式创建IWebHost, 改为直接new一个:
这样就正确了. 可以看到日志信息的第一行内容是一样的, 第二行是各自的日志信息.
日志的过滤
我们可以为整个程序设定日志记录的最低级别, 也可以为某个日志提供商和分类指定特定的过滤器.
设置全局最低记录日志的级别使用SetMinimumLevel()扩展方法:
如果想完全不输出日志的话, 可以把最低记录的级别设为LogLevel.None.
我们还可以为不同场景设置不同的最低记录级别:
然后分别建立这两个分类的logger, 并记录:
查看输出结果, 已经按配置进行了过滤:
这里可以使用完整的类名作为分类名:
然后使用ILogger<T>即可:
针对上面这个例子, 我们还可以使用配置文件:
相应的, 代码也需要改一下:
输出的效果是一样的.
日志提供商
ASP.NET Core 内置了6个日志提供商:
- Console, 使用logging.AddConsole()来启用.
- Debug, 使用logging.AddDebug()来启用. 它使用的是System.Diagnostics.Debug的Debug.WriteLine()方法, 由于Debug类的所有成员都是被[Conditional("DEBUG")]修饰过了, 所以无法被构建到Release Build里, 也就是生产环境是无法输出的, 除非你把Debug Build作为部署到生产环境