代码之家  ›  专栏  ›  技术社区  ›  Dynde

Serilog输出到hangfire上下文控制台

  •  6
  • Dynde  · 技术社区  · 7 年前

    我希望能够使用我的日志通过Hangfires上下文发出事件。控制台,这样我就不需要使用上下文。writeline将日志事件输出到我的hangfire仪表板。

    为此,我尝试实现一个特定的serilog接收器。但由于我需要来自hangfire的PerformContext(在运行时注入到任务方法中),我无法在应用程序启动时配置日志接收器。我试图在任务方法中创建一个新的记录器,只是想看看接收器是否真的可以工作,但它不能工作-有人能看到为什么它不能工作,或者可能会建议一种不同的方法吗?

    这是水槽:

     class HangfireContextSink : ILogEventSink {
            private readonly IFormatProvider formatProvider;
            private readonly PerformContext context;
            public HangfireContextSink(IFormatProvider formatProvider, PerformContext context) {
                this.formatProvider = formatProvider;
                this.context = context;
            }
            public void Emit(LogEvent logEvent) {
                var message = logEvent.RenderMessage(formatProvider);
                context.WriteLine(ConsoleTextColor.Blue, DateTimeOffset.Now.ToString() + " " + message);
            }
    

    接收器配置:

     public static class SinkExtensions {
            public static LoggerConfiguration HangfireContextSink(this LoggerSinkConfiguration loggerSinkConfiguration, PerformContext context, IFormatProvider formatProvider = null) {
                return loggerSinkConfiguration.Sink(new HangfireContextSink(formatProvider, context));
            }
        }
    

    任务方法:

     public static bool TestJob(PerformContext context) {
                using (LogContext.PushProperty("Hangfirejob", "TestJob")) {
                    try {
                        using (var hangfireLog = new LoggerConfiguration().WriteTo.HangfireContextSink(context).CreateLogger()) {
                            var progress = context.WriteProgressBar("Progress");
                            for (int i = 0; i < 10; i++) {
                                context.WriteLine("Working with {0}", i);
                                progress.SetValue((i + 1) * 10);
                                Log.Debug("Test serilog");
                                hangfireLog.Debug("Test from hangfirelog");
                                Thread.Sleep(5000);
                            }
                        }
                        Log.Debug("Done testjob");
                        return true;
                    } catch (Exception ex) {
                        Log.Error(ex, "Error!");
                        return false;
                    }
                }
            }
    
    1 回复  |  直到 7 年前
        1
  •  9
  •   CodeFuller    7 年前

    消息不会记录到Hangfire控制台,因为您使用 Debug 日志级别,而默认Serilog级别为 Information 。您可以通过调用更改日志记录级别 .MinimumLevel.Debug() 在…上 LoggerConfiguration 。还用于通过以下方式记录消息 Serilog.Log 静态类,您应该设置 Logger 所有物

    以下是将登录到Hangfire控制台的固定代码:

    using (LogContext.PushProperty("Hangfirejob", "TestJob"))
    {
        try
        {
            using (var hangfireLog = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.HangfireContextSink(context).CreateLogger())
            {
                //  This is a dirty code that will be removed in final solution
                var prevLogger = Log.Logger;
                Log.Logger = hangfireLog;
    
                var progress = context.WriteProgressBar("Progress");
                for (int i = 0; i < 10; i++)
                {
                    context.WriteLine("Working with {0}", i);
                    progress.SetValue((i + 1) * 10);
                    Log.Debug("Test serilog");
                    hangfireLog.Debug("Test from hangfirelog");
                    Thread.Sleep(5000);
                }
                Log.Debug("Done testjob");
                Log.Logger = prevLogger;
            }
            return true;
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error!");
            return false;
        }
    }
    

    这将起作用,但为每个作业创建新日志的总体方法非常糟糕。您可以通过传递 PerformContext 通过中的属性 LogEvent 。您应该定义从抽象 LogEventPropertyValue 并公开 PerformContext

    以下是最终代码:

    PerformContextProperty。cs:

    public class PerformContextProperty : LogEventPropertyValue
    {
        public PerformContext PerformContext { get; }
    
        public PerformContextProperty(PerformContext performContext)
        {
            PerformContext = performContext;
        }
    
        public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
        {
        }
    }
    

    PerformContextEnricher。cs:

    public class PerformContextEnricher : ILogEventEnricher
    {
        private readonly PerformContext performContext;
    
        public PerformContextEnricher(PerformContext performContext)
        {
            this.performContext = performContext;
        }
    
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(new LogEventProperty(HangfireContextSink.PerformContextProperty, new PerformContextProperty(performContext)));
        }
    }
    

    测试作业。cs:

    public class TestJob
    {
        public static bool Execute(PerformContext context)
        {
            using (LogContext.PushProperty("Hangfirejob", "TestJob"))
            using (LogContext.Push(new PerformContextEnricher(context)))
            {
                try
                {
                    var progress = context.WriteProgressBar("Progress");
                    for (int i = 0; i < 10; i++)
                    {
                        context.WriteLine("Working with {0}", i);
                        progress.SetValue((i + 1) * 10);
                        Log.Debug("Test serilog", context);
                        Log.Debug("Test from hangfirelog");
                        Thread.Sleep(5000);
                    }
                    Log.Debug("Done testjob");
                    return true;
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Error!");
                    return false;
                }
            }
        }
    }
    

    HangfireContextSink。cs:

    class HangfireContextSink : ILogEventSink
    {
        public const string PerformContextProperty = "PerformContext";
    
        private readonly IFormatProvider formatProvider;
    
        public HangfireContextSink(IFormatProvider formatProvider)
        {
            this.formatProvider = formatProvider;
        }
    
        public void Emit(LogEvent logEvent)
        {
            var message = logEvent.RenderMessage(formatProvider);
    
            LogEventPropertyValue propertyValue;
            if (logEvent.Properties.TryGetValue(PerformContextProperty, out propertyValue))
            {
                var context = (propertyValue as PerformContextProperty)?.PerformContext;
                context?.WriteLine(ConsoleTextColor.Green, DateTimeOffset.Now + " " + message);
            }
        }
    }
    

    下沉延伸。cs:

    public static class SinkExtensions
    {
        public static LoggerConfiguration HangfireContextSink(this LoggerSinkConfiguration loggerSinkConfiguration, IFormatProvider formatProvider = null)
        {
            return loggerSinkConfiguration.Sink(new HangfireContextSink(formatProvider));
        }
    }
    

    Serilog配置:

    Log.Logger = new LoggerConfiguration()
        .Enrich.FromLogContext()
        .MinimumLevel.Debug()
        .WriteTo.HangfireContextSink()
        .CreateLogger();
    

    在Serilog配置中,不要忘记调用 .Enrich.FromLogContext() 这样,日志事件就可以通过 LogContext

    上面的代码非常简单,这就是为什么我不详细注释它的原因。如果您对代码有一些问题,请提问,我将尝试解释不清楚的时刻。