前言

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. 使用 Amazon Cloud WAN 构建您的全球网络(内含免费套餐申请入口)

    前言 对 AWS 云技术感兴趣的朋友们,可以尝试申请免费套餐的 AWS 账户,提供了 100 余种可以使用免费套餐的 AWS 云服务. 国内区域账户:https://www.amazonaws.cn/ ...

  2. java学习之旅(day.04)

    运算符 算术运算符:+ ,- ,* ,/,%(取余或模运算), ++(自增),-- (自减) 赋值运算符:= 关系运算符:>, <,>=, <=, ==, !=(不等于),in ...

  3. [NOIP2000 提高组] 单词接龙

    传送锚点:https://www.luogu.com.cn/problem/P1019 题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以 ...

  4. 跨域问题服务端解决办法 Request header field Authorization is not allowed by Access-Control-Allow-Headers

    跨域问题服务端解决办法 一般在入口文件加 header('Access-Control-Allow-Origin:*');// 响应类型header('Access-Control-Allow-Met ...

  5. 给师妹写的《Java并发编程之线程池十八问》被表扬啦!

    写在开头     之前给一个大四正在找工作的学妹发了自己总结的关于Java并发中线程池的面试题集,总共18题,将之取名为<Java并发编程之线程池十八问>,今天聊天时受了学妹的夸赞,心里很 ...

  6. C++笔记(2)拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象. 如果在类中没有定义拷贝构造函数,编译器会自行定义一个.如果类带有指针变量,并有动态内存分配,则它必 ...

  7. 什么是LLM大模型训练,详解Transformer结构模型

    本文分享自华为云社区<LLM 大模型学习必知必会系列(四):LLM训练理论篇以及Transformer结构模型详解>,作者:汀丶. 1.模型/训练/推理知识介绍 深度学习领域所谓的&quo ...

  8. 内置对象Date

    // 内置对象 Date 时间对象         // 可以通过 内置对象 data来获取和存储时间         // 使用方式步骤         // 1,创建时间对象         // ...

  9. INFINI Labs 产品更新 | Easysearch 优化字段压缩提升写入速度,Console 优化数据迁移和校验等功能

    INFINI Labs 产品又更新啦~.本次更新概要如下:Easysearch 增强 source_reuse 压缩功能,并大幅提升写入速度:Console 优化了数据迁移和校验功能,新增了通用的数据 ...

  10. 微软新Edge浏览器 WIN7 无法登录

    由于设备上的系统组件过期,不支持登录.请确保 Windows 为最新版本,然后重新尝试登录.访问 https://go.microsoft.com/fwlink/?linkid=2107246 了解详 ...