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

时间:2016-01-17 10:33:49

标签: c# .net logging serilog

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

我试着用一个例子来解释: 因此,在方法调用中,我想记录访问它的人或访问者。

如果是外部应用程序,我想记录这样的内容(使用模板):

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] (RequestId:{RequestId} | Key:{Key} | AppVersion:{Version}) {Message}{NewLine}{Exception}"

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

"[{Caller}] {Timestamp:HH:mm:ss} [{Level}] FullName:{FullName} | Organization:{Organization} | AppVersion:{Version}) {Message}{NewLine}{Exception}"

两种类型的方法调用者都是从Thread.CurrentPrincipal.Identity访问的,但每种方法都使用不同的自定义属性实现不同类型的标识。

我的代码如下:

public void DoSomething()
{
    Log.Information("DoSomething called");
}

如果我将记录器配置为:

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();

如果被外部应用程序(线程标识)触发调用,它将永远不会显示Key和RequestId。

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

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访问和修改richher中的模板,因为它是只读的(仅限getter)。

我知道可能的邮件格式化,但这不是我在这种特殊情况下所寻找的。

我希望在日志中看到最终结果,外部应用访问类似于:

  

2016年1月17日10:11:42.524 [API] 10:11:40 [信息](RequestId:123 | Key:XXX-1 | AppVersion:1.2.1)DoSomething called

并为用户登录时:

  

2016年1月17日11:12:42.524 [WEB] 11:12:40 [信息](FullName:匿名|组织:MyOrg | AppVersion:1.2.1)DoSomething叫

我的问题是:如何(如果可能)我可以登录(并在日志中查看)具有不同属性的不同类型的事件来登录模板?是否可以在运行时动态操作模板?我不希望有来自两个或许多其他可能的事件类型的所有可能令牌的模板,并且它们的属性在一个地方定义(在一个案例或其他案例中有许多空白)。

1 个答案:

答案 0 :(得分:2)

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

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

然后在你的更丰富的地方:

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上的代码)是可行的方法。 ColoredConsole不接受自定义文本格式化程序,但是:

WriteTo.Sink(new RollingFileSink(@"Logs\app-{Date}.txt", formatter))

将允许一个人通过。