C# Serilog:记录不同类型的日志事件

C# Serilog:记录不同类型的日志事件,c#,.net,logging,serilog,C#,.net,Logging,Serilog,我的API由不同类型的使用者访问。有外部应用程序,也有通过web界面的用户 我将试着用一个例子来解释: 所以在方法调用中,我想记录访问它的人或对象 对于外部应用程序,我希望记录如下内容(使用模板): 如果是用户触发的操作,我想记录如下内容: "[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{Ne

我的API由不同类型的使用者访问。有外部应用程序,也有通过web界面的用户

我将试着用一个例子来解释: 所以在方法调用中,我想记录访问它的人或对象

对于外部应用程序,我希望记录如下内容(使用模板):

如果是用户触发的操作,我想记录如下内容:

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}"
var logger = new LoggerConfiguration()
                   .Enrich.WithProperty("Version", appVersion)
                   .Enrich.WithProperty("Caller", caller)
                   .Enrich.With(new MyEnricher())
                   .WriteTo.ColoredConsole(outputTemplate: "[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}")
                   .CreateLogger();
public class MyEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var identity = Thread.CurrentPrincipal.Identity;
        if (identity is ExternalIdentity)
        {
            var externalIdentity = Thread.CurrentPrincipal.Identity as ExternalIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Key", externalIdentity.Key));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("RequestId", externalIdentity.RequestId));
        }
        else
        {
            var userIdentity = Thread.CurrentPrincipal.Identity as UserIdentity;

            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("FullName", userIdentity.FullName));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Organization", userIdentity.OrganizationName));
        }
}
这两种类型的方法调用程序都是从
Thread.CurrentPrincipal.Identity
访问的,但每种方法调用程序都实现了不同类型的标识,具有不同的自定义属性

我的代码如下所示:

public void DoSomething()
{
    Log.Information("DoSomething called");
}
如果我已将记录器配置为:

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}"
var logger = new LoggerConfiguration()
                   .Enrich.WithProperty("Version", appVersion)
                   .Enrich.WithProperty("Caller", caller)
                   .Enrich.With(new MyEnricher())
                   .WriteTo.ColoredConsole(outputTemplate: "[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}")
                   .CreateLogger();
public class MyEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var identity = Thread.CurrentPrincipal.Identity;
        if (identity is ExternalIdentity)
        {
            var externalIdentity = Thread.CurrentPrincipal.Identity as ExternalIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Key", externalIdentity.Key));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("RequestId", externalIdentity.RequestId));
        }
        else
        {
            var userIdentity = Thread.CurrentPrincipal.Identity as UserIdentity;

            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("FullName", userIdentity.FullName));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Organization", userIdentity.OrganizationName));
        }
}
如果由外部应用程序(线程标识)触发调用,它将永远不会显示Key和RequestId

我将
MyEnricher
添加到记录器中,类似于:

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}"
var logger = new LoggerConfiguration()
                   .Enrich.WithProperty("Version", appVersion)
                   .Enrich.WithProperty("Caller", caller)
                   .Enrich.With(new MyEnricher())
                   .WriteTo.ColoredConsole(outputTemplate: "[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}")
                   .CreateLogger();
public class MyEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var identity = Thread.CurrentPrincipal.Identity;
        if (identity is ExternalIdentity)
        {
            var externalIdentity = Thread.CurrentPrincipal.Identity as ExternalIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Key", externalIdentity.Key));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("RequestId", externalIdentity.RequestId));
        }
        else
        {
            var userIdentity = Thread.CurrentPrincipal.Identity as UserIdentity;

            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("FullName", userIdentity.FullName));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Organization", userIdentity.OrganizationName));
        }
}
就我从在线文档和示例中所了解到的情况而言,日志模板仅在配置日志记录器时设置,而不是在实际创建之前设置。我无法通过LogEvent访问和修改enricher中的模板,因为它是只读的(只有getter)

我知道可能的消息格式,但这不是我在这个特殊情况下寻找的

我希望在日志中看到的外部应用程序访问的最终结果如下:

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}"
var logger = new LoggerConfiguration()
                   .Enrich.WithProperty("Version", appVersion)
                   .Enrich.WithProperty("Caller", caller)
                   .Enrich.With(new MyEnricher())
                   .WriteTo.ColoredConsole(outputTemplate: "[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}")
                   .CreateLogger();
public class MyEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var identity = Thread.CurrentPrincipal.Identity;
        if (identity is ExternalIdentity)
        {
            var externalIdentity = Thread.CurrentPrincipal.Identity as ExternalIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Key", externalIdentity.Key));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("RequestId", externalIdentity.RequestId));
        }
        else
        {
            var userIdentity = Thread.CurrentPrincipal.Identity as UserIdentity;

            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("FullName", userIdentity.FullName));
            logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("Organization", userIdentity.OrganizationName));
        }
}
2016年1月17日10:11:42.524[API]10:11:40[信息](请求ID:123 |键:XXX-1 |应用版本:1.2.1)调用剂量测量

当为用户登录时:

2016年1月17日11:12:42.524[WEB]11:12:40[信息](全名:匿名|组织:MyOrg |应用版本:1.2.1)DoSomething呼叫


我的问题是:如何(如果可能的话)记录(并在日志中查看)具有不同属性的不同类型的事件以登录模板?是否可以在运行时动态操作模板?我不希望模板包含来自这两种或许多其他可能事件类型的所有可能标记,并在一个位置定义它们的属性(在一种或另一种情况下,其中许多标记为空)。

如果您可以接受与示例稍有不同的格式,您可以在此处使用解构:

“[{Caller}]{Timestamp:HH:mm:ss}[{Level}]({Principal}){Message}{NewLine}{Exception}”

然后在您的enricher中:

public class MyEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var identity = Thread.CurrentPrincipal.Identity;
        if (identity is ExternalIdentity)
        {
            var externalIdentity = Thread.CurrentPrincipal.Identity as ExternalIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Principal", new {
                externalIdentity.Key,
                externalIdentity.RequestId
            }, true));
        }
        else
        {
            var userIdentity = Thread.CurrentPrincipal.Identity as UserIdentity;
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Principal", new {
                userIdentity.FullName,
                userIdentity.OrganizationName
            }, true));
        }
    }
}
(注意
true
触发匿名对象的序列化。)

这将以键值对语法输出主体的各种属性

否则,使用自定义的
ITextFormatter
来执行此操作(基于Serilog的
DisplayFormatter
)是一种方法<代码>彩色控制台不接受自定义文本格式化程序,但:

WriteTo.Sink(new RollingFileSink(@"Logs\app-{Date}.txt", formatter))
将允许一个通过