asp.net core+log4net 写入日志到sqlserver

asp.net core版本:5.0

log4net:2.0

一般情况下,我比较习惯把日志记录到日志文件中。但这种方式有一个缺点:把项目发布到生产环境后,每次查看日志都需要远程登录到服务器去查看日志文件,有点不太方便,所以我想把日志写入到数据库中。

首先我们新建一个asp.net core webapi项目,nuget添加如下包:

向日志系统中添加log4net:

Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }).ConfigureLogging(logBuilder =>
                {
                    logBuilder.AddLog4Net();
                });

配置log4net.config:

<?xml version="1.0" encoding="utf-8"?>
<log4net>
  <logger name="actionLog">
    <level value="ERROR" />
    <appender-ref ref="adoNetAppender_SqlServer"/>
  </logger>
  <appender name="adoNetAppender_SqlServer" type="log4net.Appender.ADONetAppender">
    <!--日志缓存写入条数 设置为0时只要有一条就立刻写到数据库 生产环境可改为10-100写入一次-->
    <bufferSize value="1" />
    <!-- 日志数据库连接类型(此处非常重要,写错会导致无法写入数据库) -->
    <connectionType value="System.Data.SqlClient.SqlConnection,System.Data"/>
    <!--日志数据库连接串-->
    <connectionString value="server=.;database=TestDb;uid=sa;pwd=123456" />
    <!--日志数据库脚本-->
    <commandText value="INSERT INTO LogError(LogDateTime,HttpMethod,Uri,ErrMessage,ErrStackTrace) VALUES(@LogDateTime,@HttpMethod,@Uri,@ErrMessage,@ErrStackTrace)" />

    <!--日志时间 -->
    <parameter>
      <parameterName value="@LogDateTime" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter>
      <parameterName value="@HttpMethod" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{HttpMethod}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@Uri" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{Uri}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@ErrMessage" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{ErrMessage}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@ErrStackTrace" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{ErrStackTrace}"/>
      </layout>
    </parameter>
  </appender>  
  <root>  
 
  </root>
</log4net>

下面我们讲解一下这个配置文件。

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中的<logger name="actionLog">

LogDateTime这个字段的值layout的type=log4net.Layout.RawTimeStampLayout,由log4net赋值,所以这里不需要赋值

数据库表结构如下:

运行程序,故意写几行索引超出范围的代码。结果如下:

 我这里是把错误日志写入到数据库,但是其他Level的日志我还是想写入到文件中,这该怎么做呢?其实这个也很简单,和之前只写入到文件中一样,加一个写入文件的appender即可,配置如下:

<?xml version="1.0" encoding="utf-8"?>
<log4net>
  <logger name="actionLog">
    <level value="ERROR" />
    <appender-ref ref="adoNetAppender_SqlServer"/>
  </logger>
  <appender name="adoNetAppender_SqlServer" type="log4net.Appender.ADONetAppender">
    <!--日志缓存写入条数 设置为0时只要有一条就立刻写到数据库 生产环境可改为10-100写入一次-->
    <bufferSize value="1" />
    <!-- 日志数据库连接类型(此处非常重要,写错会导致无法写入数据库) -->
    <connectionType value="System.Data.SqlClient.SqlConnection,System.Data"/>
    <!--日志数据库连接串-->
    <connectionString value="server=.;database=TestDb;uid=sa;pwd=123456" />
    <!--日志数据库脚本-->
    <commandText value="INSERT INTO LogError(LogDateTime,HttpMethod,Uri,ErrMessage,ErrStackTrace) VALUES(@LogDateTime,@HttpMethod,@Uri,@ErrMessage,@ErrStackTrace)" />

    <!--日志时间 -->
    <parameter>
      <parameterName value="@LogDateTime" />
      <dbType value="DateTime" />
      <layout type="log4net.Layout.RawTimeStampLayout" />
    </parameter>
    <parameter>
      <parameterName value="@HttpMethod" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{HttpMethod}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@Uri" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{Uri}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@ErrMessage" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{ErrMessage}"/>
      </layout>
    </parameter>
    <parameter>
      <parameterName value="@ErrStackTrace" />
      <dbType value="String" />
      <layout type="LogDemo.LogDbConfig.ActionLayoutPattern">
        <conversionPattern value = "%pattern{ErrStackTrace}"/>
      </layout>
    </parameter>
  </appender>

  <appender name="RollingInfoFile" type="log4net.Appender.RollingFileAppender">
    <file value="Log/Info/" />
    <appendToFile value="true" />
    <!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log,官方说线程非安全-->
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <Encoding value="UTF-8" />
    <!-- 每个文件的大小限制  -->
    <maximumFileSize value="1MB" />
    <!-- 切割最多文件数 -1表示不限制产生日志文件数-->
    <MaxSizeRollBackups value="-1" />
    <DatePattern value="yyyyMMdd&quot;.log&quot;" />
    <!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
    <RollingStyle value="Composite" />
    <param name="DatePattern" value="yyyy-MM-dd/&quot;Info.log&quot;"  />
    <StaticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="[%date{yyyy-MM-dd HH:mm:ss}] %-5logger - %m%n"/>
    </layout>
    <!--日志等级:OFF > FATAL > ERROR > WARN > INFO > DEBUG  > ALL -->
    <filter type="log4net.Filter.LevelRangeFilter">
      <levelMin value="INFO" />
      <levelMax value="INFO" />
    </filter>
  </appender>

  <root>
    <level value="ALL"/>
    <appender-ref ref="RollingInfoFile" />
  </root>
</log4net>

如果不需要把日志写入到数据库,那我们就可以不用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配置文件中,没有语法错误提示。如果写错了,程序运行的时候也不会有任何的错误提示,让你搞不清楚到底是哪里的问题,我就在这个地方耗费了很多的时间。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值