使用自定义目标将所有NLog输出重定向到Serilog [英] Redirect all NLog output to Serilog with a custom Target

查看:176
本文介绍了使用自定义目标将所有NLog输出重定向到Serilog的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为从NLog切换到Serilog的步骤,我想重定向作为NLog LogManager.GetLogger(name)的标准调用基础的标准接线,以桥接任何记录到NLog的代码,以立即转发到环境Serilog Log.Logger-即,我想只是一个配置文件,可以简单地转发消息,而无需像 Log4net.Appender.Serilog 那样进行缓冲Log4net.

As a step in switching from NLog to Serilog, I want to redirect the standard wiring underlying standard invocations of NLog's LogManager.GetLogger(name) to Bridge any code logging to NLog to forward immediately to the ambient Serilog Log.Logger - i.e. I want to just one piece of config that simply forwards the message, without buffering as Log4net.Appender.Serilog does for Log4net.

任何人都可以编造或将我指向能够正确,有效地执行此操作的规范代码段吗?我能想到的要求:

Can anyone concoct or point me to a canonical snippet that does this correctly and efficiently please? Requirements I can think of:

  • 保持级别,即nlog.Warn应该等同于serilog.Warning
  • Serilog重新产生时间是可以的
  • 在附加器中实现消息-即,无需维护与消息"(Serilog术语中的LogEvent)相关的任何属性
  • 没有缓冲
  • 我不需要使用其他任何NLog目标(例如,更改/删除邮件就可以了)
  • Maintain the level, i.e. nlog.Warn should be equivalent to serilog.Warning
  • It's ok for Serilog to generate the time anew
  • materializing the message in the appender - i.e., there's no need to maintain any properties associated with the 'message' (the LogEvent in Serilog terms)
  • no buffering
  • I don't need any other NLog Target to be used (i.e. mutating/deleting the message would be fine)

推荐答案

