目录
1. 安装NuGet包 Microsoft.Extensions.Logging.Log4Net.AspNetCore 和 log4ne
一. 日志的基本使用
1. 安装NuGet包 Microsoft.Extensions.Logging.Log4Net.AspNetCore 和 log4ne
2. 在program.cs中开启日志
//开启日志
builder.Logging.AddLog4Net();
3, 添加日志配置文件log4Net.config
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="DebugAppender" type="log4net.Appender.DebugAppender" >
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<!--指定日记记录方式,以滚动文件的方式(文件记录)-->
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<!--日志路径-->
<file value="log\" />
<!--是否是向文件中追加日志-->
<appendToFile value="true" />
<!--log保留天数-->
<param name= "MaxSizeRollBackups" value= "10"/>
<!--每个文件最大3M-->
<param name="maximumFileSize" value="3MB" />
<!--日志根据日期滚动-->
<param name="RollingStyle" value="Date" />
<!--日志文件名格式为:logs_20230431.log-->
<param name="DatePattern" value=""logs_"yyyyMMdd".log"" />
<!--日志文件名是否是固定不变的-->
<param name="StaticLogFileName" value="false" />
<!--布局-->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %5level %logger.%method [%line] - MESSAGE: %message%newline %exception" />
</layout>
</appender>
<root>
<level value="ALL"/>
<appender-ref ref="DebugAppender" />
<appender-ref ref="RollingFile" />
</root>
</log4net>
4.添加全局异常日志
/// <summary>
/// 异常过滤器,全局捕获异常
/// </summary>
public class ClubExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly ILogger<ClubExceptionFilterAttribute> _logger;
public ClubExceptionFilterAttribute(ILogger<ClubExceptionFilterAttribute> logger)
{
_logger = logger;
}
/// <summary>
/// 异常处理,存在未处理的异常时,返回500错误
/// </summary>
/// <param name="context"></param>
public override void OnException(ExceptionContext context)
{
// 如果异常没有被处理,则进行处理
if (context.ExceptionHandled == false)
{
// 记录异常信息
var str = $"异常: {context.HttpContext.Request.Path} {context.Exception.Message}";
// 记录日志
_logger.LogWarning(str);
context.Result = new ContentResult
{
StatusCode = StatusCodes.Status500InternalServerError,
Content = "服务器异常,请联系管理员"
};
// 标记异常已处理
context.ExceptionHandled = true;
}
}
}
此时我们运行项目,简单的日志文件就生成了
二. 更详细的日志设计
在项目中,一般为了方便维护,简单的日志是难以达到要求的,此时我们就需要更加具体的日志来完成项目的监护.我这里选择将日志信息保存到数据库
1. 创建实体类
public class ClubLogs
{
public long Id { get; set; }
/// <summary>
/// 操作人Id
/// </summary>
/// <summary>
/// 请求方式
/// </summary>
[Comment("接口的方法")]
public string Method { get; set; } = string.Empty;
/// <summary>
/// 请求参数
/// </summary>
[Comment("请求参数")]
public string Parameters { get; set; } = string.Empty;
....
....
....
/// <summary>
/// 返回值
/// </summary>
[Comment("返回值")]
public string ReturnValue { get; set; } = string.Empty;
}
2. 使用EF Core生成数据表
这个步骤就不展示了,比较简单
3.实现Service层
1. 首先添加服务接口
public interface IClubLogService
{
/// <summary>
/// 添加审核日志
/// </summary>
/// <param name="clubLogs"></param>
/// <returns></returns>
ClubLogs Add(ClubLogs clubLogs);
}
2. 实现接口
public class ClubLogService : BaseService, IClubLogService
{
private readonly IBaseRepository<ClubLogs> _clubLogRepository;
private readonly ILogger<ClubLogService> _logger;
public ClubLogService(IBaseRepository<ClubLogs> clubLogRepository, ILogger<ClubLogService> logger)
{
_clubLogRepository = clubLogRepository;
_logger = logger;
}
public ClubLogs Add(ClubLogs clubLogs)
{
try
{
return _clubLogRepository.Add(clubLogs);
}
catch (Exception ex)
{
_logger.LogError(ex, "保存日志数据库异常,接口: {0}\r\nMethod: {1}\r\n参数: {2}\r\nIP: {3}\r\n花费时间: {4}ms",
clubLogs.ApiUrl, clubLogs.Method, clubLogs.Parameters, clubLogs.ClientIpAddress, clubLogs.ExecutionTime);
return clubLogs;
}
}
}
4. 创建方法过滤器
方法过滤器主要用于在方法执行前和方法执行后保存相关日志记录
1. 写一个方法用于获取需要保存的日志信息
public class ClubLogHelper
{
public static ClubLogs RequestLogs(ActionContext action, UserManager<User> userManager)
{
ClubLogs logs = new ClubLogs();
var httpContext = action.HttpContext;
var request = httpContext.Request;// 请求对象
//当前用户
var userName = httpContext.User.Identity.Name;
if (!string.IsNullOrEmpty(userName))
{
var user = userManager.FindByNameAsync(userName).Result;
logs.CreateId = user.Id;
}
logs.ApiUrl = request.Path; // 请求路径
logs.Method = request.Method; // 请求方法
logs.AuditLogType = AuditLogType.Info; // 日志类型
var remoteIpAddress = httpContext.Connection.RemoteIpAddress; // 获取客户端IP地址
// 映射为IPv4地址
logs.ClientIpAddress = remoteIpAddress == null ? string.Empty : remoteIpAddress.MapToIPv4().ToString();
// 获取浏览器信息
logs.BrowserInfo = request.Headers.ContainsKey("User-Agent") ? request.Headers["User-Agent"].ToString() : string.Empty;
//获取参数
if (request.Method == "GET")
{
logs.Parameters = request.QueryString.Value ?? string.Empty;
}
else
{
// 获取请求体
using (StreamReader reader = new StreamReader(request.Body))
{
request.Body.Position = 0; //保持从头开始读取
logs.Parameters = reader.ReadToEndAsync().Result; // 读取所以请求体内容
request.Body.Position = 0; // 重置请求体位置
}
}
return logs;
}
}
2. 允许body被重复访问
这里有个问题是Request.Body默认是不允许重复读取的,因此我们要在program.cs中允许body重复读取.
//允许body重复用
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
3. 创建方法过滤器
/// <summary>
/// 方法过滤器
/// </summary>
public class ClubLogActionFilterAttribute : ActionFilterAttribute
{
private ClubLogs _clubLogs;
private readonly IClubLogService _clubLogService;
private readonly UserManager<User> _userManager;
private readonly ILogger<ClubLogActionFilterAttribute> _logger;
private DateTime _executionBefore;
private DateTime _executionAfter;
public ClubLogActionFilterAttribute(IClubLogService clubLogService, UserManager<User> userManager, ILogger<ClubLogActionFilterAttribute> logger)
{
_clubLogs = new ClubLogs();
_clubLogService = clubLogService;
_userManager = userManager;
_logger = logger;
}
/// <summary>
/// 方法执行前
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
{
_executionBefore = DateTime.Now;
_clubLogs = ClubLogHelper.RequestLogs(context, _userManager);
}
/// <summary>
/// 方法执行完毕
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuted(ActionExecutedContext context)
{
_executionAfter = DateTime.Now;
// 方法执行时间
_clubLogs.ExecutionTime = (int)(_executionAfter - _executionBefore).TotalMilliseconds;
// 序列化返回值为JSON字符串
_clubLogs.ReturnValue = context.Result == null ? string.Empty : JsonConvert.SerializeObject(context.Result);
// 异常信息
if (context.Exception != null)
{
// 记录异常信息
_clubLogs.ExceptionMessage = context.Exception.Message;
if (_clubLogs.ExceptionMessage.Length > 1024)
{
_clubLogs.ExceptionMessage = _clubLogs.ExceptionMessage.Substring(0, 1024);
}
_clubLogs.Exception = context.Exception.ToString();
if (_clubLogs.Exception.Length > 2000)
{
_clubLogs.Exception = _clubLogs.Exception.Substring(0, 2000);
}
_clubLogs.AuditLogType = AuditLogType.Exception;
// 记录文件异常日志
_logger.LogError(context.Exception, "保存日志数据库异常,接口: {0}\r\nMethod: {1}\r\n参数: {2}\r\nIP: {3}\r\n花费时间: {4}ms",
_clubLogs.ApiUrl, _clubLogs.Method, _clubLogs.Parameters, _clubLogs.ClientIpAddress, _clubLogs.ExecutionTime);
}
_clubLogService.Add(_clubLogs);
}
}
5.全局动态添加方法过滤器
builder.Services.AddControllers(
configure =>
{
// 全局为控制器添加日志
configure.Filters.Add<ClubLogActionFilterAttribute>();
})
最后,这个日志功能就成功实现了