目录
[一、引入 NuGet 包](#一、引入 NuGet 包)
[五、AOP -- 拦截器 -- 封装](#五、AOP -- 拦截器 -- 封装)
一、引入 NuGet 包
log4net
Microsoft.Extensions.Logging.Log4Net.AspNetCore
MySql.Data ---- MySQL数据库需要
Newtonsoft.Json
Autofac
Autofac.Extensions.DependencyInjection
Autofac.Extras.DynamicProxy
二、配置log4net.config
注:当前网上有两种 log4net.config 配置文件, 一种是以<log4net>为根目录, 另一种以<configuration> 为根目录
XML
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<!--正常日志:::记录正常日志-->
<!-- appender 定义日志输出方式 将日志以回滚文件的形式写到MySQL数据库中。-->
<appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
<!-- 代表缓存大小,在没达到缓存大小时,暂时不会存到数据库中, -->
<!-- 当程序关闭之后,会将未插入的信息加入到数据库中 -->
<bufferSize value="1" />
<!--引入《MySql.Data》包-->
<param name="ConnectionType" value="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" />
<!--配置连接数据库的字符串-->
<param name="ConnectionString" value="server=localhost;database=TTTTT;uid=root;pwd=123456;"/>
<!--配置MySQL的插入语句-->
<param name="CommandText" value="insert into log4net(log_datetime,log_thread,log_level,log_logger,log_message)
values(@log_datetime, @log_thread , @log_level, @log_logger, @log_message)" />
<param name="Parameter">
<param name="ParameterName" value="@log_datetime" />
<param name="DbType" value="DateTime" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy'-'MM'-'dd HH':'mm':'ss}" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName" value="@log_thread" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%t" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName" value="@log_level" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%p" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName" value="@log_logger" />
<param name="DbType" value="String" />
<param name="Size" value="255" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%c" />
</param>
</param>
<param name="Parameter">
<param name="ParameterName" value="@log_message" />
<param name="DbType" value="String" />
<param name="Size" value="4000" />
<param name="Layout" type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%m" />
</param>
</param>
</appender>
<!--正常日志:::记录正常日志-->
<!--按日期分割日志文件 一天一个-->
<!-- appender 定义日志输出方式 将日志以回滚文件的形式写到文件中。-->
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<!-- 定义中文编码类型 UTF-8-->
<param name="Encoding" value="utf-8" />
<!--定义文件存放位置-->
<file value="Log\log_"/>
<!--是否追加到文件-->
<appendToFile value="true"/>
<!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log,官方说线程非安全-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<!--最多产生的日志文件数,超过则只保留最新的n个。设定值value="-1"为不限文件数-->
<maxSizeRollBackups value="-1"/>
<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
<rollingStyle value="Composite"/>
<datePattern value="yyyy\\yyyyMM\\yyyyMMdd'.txt'"/>
<!--是否只写到一个文件中-->
<staticLogFileName value="false"/>
<!--每个文件的大小。只在混合方式与文件大小方式下使用。
超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。
可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
<maximumFileSize value="100MB"/>
<!--计数类型为1,2,3...-->
<!--<param name="CountDirection" value="1"/>-->
<layout type="log4net.Layout.PatternLayout">
<!--输出格式-样例:
记录时间:2022-08-24 17:59:31,172 线程ID:[4] 日志级别:INFO 当前类:Log4NetDemo.MainClass
日志描述:创建连接。-->
<conversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 当前类:%logger %newline日志描述:%message %newline %newline"/>
</layout>
</appender>
<!--错误日志:::记录错误日志-->
<!--按日期分割日志文件 一天一个-->
<!-- appender 定义日志输出方式 将日志以回滚文件的形式写到文件中。-->
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
<!-- 定义中文编码类型 UTF-8-->
<param name="Encoding" value="utf-8" />
<!--定义文件存放位置-->
<file value="Log\error_"/>
<!--是否追加到文件-->
<appendToFile value="true"/>
<!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log,官方说线程非安全-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<!--最多产生的日志文件数,超过则只保留最新的n个。设定值value="-1"为不限文件数-->
<maxSizeRollBackups value="-1"/>
<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
<rollingStyle value="Composite"/>
<datePattern value="yyyy\\yyyyMM\\yyyyMMdd'.txt'"/>
<!--是否只写到一个文件中-->
<staticLogFileName value="false"/>
<!--每个文件的大小。只在混合方式与文件大小方式下使用。
超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。
可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
<maximumFileSize value="100MB"/>
<!-- layout 控制Appender的输出格式,也可以是xml 一个Appender只能是一个layout-->
<layout type="log4net.Layout.PatternLayout">
<!--每条日志末尾的文字说明-->
<!--输出格式 模板-->
<!-- <param name="ConversionPattern" value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger
操作者ID:%property{Operator} 操作类型:%property{Action}%n 当前机器名:%property%n当前机器名及登录用户:%username %n
记录位置:%location%n 消息描述:%property{Message}%n 异常:%exception%n 消息:%message%newline%n%n" />-->
<!--样例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info-->
<!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别: %-5level %n错误描述:%message%newline %n"/>-->
<conversionPattern value="%n==========
%n【日志级别】%-5level
%n【记录时间】%date
%n【执行时间】[%r]毫秒
%n【错误位置】%logger 属性[%property{NDC}]
%n【错误描述】%message
%n【错误详情】%newline"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter,log4net">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
</appender>
<!--DEBUG:::记录DEBUG日志-->
<!--按日期分割日志文件 一天一个-->
<!-- appender 定义日志输出方式 将日志以回滚文件的形式写到文件中。-->
<appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
<!-- 定义中文编码类型 UTF-8-->
<param name="Encoding" value="utf-8" />
<!--定义文件存放位置-->
<file value="Log\debug_"/>
<!--是否追加到文件-->
<appendToFile value="true"/>
<!--记录日志写入文件时,不锁定文本文件,防止多线程时不能写Log,官方说线程非安全-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
<!--最多产生的日志文件数,超过则只保留最新的n个。设定值value="-1"为不限文件数-->
<maxSizeRollBackups value="-1"/>
<!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
<rollingStyle value="Composite"/>
<datePattern value="yyyy\\yyyyMM\\yyyyMMdd'.txt'"/>
<!--是否只写到一个文件中-->
<staticLogFileName value="false"/>
<!--每个文件的大小。只在混合方式与文件大小方式下使用。
超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。
可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
<maximumFileSize value="100MB"/>
<!-- layout 控制Appender的输出格式,也可以是xml 一个Appender只能是一个layout-->
<layout type="log4net.Layout.PatternLayout">
<!--输出格式-样例:
记录时间:2022-08-24 17:59:31,172 线程ID:[4] 日志级别:INFO 当前类:Log4NetDemo.MainClass
日志描述:创建连接。-->
<conversionPattern value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 当前类:%logger %newline日志描述:%message %newline %newline"/>
</layout>
<filter type="log4net.Filter.LevelRangeFilter,log4net">
<levelMin value="DEBUG" />
<levelMax value="WARN" />
</filter>
</appender>
<root>
<!--日志等级:OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL-->
<level value="ALL" />
<appender-ref ref="ADONetAppender" />
<appender-ref ref="RollingFile" />
<appender-ref ref="ErrorAppender" />
<appender-ref ref="DebugAppender" />
</root>
</log4net>
三、编写Log4net封装类
cs
public static class CustomLog4netExt
{
public static void AddLog4netExt(this WebApplicationBuilder builder)
{
// 添加 Log4net 配置文件
builder.Logging.AddLog4Net("log4net.config");
// log错误日志配置
builder.Services.AddControllers(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
options.Filters.Add(typeof(GlobalExceptionsFilter));
});
}
}
四、编写日志记录类
全局报错监测类
cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Log4Test.Log4;
/// <summary>
/// 全局异常错误日志
/// </summary>
public class GlobalExceptionsFilter : IExceptionFilter
{
private readonly IWebHostEnvironment _env;
private readonly ILogger<GlobalExceptionsFilter> _logger;
public GlobalExceptionsFilter(IWebHostEnvironment env, ILogger<GlobalExceptionsFilter> logger)
{
_env = env;
_logger = logger;
}
public void OnException(ExceptionContext context)
{
var json = new JsonErrorResponse();
json.Message = context.Exception.Message;//错误信息
if (_env.IsDevelopment())
{
json.DevelopmentMessage = context.Exception.StackTrace;//堆栈信息
}
context.Result = new InternalServerErrorObjectResult(json);
//采用log4net 进行错误日志记录
_logger.LogError(WriteLog(json.Message, context.Exception));
}
/// <summary>
/// 自定义返回格式
/// </summary>
/// <param name="throwMsg"></param>
/// <param name="ex"></param>
/// <returns></returns>
public string WriteLog(string throwMsg, Exception ex)
{
return string.Format("【自定义错误】:{0} \r\n" +
"【异常类型】:{1} \r\n" +
"【异常信息】:{2} \r\n" +
"【堆栈调用】:{3}",
new object[] {
throwMsg,
ex.GetType().Name,
ex.Message,
ex.StackTrace
});
}
}
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object value) : base(value)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
//返回错误信息
public class JsonErrorResponse
{
/// <summary>
/// 生产环境的消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 开发环境的消息
/// </summary>
public string DevelopmentMessage { get; set; }
}
日常方法监测类
cs
using Castle.DynamicProxy;
using Newtonsoft.Json;
namespace LOG.Test;
/// <summary>
/// 自定义 方法拦截器
/// </summary>
public class CustomInterceptor : IInterceptor
{
private readonly ILogger<CustomInterceptor> _logger;
public CustomInterceptor(ILogger<CustomInterceptor> logger)
{
_logger = logger;
}
public void Intercept(IInvocation invocation)
{
string result = $"开始执行-->\n方法名: {invocation.Method.Name}\n";
foreach (var item in invocation.Arguments)
{
result += $"入参---{JsonConvert.SerializeObject(item)}\n";
}
_logger.LogInformation(result);
invocation.Proceed();
_logger.LogInformation($"结束执行-->\n方法名: {invocation.Method.Name}\n" +
$"返回结果---{JsonConvert.SerializeObject(invocation.ReturnValue)}\n");
}
}
五、AOP -- 拦截器 -- 封装
cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Extras.DynamicProxy;
namespace LOG.Test;
public static class CustomAOPExt
{
public static void AddAOPExt(this WebApplicationBuilder builder)
{
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) // 工厂替换,把Autofac整合进来
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
// 依赖注入
containerBuilder.RegisterType<CustomInterceptor>();
containerBuilder.RegisterType<Student>().As<IStudent>().EnableClassInterceptors(); // 开启类拦截器
//containerBuilder.RegisterType<Student>().As<IStudent>().EnableInterfaceInterceptors(); // 开启接口拦截器
});
}
}
六、案例编写
cs
// 在Program.cs 中
// 添加 lg4net 扩展
builder.AddLog4netExt();
// 添加 AOP 扩展
builder.AddAOPExt();
创建 Student 和 IStudent 测试类和接口
创建 一个 控制器
七、结果展示
如有错误,烦请批评指正