簡體   English   中英

Serilog輸出到hangfire上下文控制台

[英]Serilog output to hangfire context console

我希望能夠使用我的日志通過Hangfires context.console發出事件,這樣我就不需要使用context.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;
                }
            }
        }

消息不會記錄到Hangfire控制台,因為您使用Debug日志級別記錄它們,而默認的Serilog級別是Information 您可以在LoggerConfiguration上調用.MinimumLevel.Debug()來更改日志記錄級別。 另外,對於通過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;
    }
}

這將起作用,但是為每個作業創建新日志的總體方法非常糟糕。 您可以通過在LogEvent通過屬性傳遞PerformContext實例來避免這種情況。 您應該定義一個從抽象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)));
    }
}

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

SinkExtensions.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屬性豐富日志事件。

上面的代碼非常簡單,這就是為什么我不詳細評論它。 如果您對代碼有疑問,請詢問,我會嘗試解釋不清楚的時刻。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM