Serilog.Sinks.InMemory 0.11.0

dotnet add package Serilog.Sinks.InMemory --version 0.11.0                
NuGet\Install-Package Serilog.Sinks.InMemory -Version 0.11.0                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用的NuGet模块版本为 Install-Package.
<PackageReference Include="Serilog.Sinks.InMemory" Version="0.11.0" />                
对于支持 PackageReference 的项目,请将此 XML 节点复制到项目文件中,以引用此包。
paket add Serilog.Sinks.InMemory --version 0.11.0                
#r "nuget: Serilog.Sinks.InMemory, 0.11.0"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。请将此内容复制到交互工具或脚本的源代码中,以引用此包。
// Install Serilog.Sinks.InMemory as a Cake Addin
#addin nuget:?package=Serilog.Sinks.InMemory&version=0.11.0

// Install Serilog.Sinks.InMemory as a Cake Tool
#tool nuget:?package=Serilog.Sinks.InMemory&version=0.11.0                

Serilog.Sinks.InMemory

用于测试的 Serilog 内存中接收器。支持 FluentAssertions,便于编写断言。

构建状态

build-and-test release

NuGet Serilog.Sinks.InMemory NuGet Serilog.Sinks.InMemory.Assertions

用法

要仅使用接收器,请添加 Serilog.Sinks.InMemory NuGet 包

dotnet CLI

dotnet add package Serilog.Sinks.InMemory

PowerShell

Install-Package Serilog.Sinks.InMemory

但最好使用断言,因此您还希望添加 Serilog.Sinks.InMemory.Assertions NuGet 包

dotnet CLI

dotnet add package Serilog.Sinks.InMemory.Assertions

PowerShell

Install-Package Serilog.Sinks.InMemory.Assertions

示例

假设您有一个包含实现一些复杂业务逻辑的方法的类

public class ComplicatedBusinessLogic
{
    private readonly ILogger _logger;

    public ComplicatedBusinessLogic(ILogger logger)
    {
        _logger = logger;
    }

    public string FirstTenCharacters(string input)
    {
        return input.Substring(0, 10);
    }
}

有一个请求要求记录包含的字符数的消息。因此,为了测试这一点,您可以创建 ILogger 的模拟并断言该方法已调用,但是模拟设置很快就会变得很复杂(真的,这是我的看法!)并且在开始验证参数值时,对模拟的断言也有同样的问题。

因此,让我们使用 Serilog 和专门的测试接收器

public class WhenExecutingBusinessLogic
{
    public void GivenInputOfFiveCharacters_MessageIsLogged()
    {
        var logger = new LoggerConfiguration()
            .WriteTo.InMemory()
            .CreateLogger();

        var logic = new ComplicatedBusinessLogic(logger);

        logic.FirstTenCharacters("12345");

        // Use the static Instance property to access the in-memory sink
        InMemorySink.Instance
            .Should()
            .HaveMessage("Input is {count} characters long");
    }
}

现在测试将失败,因为 Expected a message to be logged with template \"Input is {count} characters long\" but didn't find any

现在更改实现如下

public string FirstTenCharacters(string input)
{
    _logger.Information("Input is {count} characters long", input.Length);

    return input.Substring(0, 10);
}

再次运行测试,现在它通过了。但如何确保此消息只记录一次呢?

为此,创建一个新的测试如下

public void GivenInputOfFiveCharacters_MessageIsLoggedOnce()
{
    /* omitted for brevity */

    InMemorySink.Instance
        .Should()
        .HaveMessage("Input is {count} characters long")
        .Appearing().Once();
}

要验证消息是否多次记录,请使用 Appearing().Times(int numberOfTimes)

因此,现在您需要验证属性 count 是否具有预期值。这建立在之前的测试的基础上

public void GivenInputOfFiveCharacters_CountPropertyValueIsFive()
{
    /* omitted for brevity */

    InMemorySink.Instance
        .Should()
        .HaveMessage("Input is {count} characters long")
        .Appearing().Once()
        .WithProperty("count")
        .WithValue(5);
}

断言一条消息出现多次

假设你在循环中有一个日志消息,你想要验证这一点

public void GivenLoopWithFiveItems_MessageIsLoggedFiveTimes()
{
    /* omitted for brevity */

    InMemorySink.Instance
        .Should()
        .HaveMessage("Input is {count} characters long")
        .Appearing().Times(5);
}

断言消息具有特定的级别

除了消息被记录之外,你还想验证它是否具有正确的级别。你可以使用WithLevel()断言来实现这一点

public void GivenLoopWithFiveItems_MessageIsLoggedFiveTimes()
{
    /* omitted for brevity */

    InMemorySink.Instance
        .Should()
        .HaveMessage("Input is {count} characters long")
        .Appearing().Once()
        .WithLevel(LogEventLevel.Information);
}

