基于EF Core存储的Serilog持久化服务
前言
Serilog是 .NET 上的一个原生结构化高性能日志库,这个库能实现一些比内置库更高度的定制。日志持久化是其中一个非常重要的功能,生产环境通常很难挂接调试器或者某些bug的触发条件很奇怪。为了在脱离调试环境的情况下尽可能保留更多线索来辅助解决生产问题,持久化的日志就显得很重要了。目前Serilog支持文件和部分数据库持久化,文件日志的查找分析比较麻烦,而使用数据库持久化则会导致特定数据库依赖。既然有EF Core这种专门负责抽象底层数据库的持久化框架,为何不直接使用呢。怀着这样的心情去Nuget找了一圈,结果一无所获,无奈又只能自己写一个。
新书宣传
有关新书的更多介绍欢迎查看《C#与.NET6 开发从入门到实践》上市,作者亲自来打广告了!
正文
对代码感兴趣的朋友可以移步Github。这里直接介绍一下基本用法。
这个库分为四个包:实体模型包定义基本实体类型;基本扩展包定义了模拟日志类别和严重性级别筛选的过滤器,方便为不同的输出目标自定义过滤器(内置的筛选器仅支持在全局使用,且会对所有输出目标生效,粒度不够细,只能自己写一个基于过滤器的扩展模拟相同的行为);配置扩展包定义了从IConfiguration
读取并构建过滤器的辅助方法,支持配置的实时自动更新;EF Core服务包定义了基于EF Core的Serilog的Sink,Sink实现批处理接口,能避免频繁向数据库插入单条日志记录。方便为分离项目的解决方案按需引用,减少无关类型的污染。
以在ASP.NET Core中使用为例:
实体模型和上下文
public class YourLogRecord : LogRecord
{
public int YourProperty { get; set; }
}
public class YourApplicationDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 使用默认类型。
modelBuilder.UseLogRecord(b =>
{
b.ToTable($"{nameof(LogRecord)}s");
});
// 使用自定义类型,需要继承LogRecord。
modelBuilder.UseLogRecord<YourLogRecord>(b =>
{
b.ToTable($"{nameof(YourLogRecord)}s");
});
}
}
public class YourLogDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseLogRecord(b =>
{
b.ToTable($"{nameof(LogRecord)}s", tb => tb.ExcludeFromMigrations());
});
modelBuilder.UseLogRecord<YourLogRecord>(b =>
{
b.ToTable($"{nameof(YourLogRecord)}s", tb => tb.ExcludeFromMigrations());
});
}
}
需要注意,一定要使用两个不同的上下文类型,其中一个专用于存储日志数据。因为EF Core本身也会产生日志,如果使用一个上下文,一般配置下一定会产生无限循环。EF Core产生日志,通过EF Core写入日志,写入日志会导致产生新的EF Core日志……读取日志可以使用日志上下文,这样的话日志实体只需要日志上下文配置即可。不过还是推荐在主要上下文同时注册日志模型,这样读取日志产生的EF Core日志就可以安全的写入了。
使用两个上下文的情况下可以在日志上下文中配置实体从迁移中排除,把日志表迁移托管给主上下文。
服务注册
// 注册主上下文
services.AddDbContext<YourApplicationDbContext>(options =>
{
options.UseSqlite("app.db")
});
// 注册日志上下文
services.AddDbContext<YourLogDbContext>(options =>
{
// 重要!
// 抑制此上下文的命令执行相关日志生成以消除无限写入循环。
options.ConfigureWarnings(b => b.Ignore(RelationalEventId.CommandExecuted, RelationalEventId.CommandError));
options.UseSqlite("app.db")
});
// 注册日志过滤器配置监视器管理器服务。
services.AddMinimumLevelOverridableSerilogFilterConfigurationMonitorManager();
基础使用(Program.cs)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostBuilder, serviceProvider, configuration) =>
{
configuration
.ReadFrom.Configuration(hostBuilder.Configuration)
.ReadFrom.Services(serviceProvider)
.WriteTo.Logger(internalConfiguration =>
{
internalConfiguration
.Filter.ByIncludingOnly(
// 添加一个基于配置监视器的日志过滤器
new MinimumLevelOverridableSerilogFilterConfigurationMonitor(
serviceProvider,
// 配置路径
"SerilogFilterExtensions:EntityFrameworkCore"
).Filter)
// 使用默认日志类型
.WriteTo.EntityFrameworkCore(
serviceProvider.GetRequiredService<IServiceScopeFactory>(),
// 日志上下文提取工厂,取决于上下文服务应该如何获取,例如使用上下文工厂服务或者直接获取
static sp => sp.GetRequiredService<YourLogDbContext>(),
// 日志的JSON序列化选项
new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
});
// 使用自定义日志类型
.WriteTo.EntityFrameworkCore<YourLogDbContext, YourLogRecord>(
serviceProvider.GetRequiredService<IServiceScopeFactory>(),
static sp => sp.GetRequiredService<YourLogDbContext>(),
new()
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
});
});
}, writeToProviders: true);
Serilog的内置日志级别筛选仅可用于全局,无法针对各个Sink独立配置,因此笔者只能自己实现一个相同效果的过滤器。其中CoreDX.Serilog.Extensions
是过滤器本体,可手动基于代码构建,CoreDX.Serilog.Extensions.Configuration
是配置扩展,可自动监控配置。配置应该类似以下结构:
{
"SerilogFilterExtensions": {
"EntityFrameworkCore": {
"Default": "Warning",
"Override": {
"Microsoft.AspNetCore.DataProtection.KeyManagement": "Error",
"Microsoft.AspNetCore.DataProtection.Repositories": "Error",
"Microsoft.EntityFrameworkCore.Database.Command": "Error",
"Microsoft.EntityFrameworkCore.Model.Validation": "Error"
}
}
}
}
结语
为了实现对 .NETStantard 2.0 的兼容代码上使用了条件编译预处理实现一份代码一个项目同时编译到所有框架,最大程度共用代码简化代码管理。其中 .NET 6 以下使用Json.NET序列化,其他的使用System.Text.Json序列化。
许可证:MIT
代码仓库:CoreDX.Serilog.Sinks.EntityFrameworkCore - Github
Nuget:CoreDX.Serilog.Sinks.EntityFrameworkCore
Nuget:CoreDX.Serilog.Sinks.EntityFrameworkCore.Models
Nuget:CoreDX.Serilog.Extensions
Nuget:CoreDX.Serilog.Extensions.Configuration
QQ群
读者交流QQ群:540719365
欢迎读者和广大朋友一起交流,如发现本书错误也欢迎通过博客园、QQ群等方式告知笔者。
本文地址:https://www.cnblogs.com/coredx/p/18298297.html
基于EF Core存储的Serilog持久化服务的更多相关文章
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- 基于EF Core的Code First模式的DotNetCore快速开发框架
前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...
- 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持
前言 距离上一篇文章<基于EF Core的Code First模式的DotNetCore快速开发框架>已过去大半个年头,时光荏苒,岁月如梭...比较尴尬的是,在这大半个年头里,除了日常带娃 ...
- .net Core 基于EF Core 实现数据库上下文
在做项目时,需要将某一些功能的实体建立在另一个数据库中,连接不同的数据库用以存储记录.通过查找资料,实现EF Core上下文. 下面是实现上下文后的解决方案的目录: 1.UpAndDownDbCont ...
- 基于ef core 2.0的数据库增删改审计系统
1.首先是建审计存储表 CREATE TABLE [dbo].[Audit] ( [Id] [uniqueidentifier] NOT NULL, [EntityName] [nvarchar](1 ...
- 使用Asp.Net Core MVC 开发项目实践[第四篇:基于EF Core的扩展2]
上篇我们说到了基于EFCore的基础扩展,这篇我们讲解下基于实体结合拉姆达表达式的自定义更新以及删除数据. 先说下原理:其实通过实体以及拉姆达表达式生成SQL语句去执行 第一种更新扩展: 自定义更新字 ...
- 使用Asp.Net Core MVC 开发项目实践[第三篇:基于EF Core的扩展]
上篇我们说到了EFCore的基础使用,这篇我们将讲解下基于EFCore的扩展. 我们在Mango.Framework.EFCore类库项目中创建一个类名EFExtended的扩展类,并且引入相关的命名 ...
- ASP.NET Core MVC+EF Core从开发到部署
笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...
- EF Core 数据过滤
1 前言 本文致力于将一种动态数据过滤的方案描述出来(基于 EF Core 官方的数据筛选器),实现自动注册,多个条件过滤,单条件禁用(实际上是参考ABP的源码),并尽量让代码保持 EF Core 的 ...
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
随机推荐
- pip install 报错:Versioning for this project requires either an sdist tarball, ...
问题:Versioning for this project requires either an sdist tarball, or access to an upstream git reposi ...
- 安装 MySQL ZIP 版本
安装 MySQL 的 ZIP 版本相对于安装包版本稍微复杂一些,因为它需要手动解压和配置.以下是在 Windows 上安装 MySQL ZIP 版本的步骤: 下载 MySQL ZIP 文件: 前往 M ...
- IceRPC之调度管道->快乐的RPC
作者引言 很高兴啊,我们来到了IceRPC之调度管道->快乐的RPC, 基础引导,有点小压力,打好基础,才能让自已不在迷茫,快乐的畅游世界. 调度管道 Dispatch pipeline 了解如 ...
- 源码分析——MyBatis与Spring整合后如何保证SqlSession线程安全
在MyBatis架构中SqlSession是提供给外层调用的顶层接口,它是MyBatis对外暴露的最重要的接口,用户通过该接口即可完成数据库的全部操作.在上文中我们明白了我们常用的Mybatis动态代 ...
- 将python文件转换成exe可执行文件
一.安装Pyinstaller pip install pyinstaller(Pyinstaller) 二.找到 .py文件的路径并执行如下命令 pyinstaller -F 要转换的文件.py 三 ...
- golang kmp算法实现
// 不多逼逼直接上代码.原理的话可以参考下面的链接.讲的非常清晰package main import "fmt" func genNext(s string) []int { ...
- VmWare虚拟机和主机配置为同一网段IP
参考博客:将虚拟机IP与主机IP设置在同一网段的方法 - 天懿 - 博客园 (cnblogs.com) 主机地址 主机通过WiFi连接,地址信息为: 虚拟机配置 选择编辑-->虚拟网络编辑器-- ...
- (八十七)c#Winform自定义控件-朝上的瓶子
官网 http://www.hzhcontrols.com/ 前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kw ...
- 增补博客 第六篇 python 电子算盘
珠算测试器 题目描述]设计一个珠算测试器,要求能够完成珠算加减法的测试.具体的要求功能如下:(1)用户启动测试,输入用户名后系统随机生成特定数目的加减法测试题:(2) 要求测试使用表盘式或数字时秒表进 ...
- java多线程-3-使用多线程的时机
许多人对于计算机的运行原理不了解,甚至根本不了解. 不幸的是,此类中的一部分人也参与了计算机的编码工作.可想而知,编写的效率和结果.听者伤心,闻者流泪. 此类同学的常见的误解: 并发就能加快任务完成 ...