asp.net core版本:5.0
log4net:2.0
一般情况下,我比较习惯把日志记录到日志文件中。但这种方式有一个缺点:把项目发布到生产环境后,每次查看日志都需要远程登录到服务器去查看日志文件,有点不太方便,所以我想把日志写入到数据库中。
首先我们新建一个asp.net core webapi项目,nuget添加如下包:
向日志系统中添加log4net:
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
}).ConfigureLogging(logBuilder =>
{
logBuilder.AddLog4Net();
});
配置log4net.config:
下面我们讲解一下这个配置文件。
logger节点,name随意命名,需要与LogManager.GetLogger的name参数一样。level子节点我配置为ERROR,只记录level为ERROR的日志。appender-ref,ref属性的值指定要添加的appender,即下面的appender
我们看appender这个节点,name属性可以按照我们的喜好命名,type则必须是log4net.Appender.ADONetAppender,因为我们使用的是sqlserver数据库。如果你使用的是mysql数据库,那就不是这个了。
connectionType,因为我们已经引入了System.Data.SqlClient包,所以不用添加Version和PublicKeyToken。这里有2个地方需要注意一下:1、我看有的博客在这个地方写的是Microsoft.Data.SqlClient.SqlConnection,Microsoft.Data,但是他又没告诉我们要引入Microsoft.Data.SqlClient。这种方式能不能用我没有试过,有兴趣的可以试一下。2、这个地方你写的包名必须要引入到你的项目中。
parameter,这个节点对应insert values里面的每一个插入字段,是对插入字段做一个配置。
重点看一下layout,type属性就是对字段做配置的类名,这个值是类所在命名控件.类名,所以这个值要根据你自己的项目来确定。conversionPattern的value属性中的pattern字符串与AddConverter的第一个参数name必须相同。我的这个类的代码如下:
using log4net.Layout;
namespace LogDemo.LogDbConfig
{
public class ActionLayoutPattern : PatternLayout
{
public ActionLayoutPattern()
{
this.AddConverter("pattern", typeof(FieldsConverter));
}
}
}
FieldsConverter类就是给每个自定义字段赋值,代码如下:
using log4net.Layout.Pattern;
public class FieldsConverter : PatternLayoutConverter
{
protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
var dto = loggingEvent.MessageObject as LogItems;
switch (this.Option)
{
case "Uri":
writer.Write(dto.Uri);
break;
case "HttpMethod":
writer.Write(dto.HttpMethod);
break;
case "ErrMessage":
writer.Write(dto.ErrMessage);
break;
case "ErrStackTrace":
writer.Write(dto.ErrStackTrace);
break;
default:
writer.Write("");
break;
}
}
}
LogItems,这个类是自定义字段的类,代码如下:
public class LogItems
{
public string Uri { get; set; }
public string HttpMethod { get; set; }
public string ErrMessage { get; set; }
public string ErrStackTrace { get; set; }
}
接下来我们写入数据库:
var logData = new LogItems();
logData.Uri = Request.Path;
logData.HttpMethod = Request.Method;
logData.ErrMessage = ex.Message;
logData.ErrStackTrace = ex.StackTrace;
var iLog = LogManager.GetLogger("actionLog");
iLog.Error(logData);
这段代码是写在catch里面的,ex就是Exception的实例。Request是ControllerBase的一个属性,用来获取当前http请求的元数据。
GetLogger的参数actionLog对应log4net.config中的
LogDateTime这个字段的值layout的type=log4net.Layout.RawTimeStampLayout,由log4net赋值,所以这里不需要赋值
数据库表结构如下:
运行程序,故意写几行索引超出范围的代码。结果如下:
我这里是把错误日志写入到数据库,但是其他Level的日志我还是想写入到文件中,这该怎么做呢?其实这个也很简单,和之前只写入到文件中一样,加一个写入文件的appender即可,配置如下:
如果不需要把日志写入到数据库,那我们就可以不用log4net的ILog类来写入(LogManager.GetLogger返回的就是ILog),而是通过Microsoft.Extensions.Logging.ILogger接口来写入。这里顺便提一下这个接口,它是asp.net core自带的日志接口,内置了console logger和debug logger(还有其他的logger,我记不清了),每次调用这个接口写入方法时(LogInfo、LogError等),它就向所有实现这个接口的logger发送日志。因为我们调用了ConfigureLogging把log4net也加入了日志系统(logBuilder.AddLog4Net()),所以使用ILogger写入日志时也会把日志写入到log4net。
至此,无论是把日志写入到数据库还是文件都可以了。
最后提醒一下大家,一定要认真检查commandText的insert语句,因为这个语句是写在log4net.config配置文件中,没有语法错误提示。如果写错了,程序运行的时候也不会有任何的错误提示,让你搞不清楚到底是哪里的问题,我就在这个地方耗费了很多的时间。