我认为最好的选择确实是自定义NLog目标.像这样:(C#)

I think the best option is indeed a custom NLog target. Something like this: (C#)

using NLog;
using NLog.Targets;
using Serilog;
using Serilog.Events;

namespace MyNamespace
{
    [Target("SerilogTarget")]
    public sealed class SerilogTarget : TargetWithLayout
    {
        protected override void Write(LogEventInfo logEvent)
        {
            var log = Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, logEvent.LoggerName);
            var logEventLevel = ConvertLevel(logEvent.Level);
            if ((logEvent.Parameters?.Length ?? 0) == 0)
            {
                // NLog treats a single string as a verbatim string; Serilog treats it as a String.Format format and hence collapses doubled braces
                // This is the most direct way to emit this without it being re-processed by Serilog (via @nblumhardt)
                var template = new Serilog.Events.MessageTemplate(new[] { new Serilog.Parsing.TextToken(logEvent.FormattedMessage) });
                log.Write(new Serilog.Events.LogEvent(DateTimeOffset.Now, logEventLevel, logEvent.Exception, template, Enumerable.Empty<Serilog.Events.LogEventProperty>()));
            }
            else
                // Risk: tunneling an NLog format and assuming it will Just Work as a Serilog format
#pragma warning disable Serilog004 // Constant MessageTemplate verifier
                log.Write(logEventLevel, logEvent.Exception, logEvent.Message, logEvent.Parameters);
#pragma warning restore Serilog004
        }

        static Serilog.Events.LogEventLevel ConvertLevel(LogLevel logEventLevel)
        {
            if (logEventLevel == LogLevel.Info)
                return Serilog.Events.LogEventLevel.Information;
            else if (logEventLevel == LogLevel.Trace)
                return Serilog.Events.LogEventLevel.Verbose;
            else if (logEventLevel == LogLevel.Debug)
                return Serilog.Events.LogEventLevel.Debug;
            else if (logEventLevel == LogLevel.Warn)
                return Serilog.Events.LogEventLevel.Warning;
            else if (logEventLevel == LogLevel.Error)
                return Serilog.Events.LogEventLevel.Error;
            return Serilog.Events.LogEventLevel.Fatal;
        }
    }
}

将其注册到您的main()app_start:

// Register so it can be used by config file parsing etc
Target.Register<MyNamespace.SerilogTarget>("SerilogTarget"); 

在进行任何日志记录之前,需要连接Target,以便LogManager.GetLogger()实际上可以触发对SerilogTarget.Write

Before any logging takes place, the Target needs to be wired in so LogManager.GetLogger() can actually trigger a call to SerilogTarget.Write

    public static void ReplaceAllNLogTargetsWithSingleSerilogForwarder()
    {
        // sic: blindly overwrite the forwarding rules every time
        var target = new SerilogTarget();
        var cfg = new NLog.Config.LoggingConfiguration();
        cfg.AddTarget(nameof(SerilogTarget), target);
        cfg.LoggingRules.Add(new NLog.Config.LoggingRule("*", LogLevel.Trace, target));
        // NB assignment must happen last; rules get ingested upon assignment
        LogManager.Configuration = cfg;
    }


另请参见: https://github. com/nlog/nlog/wiki/如何编写自定义目标

在不引起任何可避免的性能影响等的情况下实现此目的的最佳方法.

the optimal way to do this without inducing any avoidable perf impact etc.

这是NLog中的最佳方法,对NLog的站点没有性能影响.

This is the optimal way in NLog and has no performance impact on the NLog's site.

TargetAttribute给我买什么?

在这种情况下,您不需要它. TargetAttribute在注册完整的程序集时使用,但是由于我们是手动注册的,因此不需要它.我认为这是最佳做法,但您可以忽略它.

Well in this case you don't need it. The TargetAttribute is used when registering a full assembly, but because we register manually, it's not needed. I think it's best practice, but you could leave it out.

Register还能给我买什么

当以编程方式使用配置时,确实不需要这样做.但是,如果您具有XML配置,则需要注册.

This is indeed not needed when using programmatically config. But if you have XML config, you need the register.

我习惯于编写以各种方式工作的目标(手动注册,按程序集注册,从代码配置,从XML配置).我知道这可能会造成混淆.

F#端口:

module SerilogHelpers =

    let private mapLevel = function
        | x when x = NLog.LogLevel.Info -> LogEventLevel.Information
        | x when x = NLog.LogLevel.Off || x = NLog.LogLevel.Trace -> LogEventLevel.Verbose
        | x when x = NLog.LogLevel.Debug -> LogEventLevel.Debug
        | x when x = NLog.LogLevel.Warn -> LogEventLevel.Warning
        | x when x = NLog.LogLevel.Error ->  LogEventLevel.Error
        | _ -> LogEventLevel.Fatal

    // via https://stackoverflow.com/a/49639001/11635
    [<NLog.Targets.Target("SerilogTarget")>]
    type SerilogTarget() =
        inherit NLog.Targets.Target()

        static member InitializeAsGlobalTarget() =
            // sic: blindly overwrite the forwarding rules every time
            // necessary as Azure Startup establishes a different config as a bootstrapping step
            // see: LogModule.To.target("rollingFile", create, "*", LogLevel.Trace)
            let cfg, target = NLog.Config.LoggingConfiguration(), SerilogTarget()
            cfg.AddTarget("SerilogTarget", target)
            cfg.LoggingRules.Add(NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, target))
            // NB assignment must happen last; rules get ingested upon assignment
            NLog.LogManager.Configuration <- cfg

        override __.Write(logEvent : NLog.LogEventInfo) =
            let log = Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, logEvent.LoggerName)
            match logEvent.Parameters with
            | xs when isNull xs || xs.Length = 0 ->
                // NLog treats a single string as a verbatim string; Serilog treats it as a String.Format format and hence collapses doubled braces
                // This is the most direct way to emit this without it being re-processed by Serilog (via @nblumhardt)
                let template = MessageTemplate [| Serilog.Parsing.TextToken(logEvent.FormattedMessage) |]
                log.Write(new LogEvent(DateTimeOffset.Now, mapLevel logEvent.Level, logEvent.Exception, template, Seq.empty<LogEventProperty>))
            | _ ->
                // Risk: tunneling an NLog format and assuming it will Just Work as a Serilog format
                log.Write(mapLevel logEvent.Level, logEvent.Exception, logEvent.Message, logEvent.Parameters)

这篇关于使用自定义目标将所有NLog输出重定向到Serilog的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