ASP.NET Core搭建多层网站架构【7-使用NLog日志记录器】
2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0
摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构【7-使用NLog日志记录器】
NLog日志记录器的写入数据库、写入文件、彩色控制台,按等级过滤日志等功能
本章节介绍了NLog日志记录器的写入数据库、写入文件、彩色控制台,按等级过滤日志等功能,之前写过ASP.NET Core中使用NLog记录日志,仅仅是写入数据库和文件,也没有按等级过滤日志
添加包引用
向MS.WebCore
类库添加以下包引用:
<ItemGroup>
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
</ItemGroup>
上一章节中已经添加过,这里再次提示下,已经添加过的不需要重复添加
NLog.Web.AspNetCore
包中已经包含NLog
包,所以只需要这一个包即可
向MS.WebApi
应用程序添加以下包引用:
<ItemGroup>
<PackageReference Include="MySql.Data" Version="8.0.19" />
</ItemGroup>
MySql.Data
这个包是NLog写入MySQL数据库需要使用的数据库提供程序。
NLogExtensions
在MS.WebCore
类库中添加Logger文件夹,在该文件夹中添加NLogExtensions.cs
类:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Config;
using NLog.Web;
using System.Linq;
using System.Xml.Linq;
namespace MS.WebCore.Logger
{
public static class NLogExtensions
{
//优先级:Trace>Debug>Info>Warn>Error>Fatal
const string _mssqlDbProvider = "Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient";
const string _mysqlDbProvider = "MySql.Data.MySqlClient.MySqlConnection, MySql.Data";
/// <summary>
/// 确保NLog配置文件sql连接字符串正确
/// </summary>
/// <param name="nlogPath"></param>
/// <param name="dbType"></param>
/// <param name="sqlConnectionStr"></param>
public static void EnsureNlogConfig(string nlogPath, string dbType, string sqlConnectionStr)
{
XDocument xd = XDocument.Load(nlogPath);
if (xd.Root.Elements().FirstOrDefault(a => a.Name.LocalName == "targets")
is XElement targetsNode && targetsNode != null &&
targetsNode.Elements().FirstOrDefault(a => a.Name.LocalName == "target" && a.Attribute("name").Value == "log_database")
is XElement targetNode && targetNode != null)
{
if (!targetNode.Attribute("connectionString").Value.Equals(sqlConnectionStr))//连接字符串不一致则修改
{
targetNode.Attribute("connectionString").Value = sqlConnectionStr;
//dbProvider的变动仅限mssql和mysql
if (dbType.ToLower() == "mysql")
{
targetNode.Attribute("dbProvider").Value = _mysqlDbProvider; //mysql
}
else
{
targetNode.Attribute("dbProvider").Value = _mssqlDbProvider; //mssql
}
xd.Save(nlogPath);
//编辑后重新载入配置文件(不依靠NLog自己的autoReload,有延迟)
LogManager.Configuration = new XmlLoggingConfiguration(nlogPath);
}
}
}
/// <summary>
/// 注入Nlog服务
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IHostBuilder AddNlogService(this IHostBuilder builder)
{
return builder
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddDebug();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog()// 替换NLog作为日志管理
;
}
}
}
说明:
- 第一个
EnsureNlogConfig
方法是确保NLog配置文件sql连接字符串和appsettings.json
文件中一致,NLog写入数据库功能有对应的DbProvider,我这里只内置了MySQL和SQL server的,如有需要自行修改 - 第二个
AddNlogService
是对IHostBuilder的一个方法扩展,把NLog开启的配置封装在里面
NLog.config
在MS.WebApi
应用程序中添加Web配置文件,并改名为NLog.config:
右击NLog.config
文件,"属性"中选择复制到输出目录为 始终复制
修改NLog.config
文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="false" internalLogLevel="Warn" internalLogFile="${basedir}/logs/NlogRecords.log">
<!--指定了当NLog自己遇到Warn等级以上的报错时,生成日志到./logs/NlogRecords.log下(网站的相对路径)。除非纠错,不可以设为Trace否则速度很慢,起码Debug以上-->
<extensions>
<add assembly="NLog.Web.AspNetCore" />
</extensions>
<targets>
<!--通过数据库记录日志 配置
dbProvider请选择mysql或是sqlserver,同时注意连接字符串,需要安装对应的sql数据提供程序
dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" connectionString="server=192.168.137.10;database=EvMSDB;user=root;password=mysql@local"
dbProvider="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient" connectionString="Server=192.168.1.204;Database=EvMSDB;User ID=sa;Password=yzhly@126"
-->
<target name="log_database" xsi:type="Database" dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" connectionString="server=192.168.137.10;database=MSDB;user=root;password=mysql@local;">
<commandText>
INSERT INTO TblLogrecords (LogDate,LogLevel,Logger,Message,MachineName,MachineIp,NetRequestMethod,NetRequestUrl,NetUserIsauthenticated,NetUserAuthtype,NetUserIdentity,Exception)
VALUES(@LogDate,@LogLevel,@Logger,@Message,@MachineName,@MachineIp,@NetRequestMethod,@NetRequestUrl,@NetUserIsauthenticated,@NetUserAuthtype,@NetUserIdentity,@Exception);
</commandText>
<parameter name="@LogDate" layout="${date}" />
<parameter name="@LogLevel" layout="${level}" />
<parameter name="@Logger" layout="${logger}" />
<parameter name="@Message" layout="${message}" />
<parameter name="@MachineName" layout="${machinename}" />
<parameter name="@MachineIp" layout="${aspnet-request-ip}" />
<parameter name="@NetRequestMethod" layout="${aspnet-request-method}" />
<parameter name="@NetRequestUrl" layout="${aspnet-request-url}" />
<parameter name="@NetUserIsauthenticated" layout="${aspnet-user-isauthenticated}" />
<parameter name="@NetUserAuthtype" layout="${aspnet-user-authtype}" />
<parameter name="@NetUserIdentity" layout="${aspnet-user-identity}" />
<parameter name="@Exception" layout="${exception:tostring}" />
</target>
<!--输出文件-->
<target name="log_file" xsi:type="File" fileName="${basedir}/logs/${shortdate}.log" layout="${longdate} | ${level:uppercase=false} | ${logger} | ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}" />
<!--ColoredConsole彩色控制台 xsi:type="Console"是指定输出到普通控制台-->
<target name="log_console" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="true" layout="${longdate}|${level}|${logger}|${message} ${exception}">
<highlight-row condition="level == LogLevel.Trace" foregroundColor="DarkGray" />
<highlight-row condition="level == LogLevel.Debug" foregroundColor="Gray" />
<highlight-row condition="level == LogLevel.Info" foregroundColor="White" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Magenta" backgroundColor="White" />
</target>
</targets>
<rules>
<!--跳过所有级别的Microsoft组件的日志记录-->
<!--<logger name="Microsoft.*" maxlevel="Info" final="true" />-->
<!-- BlackHole without writeTo -->
<!--只通过数据库记录日志,这里的*,如果给了name名字,代码里用日志记录的时候,取logger需要把name当做参数-->
<logger name="*" minlevel="Info" writeTo="log_database" />
<logger name="*" minlevel="Trace" writeTo="log_console" />
<logger name="*" minlevel="Warn" writeTo="log_file" />
</rules>
</nlog>
说明:
internalLogLevel="Warn" internalLogFile="${basedir}/logs/NlogRecords.log"
这段内容,指定了当NLog自己遇到Warn等级以上的报错时,生成日志到./logs/NlogRecords.log下(网站的相对路径)- 下面介绍一共设了的三种target:
- log_database 写入到数据库:
- 根据dbProvider不同,写入到不同类型的数据库,其他数据库类型可以查看官方文档
- connectionString即数据库连接字符串,我们会在项目启动时,调用
EnsureNlogConfig
方法确保它和appsettings.json
一致
- log_file 输出到文件:
- 文件名按日期命名写入到./logs文件夹下
- log_console 输出到彩色控制台:
- 此时应用的是ColoredConsole即彩色控制台
- 我自定义了一些highlight-row,自己指定了各等级日志的颜色
- log_database 写入到数据库:
- rules应用规则,给三种target限定了三种等级:
- 首先写入控制台最小等级为Trace,这样我们在调试时,任何日志我们都能在控制台看到
- 写入数据库的最小等级为Info,这样并不是所以日志都写入数据库,仅当日志等级大于等于Info时才写入
- 当发生一些警告、致命错误时,必须以文件形式记录下来,所以写入文件的最小等级为Warn
- 以上是我自己的配置,可以自行配置等级
网站项目应用NLog服务
在MS.WebApi
应用程序的Program.cs
类中,添加以下代码至Main方法中:
//using MS.WebCore.Logger
//添加以上using引用
//确保NLog.config中连接字符串与appsettings.json中同步
NLogExtensions.EnsureNlogConfig("NLog.config", "MySQL", scope.ServiceProvider.GetRequiredService<IConfiguration>().GetSection("ConectionStrings:MSDbContext").Value);
在CreateHostBuilder
方法内,追加使用NLog服务.AddNlogService()
修改后如下图所示:
此时启动项目,可以看到控制台的信息已经发生了变化:
黄色的那部分日志是EntityFrameworkCore自带的日志,在5-网站数据库实体设计及映射配置这一章节中,指定了EFCore使用原生日志方法,而不是NLog日志,因为很可能数据库还没有创建,而NLog要写入到数据库时找不到数据库造成性能下降
修改原生日志等级
在MS.WebApi
应用程序中,打开appsettings.Development.json
文件,修改LogLevel的默认等级为Trace,Microsoft.Hosting.Lifetime的等级为Warning:
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
}
}
修改后如下图
如此一来,在开发调试阶段,微软原生的Logging日志记录最小等级为Trace,Microsoft.Hosting.Lifetime的等级为Warning以下的日志都会跳过
测试写日志
Program.cs
类的Main方法修改为以下内容:
//using NLog;
//添加以上using引用
public static void Main(string[] args)
{
Logger logger = LogManager.GetCurrentClassLogger();
try
{
var host = CreateHostBuilder(args).Build();
logger.Trace("网站启动中...");
using (IServiceScope scope = host.Services.CreateScope())
{
logger.Trace("初始化NLog");
//确保NLog.config中连接字符串与appsettings.json中同步
NLogExtensions.EnsureNlogConfig("NLog.config", "MySQL", scope.ServiceProvider.GetRequiredService<IConfiguration>().GetSection("ConectionStrings:MSDbContext").Value);
logger.Trace("初始化数据库");
//初始化数据库
DBSeed.Initialize(scope.ServiceProvider.GetRequiredService<IUnitOfWork<MSDbContext>>());
//for test -start
//用于查看彩色控制台样式,以及日志等级过滤
logger.Trace("Test For Trace");
logger.Debug("Test For Debug");
logger.Info("Test For Info");
logger.Warn("Test For Warn");
logger.Error("Test For Error");
logger.Fatal("Test For Fatal");
//for test -end
}
logger.Trace("网站启动完成");
host.Run();
}
catch (Exception ex)
{
logger.Fatal(ex, "网站启动失败");
throw;
}
}
用于测试的代码使用完记得注释掉!
WeatherForecastController.cs
类中Get方法添加以下代码:
_logger.LogTrace("WeatherForecast被调用");
完成后代码如下图所示
启动项目,可以看到各日志都按对应等级过滤,写入到不同的目标中:
启动Postman,调用http://localhost:5000/weatherforecast
,可以看到原生日志方法已经被NLog替换了:
说明
- 和之前的文章ASP.NET Core中使用NLog记录日志相比:
- 写入数据库时,我去除了LogType这种自定义字段,这样就可以使用原生的写日志方法
- 不用方法去区别写入数据库还是写入文件,由日志等级自行去过滤,例如之前《ASP.NET Core中使用NLog记录日志》文章中,日志写入文件要调用
NLogUtil.WriteFileLog
方法,而现在,日志写入文件是按日志等级来的,大于指定的等级自动写入文件
- 文章一共有三处指定了默认的日志等级:
- NLogExtensions的AddNlogService方法里
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace)
- appsettings.json的LogLevel里
"Default": "Information"
- appsettings.Development.json的LogLevel里
"Default": "Trace"
- 以上三处全是指定的微软原生Logging的日志默认等级,而不是NLog的,要注意理解和区别。例如把appsettings.Development.json的LogLevel里
"Default": "Trace"
等级提高至Information,此时再用Postman调用http://localhost:5000/weatherforecast
,你会发现控制台没有日志输出了,因为我们在WeatherForecastController中写的日志等级为LogTrace,被过滤掉了
- NLogExtensions的AddNlogService方法里
项目完成后,如下图所示:
ASP.NET Core搭建多层网站架构【7-使用NLog日志记录器】的更多相关文章
- ASP.NET Core搭建多层网站架构【0-前言】
2020/01/26, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构 目录 0-前言 1-项目结构分层建立 2-公共基 ...
- ASP.NET Core搭建多层网站架构【1-项目结构分层建立】
2020/01/26, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[1-项目结构分层建立] 文章目录 此分支项目代码 ...
- ASP.NET Core搭建多层网站架构【2-公共基础库】
2020/01/28, ASP.NET Core 3.1, VS2019,Newtonsoft.Json 12.0.3, Microsoft.AspNetCore.Cryptography.KeyDe ...
- ASP.NET Core搭建多层网站架构【3-xUnit单元测试之简单方法测试】
2020/01/28, ASP.NET Core 3.1, VS2019, xUnit 2.4.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[3-xUnit单元测试 ...
- ASP.NET Core搭建多层网站架构【4-工作单元和仓储设计】
2020/01/28, ASP.NET Core 3.1, VS2019, Microsoft.EntityFrameworkCore.Relational 3.1.1 摘要:基于ASP.NET Co ...
- ASP.NET Core搭建多层网站架构【5-网站数据库实体设计及映射配置】
2020/01/29, ASP.NET Core 3.1, VS2019, EntityFrameworkCore 3.1.1, Microsoft.Extensions.Logging.Consol ...
- ASP.NET Core搭建多层网站架构【6-注册跨域、网站核心配置】
2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站 ...
- ASP.NET Core搭建多层网站架构【8.1-使用ViewModel注解验证】
2020/01/29, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[8.1-使用ViewModel注解验证] 使用V ...
- ASP.NET Core搭建多层网站架构【8.2-使用AutoMapper映射实体对象】
2020/01/29, ASP.NET Core 3.1, VS2019, AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0 摘要:基 ...
随机推荐
- Leetcode 面试题 01.01. 判定字符是否唯一
实现一个算法,确定一个字符串 s 的所有字符是否全都不同. 示例 1: 输入: s = "leetcode"输出: false 示例 2: 输入: s = "abc&qu ...
- 剑指offer 面试题. 滑动窗口的最大值
题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6 ...
- 【算法学习记录-排序题】【PAT A1012】The Best Rank
To evaluate the performance of our first year CS majored students, we consider their grades of three ...
- asp.net + MVC5 入门完整教程一
原文链接:https://blog.csdn.net/qq_21419015/article/details/80311918原创凌霜残雪 最后发布于2018-05-14 17:26:30 阅读数 3 ...
- AcWing 148. 合并果子
#include <iostream> #include <algorithm> #include <queue> using namespace std; int ...
- Iris项目结构
- 【做题笔记】洛谷P1036 选数
作为一个 DFS 初学者这题真的做得很惨...其实窝学 DFS 一年多了,然后一开始就学不会最近被图论和数据结构打自闭后才准备好好学一学233 一开始,直接套框架,于是就有 #include < ...
- tomcat、nginx、apache、tengine都是什么,及其作用
Tomcat的功能职责:Tomcat运行在JVM之上,它和HTTP服务器一样,绑定IP地址并监听TCP端口,同时还包含以下指责: • 管理Servlet程序的生命周期• 将URL映射到指定的Ser ...
- RegExp-dotAll
//.不能匹配四个字节的utf16字符和行终止符\n,\r console.log(/foo.bar/.test('foo\nbar')) //false //dotAll console.log(/ ...
- C++转换构造函数和隐式转换函数
今天是第一次听到C++还有个转换构造函数,之前经常见到默认构造函数.拷贝构造函数.析构函数,但是从没听说过转换构造函数,隐式转换函数也是一样,C++的确是够博大精深的,再次叹服! 其 ...