Serilog输出到hangfire上下文控制台

时间:2018-03-06 12:40:07

标签: .net hangfire serilog

我希望能够通过Hangfires context.console使用我的日志发送事件,这样我就不需要使用context.writeline将日志事件输出到我的hangfire仪表板。

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

这是水槽:

 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;
                }
            }
        }

任务方法:

join

1 个答案:

答案 0 :(得分:8)

消息未记录到Hangfire控制台,因为您使用Information日志级别记录消息,而默认的Serilog级别为.MinimumLevel.Debug()。您可以在LoggerConfiguration上调用Serilog.Log来更改日志记录级别。另外,对于通过Logger静态类记录消息,您应该设置其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; } } 属性。

这是一个将登录到Hangfire控制台的固定代码:

PerformContext

这将有效,但是为每个作业创建新日志的整体方法非常糟糕。您可以通过将LogEvent的实例传递到LogEventPropertyValue中的属性来避免这种情况。您应该定义一个派生自抽象PerformContext的自定义属性类,并公开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) { } } 的实例。

以下是最终代码:

<强> PerformContextProperty.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)));
    }
}

<强> PerformContextEnricher.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;
            }
        }
    }
}

<强> TestJob.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);
        }
    }
}

<强> HangfireContextSink.cs:

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

<强> SinkExtensions.cs:

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

Serilog配置:

.Enrich.FromLogContext()

在Serilog配置中,请勿忘记致电LogContext,以便使用{{1}}中的属性丰富日志事件。

以上代码非常简单,这就是我没有详细评论的原因。如果您对代码有疑问,请询问,我会尝试解释不清楚的时刻。