前言

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持久化服务的更多相关文章

  1. 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,这种形 ...

  2. 基于EF Core的Code First模式的DotNetCore快速开发框架

    前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...

  3. 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持

    前言 距离上一篇文章<基于EF Core的Code First模式的DotNetCore快速开发框架>已过去大半个年头,时光荏苒,岁月如梭...比较尴尬的是,在这大半个年头里,除了日常带娃 ...

  4. .net Core 基于EF Core 实现数据库上下文

    在做项目时,需要将某一些功能的实体建立在另一个数据库中,连接不同的数据库用以存储记录.通过查找资料,实现EF Core上下文. 下面是实现上下文后的解决方案的目录: 1.UpAndDownDbCont ...

  5. 基于ef core 2.0的数据库增删改审计系统

    1.首先是建审计存储表 CREATE TABLE [dbo].[Audit] ( [Id] [uniqueidentifier] NOT NULL, [EntityName] [nvarchar](1 ...

  6. 使用Asp.Net Core MVC 开发项目实践[第四篇:基于EF Core的扩展2]

    上篇我们说到了基于EFCore的基础扩展,这篇我们讲解下基于实体结合拉姆达表达式的自定义更新以及删除数据. 先说下原理:其实通过实体以及拉姆达表达式生成SQL语句去执行 第一种更新扩展: 自定义更新字 ...

  7. 使用Asp.Net Core MVC 开发项目实践[第三篇:基于EF Core的扩展]

    上篇我们说到了EFCore的基础使用,这篇我们将讲解下基于EFCore的扩展. 我们在Mango.Framework.EFCore类库项目中创建一个类名EFExtended的扩展类,并且引入相关的命名 ...

  8. ASP.NET Core MVC+EF Core从开发到部署

    笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...

  9. EF Core 数据过滤

    1 前言 本文致力于将一种动态数据过滤的方案描述出来(基于 EF Core 官方的数据筛选器),实现自动注册,多个条件过滤,单条件禁用(实际上是参考ABP的源码),并尽量让代码保持 EF Core 的 ...

  10. 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

    项目开发中的一些注意事项以及技巧总结   1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...

随机推荐

  1. 用 C 语言开发一门编程语言 — S-表达式

    目录 文章目录 目录 前文列表 使用 S-表达式进行重构 读取并存储输入 实现 S-Expression 语法解析器 实现 S-Expression 存储器 实现 lval 变量的构造函数 实现 lv ...

  2. Python:用tqdm模块绘制进度条

    在计算密集型迭代计算中,我们常常需要知道当前的迭代轮次,最传统的方法就是打印当前迭代计数器的轮数.那有没有更好的方法呢?我们可以使用tqdm模块(非py内置,需要单独按照)来在控制台绘制进度条,这样更 ...

  3. Python 将PowerPoint (PPT/PPTX) 转为HTML

    PPT是传递信息.进行汇报和推广产品的重要工具.然而,有时我们需要将这些精心设计的PPT演示文稿发布到网络上,以便于更广泛的访问和分享.本文将介绍如何使用Python将PowerPoint文档转换为网 ...

  4. Asp .Net Core 系列:集成 CAP + RabbitMQ + MySQL(含幂等性)

    简介 官网:https://cap.dotnetcore.xyz/ CAP 是什么? 是一个 EventBus,同时也是一个在微服务或者 SOA 系统中解决分布式事务问题的一个框架.它有助于创建可扩展 ...

  5. 超详细--redis在Linux环境搭建主从复制

    引言Redis是一个高性能的缓存中间件,一个Redis服务器可以支撑很多的并发请求.但是在一些超高的并发场景下,虽然Redis读写速度很快,但也会产生读写压力过大,服务器负载过高的情况.为了分担读写的 ...

  6. Swoole 源码分析之 Timer 定时器模块

    原文首发链接:Swoole 源码分析之 Timer 定时器模块 大家好,我是码农先森. 引言 Swoole 中的毫秒精度的定时器.底层基于 epoll_wait 和 setitimer 实现,数据结构 ...

  7. WebView2在WPF中的应用

    开发环境 运行环境:.Net 6 开发环境:Visual Studio 2022 17.1.3 框架语言:WPF 安装WebView2 通过Package Manager控制台安装 Install-P ...

  8. .Net6 MiNiApi +EFCore6.0高B格操作的WebApi

    编辑器的选择项目运行雏形EFCore6.0的使用实体创建![在这里插入图片描述](https://img-blog.csdnimg.cn/abe224cea341434c9299e9643dbcd9e ...

  9. Linux和Windows时间不一致问题

    问题描述 装过双系统或者虚拟机装Linux的人都知道,Linux的时间和Windows往往是不同步的,在编写跨平台程序的时候特别是对时间敏感的代码就带来很大的困扰 解决办法 这个问题可以在Linux下 ...

  10. 将mnist训练的caffemodel生成动态链接库DLL

    在项目程序中经常看到动态链接库,非常好奇,想自己实现一下,于是乎尝试一波.就因为这种好奇,每天都被bug所困扰... 1. 训练caffemodel 在windows环境下搭建caffe无果,转投Ub ...