这也适用于多个消息

public void GivenLoopWithFiveItems_MessageIsLoggedFiveTimes()
{
    logger.Warning("Test message");
    logger.Warning("Test message");
    logger.Warning("Test message");

    InMemorySink.Instance
        .Should()
        .HaveMessage("Test message")
        .Appearing().Times(3)
        .WithLevel(LogEventLevel.Information);
}

这将失败并显示以下消息:预期日志消息 "Hello, world!" 的实例具有 Information 级别,但找到了 3 个具有 Warning 级别的实例

使用模式断言消息

除了匹配确切的消息之外,还可以使用Containing()断言来匹配某种特定的模式

InMemorySink.Instance
   .Should()
   .HaveMessage()
   .Containing("some pattern")
   .Appearing().Once();

它会匹配到日志消息

  • 这是一个模式
  • 消息中的某个模式
  • 消息中的某个模式

断言消息已被记录(或没有!)

当你想断言一条消息已被记录但不关心具体信息时,可以使用HaveMessageAppearing

InMemorySink.Instance
    .Should()
    .HaveMessage()
    .Appearing().Times(3); // Expect three messages to be logged

当然,当你期望没有消息被记录时,也可以使用相反的方法

InMemorySink.Instance
    .Should()
    .NotHaveMessage();

或者特定的消息不应该被记录

InMemorySink.Instance
    .Should()
    .NotHaveMessage("a specific message");

对消息断言属性

当你想断言消息具有属性时,可以使用WithProperty断言

InMemorySink.Instance
    .Should()
    .HaveMessage("Message with {Property}")
    .Appearing().Once()
    .WithProperty("Property");

然后你可以使用WithValue来断言它具有特定的值

InMemorySink.Instance
    .Should()
    .HaveMessage("Message with {Property}")
    .Appearing().Once()
    .WithProperty("Property")
    .WithValue("property value");

使用And约束可以断言消息具有多个属性

InMemorySink.Instance
    .Should()
    .HaveMessage("Message with {Property1} and {Property2}")
    .Appearing().Once()
    .WithProperty("Property1")
    .WithValue("value 1")
    .And
    .WithProperty("Property2")
    .WithValue("value 2");

当你有一个出现多次的日志消息,并且你想断言日志属性值的期望值时,可以使用WithValues断言

InMemorySink.Instance
    .Should()
    .HaveMessage("Message with {Property1} and {Property2}")
    .Appearing().Times(3)
    .WithProperty("Property1")
    .WithValue("value 1", "value 2", "value 3")

注意: WithValue接受一个值数组。

有时你可能想要使用像BeLessThanOrEqual()HaveLength()这样的断言,在这些情况下,WithValue可能不是很有用。相反,你可以使用WhichValue<T>来访问日志属性的值

InMemorySink.Instance
    .Should()
    .HaveMessage()
    .Appearing().Once()
    .WithProperty("PropertyOne")
    .WhichValue<string>()
    .Should()
    .HaveLength(3);

如果日志属性值的类型与泛型类型参数不匹配,则WhichValue<T>方法将抛出异常。

注意: 这只适用于标量值。当你以对象作为属性值记录日志消息时,Serilog将其转换为字符串。

使用解构对象断言属性

如果你使用对象解构

var someObject = new { Foo = "bar", Baz = "quux" };
logger.Information("Hello {@SomeObject}", someObject);

并且想断言解构对象的属性,你可以使用HavingADestructuredObject()断言,如下所示

InMemorySink.Instance
    .Should()
    .HaveMessage("Hello {@SomeObject}")
    .Appearing().Once()
    .WithProperty("SomeObject")
    .HavingADestructuredObject()
    .WithProperty("Foo")
    .WithValue("bar");

当属性SomeObject不包含解构对象时,断言将失败并显示以下消息:"预期消息 "Hello {NotDestructured}" 拥有属性 "NotDestructured",该属性包含一个解构对象,但找到的是标量值"

在测试之间清除日志事件

根据你的测试框架和测试设置,你可能想要确保通过InMemorySink捕获的日志事件被清除,这样测试就不会相互干扰。为此,InMemorySink实现了IDisposable接口。当调用Dispose()时,LogEvents集合会清空。

这将取决于你的测试框架或测试是否需要此功能。对于xUnit,此功能不是必需的,因为它在每个测试类的实例中隔离每个测试,这意味着它们都有自己的InMemorySink实例。然而,MSTest采用不同的方法,在那里你可能想要使用此功能,如下所述

[TestClass]
public class WhenDemonstratingDisposableFeature
{
    private Logger _logger;

