J4JLogger:提供源代码信息的Serilog包装器

目录

介绍

背景

使用代码

附加功能

短信

缓存记录器

普通的旧事件


介绍

我喜欢Serilog,并在我所有的项目中使用它。但是开箱即用,它不具备记录已记录事件的源代码信息(例如,源文件、调用方法、行号)的能力。J4JLogger是一个Serilog包装器,它可以做到这一点。

它还提供了一种通过SMS发送日志事件的方法(对于真正严重的问题很有用)。虽然目前只包含一个Twilio提供程序,但为其他SMS库添加额外的提供程序很简单。

最后,在设置真实日志系统之前(例如,在IHostBuilder构建IHost过程中)J4JLogger提供适合用作日志目标的缓存记录器。缓存的条目一旦建立就可以转储到日志系统中,提供在调试启动问题时有用的信息。

您可以在J4JLogger的Github页面上找到更多文档。

背景

当我在写作和调试代码时,我通常喜欢能够直接转到导致问题的地方。由于我几乎总是包含日志记录,因此查看日志事件将是一个不错的起点……但是,开箱即用的Serilog不提供包含该信息的方法。

包含它的一种方法可能是编写一个丰富器,在Serilog世界中,它允许您将其他信息插入到日志记录过程中。但真正的挑战是首先获取信息。必须在所有日志记录调用中指定方法调用名称、源代码文件名和行号是非常乏味的。它也很容易被破解,因为名称和数字在开发过程中会发生很大变化。

有一个更好的方法:如果您在参数列表中包含某些参数,用特定属性修饰并分配特定默认值,C#编译器允许您自动将该信息包含在任何方法调用中。这是一个例子:

public void Write( 
     LogEventLevel level, 
     string template, 
     [CallerMemberName] string memberName = "", 
     [CallerFilePath] string srcPath = "",
     [CallerLineNumber] int srcLine = 0 );

当您调用此方法时,有关调用方法的上下文的信息——调用成员的名称、源代码文件的路径和调用的行号——Write()方法中是可用的。这就是J4JLogger日志事件中包含的信息。

在某些情况下,这可能需要稍微改变调用其各种方法的“Serilog方式。出现这个问题是因为许多Serilog日志记录方法包含泛型参数,编译器可能会将其误认为memberName,srcPath/srcLine

考虑以下方法,经过扩充以包括那些额外的上下文参数:

public void Error<T0>( 
    string template, 
    T0 propertyValue,
    [CallerMemberName] string memberName = "",
    [CallerFilePath] string srcPath = "",
    [CallerLineNumber] int srcLine = 0);

如果您要记录涉及字符串参数的内容:

_logger.Error( "This is a Serilog template with a parameter '{0}'", "some text");

编译器不知道某些文本propertyValue还是memberName

为了避免这种歧义,您必须像这样调用该方法:

_logger.Error<string>( "This is a Serilog template with a parameter '{0}'", "some text");

现在编译器知道"some text"propertyValue,正如你所期望的那样。

这个问题并不总是出现。它通常在第一个参数是string时发生。

使用代码

有多种方法可以设置J4JLogger(查看Github页面了解详细信息)。这是使用基于IConfigurationSerilog附加库:

using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using J4JSoftware.Logging;
using Microsoft.Extensions.Configuration;
using Serilog;

namespace ConfigurationBasedExample
{
    // shows how to use J4JLogger with a configuration file
    // see the ConfigurationBasedExample project for other files and details
    class Program
    {
        static void Main(string[] args)
        {
            var loggerConfig = new J4JLoggerConfiguration();

            var configRoot = new ConfigurationBuilder()
                .AddJsonFile( Path.Combine( Environment.CurrentDirectory, 
                              "appConfig.json" ), true )
                .Build();

            loggerConfig.SerilogConfiguration
                .ReadFrom
                .Configuration( configRoot );

            var logger = loggerConfig.CreateLogger();
            logger.SetLoggedType(typeof(Program));

            logger.Information("This is an Informational logging message");
            logger.Fatal("This is a Fatal logging message");
        }
    }
}

您可以在Github站点上看到控制台输出的样子。日志文件如下所示:

2021-09-15 10:14:59.173 -07:00 [INF] This is an Informational logging message 
ConfigurationBasedExample.Program::Main 
(C:\Programming\J4JLogging\examples\ConfigurationBasedExample\Program.cs:32) 
2021-09-15 10:14:59.238 -07:00 [FTL] This is a Fatal logging message 
ConfigurationBasedExample.Program::Main 
(C:\Programming\J4JLogging\examples\ConfigurationBasedExample\Program.cs:33) 

附加功能

短信

有时,我发现我想立即通知某些日志事件(例如,如果我所依赖的正在运行的服务出现问题)。为了适应该用例,我添加了将日志事件作为SMS消息发送的功能。

为此,您必须将Serilog接收器添加到连接SerilogSMS服务的记录器。该库包括一个可与Twilio一起使用的接收器。但是你可以添加其他的,这并不难(查看Github文档了解详细信息)。

SMS功能通过调整记录器的SmsHandling属性来触发。这可以通过设置它或通过以下便捷方法来完成:

public void SendNextEventToSms() => SmsHandling = SmsHandling.SendNextMessage;
public void SendAllEventsToSms() => SmsHandling = SmsHandling.SendUntilReset;
public void StopSendingEventsToSms() => SmsHandling = SmsHandling.DoNotSend;

我通常在逐个消息的基础上使用SMS功能(即SendNextMessage),以免使我的SMS收件箱超载。SendNextMessage将发送下一个日志事件,并且仅发送下一个日志事件到所有Serilog SMS接收器。

缓存记录器

我是微软IHostBuilder/IHost API的忠实粉丝。它允许您在启动时以开发人员定义的方式配置应用程序,然后将执行交给您声明为运行时代理的任何方法。它支持使用IConfigurationBuilder/IConfiguration API和依赖注入。在我的项目中,我经常使用它来设置J4JLogging子系统。

鉴于大多数启动例程的复杂性,在初始化期间记录事件会很方便,以便研究问题。不幸的是,尝试简单地使用J4JLogger(或任何其他记录器,我怀疑)会产生先有鸡还是先有蛋的问题:您无法登录到尚未配置和构建的记录器。

我解决这个问题的方法是定义一个特殊版本的J4JLoggerJ4JCachedLogger,它不包装Serilog记录器。事实上,它根本不会发出任何日志事件。相反,它将它们存储在内存中,直到设置真正的记录器。此时您可以通过调用以下方法将所有缓存的日志事件转储到J4JLogger

public abstract bool OutputCache( J4JCachedLogger cachedLogger );

正如在J4JLogger中实现的那样,此方法将所有缓存的事件按照它们创建的顺序写入日志(在J4JCachedLogger中,实现什么也不做,因为事件已经在缓存中)。

这不是先有鸡还是先有蛋的问题的完整解决方案,因为如果在设置J4JLogger实例之前初始化失败,则没有任何东西可以发出缓存事件。我正在努力为这种情况提供一种查看/输出这些缓存事件的替代方法。

普通的旧事件

我有时需要在UI中显示日志消息或其中的一部分。为了简化这一点,我在IJ4JLogger中添加了一个您可以监听的C#事件:

event EventHandler<NetEventArgs>? LogEvent;

NetEventArgs是一个简单的类,它只回显记录的事件:

public class NetEventArgs
{
    public NetEventArgs( LogEventLevel level, string mesg )
    {
        Level = level;
        LogMessage = mesg;
    }

    public LogEventLevel Level { get; }
    public string LogMessage { get; }
}

请注意,除非您添加NetEventSinkSerilog配置中,否则您不会收到任何事件。最简单的方法是使用提供的扩展方法(在J4JLoggerConfiguration的实例上调用,而不是底层的Serilog配置对象):

public static LoggerConfiguration NetEvent(
    this J4JLoggerConfiguration loggerConfig,
    string outputTemplate = NetEventSink.DefaultTemplate,
    LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose
)

您可以提供自己的Serilog输出模板和最低事件级别以在您这样做时报告。

https://www.codeproject.com/Articles/5314007/J4JLogger-A-Serilog-Wrapper-that-Provides-Source-C

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值