在编写和调试代码时,通常希望能够直接定位到出现问题的地方。由于几乎总是包含日志记录,查看日志事件会是一个很好的起点。但是,Serilog默认并不提供包含源代码信息(如源文件、调用方法和行号)的方式。J4JLogger作为一个Serilog的包装器,提供了这样的功能,并且可以通过短信发送日志事件,这对于真正严重的问题非常有用。虽然目前只包含了Twilio提供商,但添加其他SMS库的提供商也很简单。
J4JLogger还提供了一个缓存日志器,适合在“真正的”日志系统建立之前作为日志目标使用(例如,在构建IHost时)。缓存的条目可以在日志系统建立后转储到日志系统中,提供在调试启动问题时有用的信息。
在编写代码时,通常喜欢能够直接定位到造成问题的地方。由于几乎总是包含日志记录,查看日志事件会是一个很好的起点。但是,Serilog默认并不提供包含源代码信息(如源文件、调用方法和行号)的方式。
一种可能的解决方案是编写一个丰富器(enricher),在Serilog中,它允许将额外的信息插入到日志记录过程中。但真正的挑战在于获取这些信息。如果需要在所有的日志调用中指定方法调用名称、源代码文件名称和行号,那将是非常繁琐的。而且这样做也很容易出错,因为这些名称和数字在开发过程中会经常变化。
有一个更好的方法:C#编译器允许在任何方法调用中自动包含这些信息,只要在参数列表中包含某些带有特定属性和分配特定默认值的参数。例如:
public void Write(
LogEventLevel level,
string template,
[CallerMemberName] string memberName = "",
[CallerFilePath] string srcPath = "",
[CallerLineNumber] int srcLine = 0);
当调用这个方法时,关于方法被调用的上下文信息——调用成员的名称、源代码文件的路径和调用的行号——在Write()方法中是可用的。这就是J4JLogger包含在日志事件中的信息。
有多种方式可以设置J4JLogger(请查看Github页面上的详细信息)。这里是一个使用Serilog的IConfiguration-based插件的例子:
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using J4JSoftware.Logging;
using Microsoft.Extensions.Configuration;
using Serilog;
namespace ConfigurationBasedExample
{
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");
}
}
}
有时,需要立即通知某些日志事件(例如,如果依赖的服务运行出现问题)。为了适应这种情况,增加了将日志事件作为短信发送的功能。
要做到这一点,需要向日志器添加一个Serilog接收器,它在Serilog和短信服务之间进行接口。该库包括一个与Twilio一起工作的接收器。但可以添加其他的,这并不难(请查看Github文档了解详细信息)。
SMS功能是通过调整日志器的SmsHandling属性来触发的。这可以通过设置或通过以下便捷方法来完成:
public void SendNextEventToSms() => SmsHandling = SmsHandling.SendNextMessage;
public void SendAllEventsToSms() => SmsHandling = SmsHandling.SendUntilReset;
public void StopSendingEventsToSms() => SmsHandling = SmsHandling.DoNotSend;
非常喜欢Microsoft的IHostBuilder/IHost API。它允许在启动时以开发者定义的方式配置应用程序,然后将执行交给声明的运行时代理方法。它支持使用IConfigurationBuilder/IConfiguration API和依赖注入。在项目中,经常使用它来设置J4JLogging子系统。
鉴于大多数启动程序的复杂性,如果在初始化期间记录事件以便于研究问题将会很方便。不幸的是,尝试简单地通过J4JLogger(或任何其他日志器,怀疑)来做这件事会产生一个鸡和蛋的问题:不能记录到一个尚未配置和构建的日志器。
解决这个问题的方法是定义了一个特殊的J4JLogger版本,J4JCachedLogger,它不包装Serilog日志器。实际上,它根本不发出任何日志事件。相反,它将它们存储在内存中,直到“真正的”日志器建立起来。在这一点上,可以通过调用以下方法将所有缓存的日志事件转储到J4JLogger:
public abstract bool OutputCache(J4JCachedLogger cachedLogger);
有时需要在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; }
}