    [TestInitialize]
    public void Initialize()
    {
        _logger?.Dispose();

        _logger = new LoggerConfiguration()
            .WriteTo.InMemory()
            .CreateLogger();
    }

    [TestMethod]
    public void GivenAFoo_BarIsBlah()
    {
        _logger.Information("Foo");

        InMemorySink.Instance
            .Should()
            .HaveMessage("Foo");
    }

    [TestMethod]
    public void GivenABar_BazIsQuux()
    {
        _logger.Information("Bar");

        InMemorySink.Instance
            .Should()
            .HaveMessage("Bar");
    }
}

此方法确保状態化对象CharterABar_BazIsQuux在先前的测试中不会看到任何日志消息。

创建记录器

通过LoggerConfiguration对象创建记录器。默认初始化如下

var logger = new LoggerConfiguration()
    .WriteTo.InMemory()
    .CreateLogger();

输出模板

基于文本的输出使用输出模板来控制格式化。这可以通过outputTemplate参数进行修改

var logger = new LoggerConfiguration()
    .WriteTo.InMemory(outputTemplate: "{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

默认模板,如上例所示,使用内置属性如Timestamp和Level。有关进一步配置和这些属性的说明,请参阅官方文档

最低级别

在此示例中,只有信息级别日志及其以上才会写入InMemorySink。

var logger = new LoggerConfiguration()
    .WriteTo.InMemory(restrictedToMinimumLevel: Events.LogEventLevel.Information)
    .CreateLogger();

默认级别 - 如果未指定MinimumLevel,则将处理详细信息级别事件和相关更高级别的事件。

动态级别

如果一个应用程序需要动态级别切换,在配置记录器时首先要创建LoggingLevelSwitch实例

var levelSwitch = new LoggingLevelSwitch();

此对象将当前最低级别默认设置为信息,所以为了使日志更加限制性,请提前设置其最低级别

levelSwitch.MinimumLevel = LogEventLevel.Warning;

配置记录器时,使用MinimumLevel.ControlledBy()提供切换器

var log = new LoggerConfiguration()
    .MinimumLevel.ControlledBy(levelSwitch)
    .WriteTo.InMemory()
    .CreateLogger();

现在,写入记录器的事件将根据切换器的MinimumLevel属性进行过滤。

要在运行时将级别提高或降低,例如响应通过网络发送的命令,请更改属性

levelSwitch.MinimumLevel = LogEventLevel.Verbose;
log.Verbose("This will now be logged");
产品 兼容的附加计算的target framework版本
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 已计算。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.0 兼容。 netstandard2.1 已计算。
.NET Framework net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。
MonoAndroid monoandroid 已计算。
MonoMac monomac 已计算。
MonoTouch monotouch 已计算。
Tizen tizen40 已计算。 tizen60 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
Xamarin.TVOS xamarintvos 已计算。
Xamarin.WatchOS xamarinwatchos 已计算。
兼容的目标框架
包含的目标框架(在包中)
了解更多关于 目标框架.NET Standard 的信息。
  • .NETStandard 2.0

NuGet 包 (4)

显示依赖 Serilog.Sinks.InMemory 的前 4 个 NuGet 包

下载
Serilog.Sinks.InMemory.Assertions

与 Serilog.Sinks.InMemory 包一起使用的 FluentAssertions 扩展

Swisschain.Extensions.Testing

包描述

Eshva.Common.Testing

Eshva 的常用代码集合

Swisschain.ExtensionsV2.Testing

包描述

GitHub 代码仓库 (5)

显示依赖 Serilog.Sinks.InMemory 的前 5 个热门 GitHub 代码仓库

代码仓库 星标数
EventStore/EventStore
EventStoreDB,一个面向事件的本机数据库。专为事件存储、事件驱动和微服务架构设计
dafny-lang/dafny
Dafny 是一种具备验证意识的编程语言
nkdAgility/azure-devops-migration-tools
Azure DevOps 迁移工具允许您将团队、待办事项、任务、测试用例以及计划和方案从 Azure DevOps / TFS 中的一个项目迁移到另一个项目,无论是在同一组织内还是在不同组织之间
serilog-contrib/serilog-ui
适用于多个输出的简单 Serilog 日志查看器 UI
serilog-contrib/Serilog.Enrichers.Sensitive
一个 Serilog LogEvent 增强器,它可以掩码敏感数据
版本 下载 上次更新
0.11.0 1,616,281 9/30/2022
0.10.0 710 9/29/2022
0.7.0 337,407 6/10/2022
0.6.0 356,458 12/13/2020
0.5.0 105,317 7/21/2020
0.3.0 370,693 10/15/2019
0.2.0 43,725 10/15/2018
0.1.0 1,497 10/15/2018