.NetCore框架集成了内置日志中间件,且该中间件在默认状态下处于启用状态
1 内置日志中间件
1.1 重构Web.Controllers.HomeController.HomeController方法
public HomeController(SqlSugarContext context, ILogger<HomeController> logger)
{
_context = context;
_logger = logger;
}
1.2 重构Web.Controllers.HomeController.Index方法
在该方法中定义以下代码:
//调用.NetCore框架内置日志中间件。
_logger.LogTrace("这是内置【跟踪】日志方法!");
_logger.LogDebug("这是内置【调用】日志方法!");
_logger.LogInformation("这是内置【信息】日志方法!");
_logger.LogWarning("这是内置【警告】日志方法!");
_logger.LogError("这是内置【错误】日志方法!");
_logger.LogCritical("这是内置【严重】日志方法!");
按F5执行结果如下:
从上图上可以看出,内置日志中间件中的方法“LogTrace”和“LogDebug”并不在控件台中输出日志信息。
2 自定义日志
在当前实际开发中日志的主要作用是:
- 安全追踪,即当前网站受到攻击时,能够通用日志纪录信息查找出攻击方,及其通过那个类的进行攻击,以便开发者针对该方法进行打补丁。
- 商业网站中,也有利用日志数据,进行数据分析后,向终端用户进行精确推送。
不管上面的那功能都不是内置日志中间件所能达到的,首先内置日志中间件中的数据不能被持久化就是硬伤,所以开发者一半情况下会根据内置日志中间件的实现逻辑,自定义能够持久化的日志实体。
2.1 LogLevel
/// <summary>
/// 【日志级别--枚举】
/// <remarks>
/// 摘要:
/// 该枚举定义了5种日志级别,日志级别的严格成度从上往下递增,通过枚举实例选定其中的1种,可以过滤掉低级别的日志信息。
/// </remarks>
/// </summary>
public enum LogLevel
{
/// <summary>
/// 【调试】
/// <remarks>
/// 摘要:
/// 设置处于“调试”级别的“日志级别--枚举”实例。
/// </remarks>
/// </summary>
Debug = 10,
/// <summary>
/// 【信息】
/// <remarks>
/// 摘要:
/// 设置处于“信息”级别的“日志级别--枚举”实例。
/// </remarks>
/// </summary>
Information = 20,
/// <summary>
/// 【警告】
/// <remarks>
/// 摘要:
/// 设置处于“警告”级别的“日志级别--枚举”实例。
/// </remarks>
/// </summary>
Warning = 30,
/// <summary>
/// 【错误】
/// <remarks>
/// 摘要:
/// 设置处于“错误”级别的“日志级别--枚举”实例。
/// </remarks>
/// </summary>
Error = 40,
/// <summary>
/// 【灾难】
/// <remarks>
/// 摘要:
/// 设置处于“灾难”级别的“日志级别--枚举”实例。
/// </remarks>
/// </summary>
Fatal = 50
}
2.2 Log
/// <summary>
/// 【日志--类】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过该实体类及其属性成员,用于实现当前程序【Data】.【领域】.【日志集】.【日志】实体与“[SqlSugarAcquaintance].[Log]”表之间的CURD的交互操作,
/// 并把这些数据存储到数据库设置实例中(内存)。
/// </remarks>
public class Log
{
/// <summary>
/// 【编号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定“日志”实例的整型编号值。
/// </remarks>
/// </summary>
[SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
public int Id { get; set; }
/// <summary>
/// 【日志级别编号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定“日志级别--枚举”实例的整型值。
/// </remarks>
/// </summary>
public int LogLevelId { get; set; }
/// <summary>
/// 【实体名称】
/// <remarks>
/// 摘要:
/// 获取/设置调用1个指定“日志”实例方法及其该方法所在类的全名。
/// </remarks>
/// </summary>
[SugarColumn(ColumnDataType = "nvarchar", Length = int.MaxValue)]
public string EntityName { get; set; }
/// <summary>
/// 【IP地址】
/// <remarks>
/// 摘要:
/// 获取/设置触发1个指定“日志”实例的客户端的IP地址。
/// </remarks>
/// </summary>
[SugarColumn(ColumnDataType = "nvarchar", Length = 255)]
public string IpAddress { get; set; }
/// <summary>
/// 【短信息】
/// <remarks>
/// 摘要:
/// 获取/设置对1个指定“日志”实例进行描述的简短信息。
/// </remarks>
/// </summary>
[SugarColumn(ColumnDataType = "nvarchar", Length = 255)]
public string ShortMessage { get; set; }
/// <summary>
/// 【完整信息】
/// <remarks>
/// 摘要:
/// 获取/设置对1个指定“日志”实例进行描述的完整信息。
/// </remarks>
/// </summary>
[SugarColumn(ColumnDataType = "nvarchar", Length = int.MaxValue)]
public string FullMessage { get; set; }
/// <summary>
/// 【新建日期时间】
/// <remarks>
/// 摘要:
/// 获取/设置“日志”实体的1个指定实例,第1次被持久化存储到数据库对应表中的(本地)时间。
/// </remarks>
/// </summary>
public DateTime CreatedDataTime { get; set; }
}
2.3 重构Data.SqlSugarContext.CreateTable方法
//通过过滤操作,获取领域文件夹中的所有实体的类型实例。
//注意:“Where”过滤操作中“ c.Namespace”最好使用“Contains”,而不要使用“==”。
Type[] _typeArray = assembly.GetTypes()
.Where(c => c.Namespace.Contains("Data.Domain")&& c.IsClass)//过滤操作。
.ToArray();
注意:
在Code-Frist模式下SqlSugarCore中间件通过标记来定义实体与表之间的约束关系,使实体与约束定义产生了极强的耦合,并且当前本人没有在SqlSugarCore中间件找到关于忽略实体与表之间约束关系的标记即“[SugarTable]”标记不存在“IsIgnore = true”,所以实体的支持类最好不要定义在“Domain”文件夹中。
2.4 IpAddressExtension
/// <summary>
/// 【IP地址扩展】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过该类中的方法获取字符串类型的客户端IP地址。
/// </remarks>
public static class IpAddressExtension
{
///<param name="context">HTTP下文实例。</param>
/// <summary>
/// 【获取客户端IP地址】
/// <remarks>
/// 摘要:
/// 该方法用于获取客户端IP地址字符串。
/// </remarks>
/// </summary>
public static string GetClientUserIp(this HttpContext context)
{
var userHostAddress = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (string.IsNullOrEmpty(userHostAddress))
{
userHostAddress = context.Connection.RemoteIpAddress.ToString();
}
//最后判断获取是否成功,并检查IP地址的格式(检查其格式非常重要)
if (!string.IsNullOrEmpty(userHostAddress) && IsIP(userHostAddress))
{
return userHostAddress;
}
return "127.0.0.1";
}
///<param name="ip">1个指定的字符串。</param>
/// <summary>
/// 【IP地址?】
/// <remarks>
/// 摘要:
/// 通过相应的参数实例,验证1个指定的字符串是否与IP地址格式相匹配。
/// </remarks>
/// </summary>
/// <returns>
/// 返回:
/// 1个值false(不匹配)/true(匹配)。
/// </returns>
public static bool IsIP(string ip)
{
return System.Text.RegularExpressions.Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
}
}
2.5 重构Program.cs
//解决Ubuntu Nginx 代理不能获取IP问题。
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
2.6重构Web.Controllers.HomeController.Index方法
在该方法中定义以下代码:
//实例化自定义日志实体的1个指定实例,并把该实例持久化到日志表中。
Log _model = new Log
{
LogLevelId = (int)LogLevel.Information,
EntityName = typeof(HomeController).FullName +".Index",
IpAddress= HttpContext.Request.HttpContext.GetClientUserIp(),
ShortMessage = "获取与考试相关的所有数据,为页面的渲染显示提供支撑。",
FullMessage = "获取与考试相关的所有数据,为页面的渲染显示提供支撑。",
CreatedDataTime = DateTime.Now,
};
await _context.SugarScope.Insertable(_model).ExecuteReturnEntityAsync();
按F5执行结果如下:
对以上功能更为具体实现和注释见:22-10-08-06_SqlSugarAcquaintance(初识SqlSugarCore之内置与自定义日志)。