为Hangfire作业的每个Serilog添加唯一值

6
我将使用Hangfire处理后台作业,并使用Serilog记录日志。我试图通过添加"TrackingId"来为我的serilogs添加信息,以便从特定的Hangfire作业中筛选具有相同"TrackingId"的所有日志。
在`Startup.cs`中,我配置Serilog如下:
Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(Configuration)
    .WriteTo.Seq(serverUrl: serverUrl, apiKey: apiKey)

    // Enrich the logs with a tracking id. Will be a new value per request
    .Enrich.WithProperty("TrackingId", Guid.NewGuid())

    .CreateLogger();

我这样排队任务:

BackgroundJob.Enqueue<MyService>(myService => myService.DoIt(someParameter));

但是这样做将不会为每个Hangfire作业设置单独的TrackingId。有没有办法可以实现这一点?


我已经查看了Hangfire API,但无法轻松找到解决方法;添加“服务器过滤器”似乎可以起作用。希望有所帮助。 - Nicholas Blumhardt
1个回答

0
值得一提的是,我最终使用下面展示的服务器/客户端过滤器和GlobalJobFilters注册来完成此操作。 我遇到的一个令人烦恼的问题是,默认情况下将AutomaticRetryAttribute添加到GlobalJobFilters集合中,并且该类将记录失败作业的错误,而不知道我们在自定义JobLoggerAttribute中创建的Serilog LogContext。 就我个人而言,我只允许手动重试,因此我只需删除该属性并在IServerFilter.OnPerformed方法中处理错误即可。 如果这对您有用,请查看我的帖子末尾以了解如何删除它。
如果您要允许自动重试,则需要:1)创建一个自定义属性,装饰AutomaticRetryAttribute并使其了解自定义LogContext,2)再次从GlobalJobFilters集合中删除默认的AutomaticRetryAttribute,并3)将您的装饰器属性添加到集合中。
public class JobLoggerAttribute : JobFilterAttribute, IClientFilter, IServerFilter
{
    private ILogger _log;

    public void OnCreating(CreatingContext filterContext)
    {
        _log = GetLogger();

        _log.Information("Job is being created for {JobType} with arguments {JobArguments}", filterContext.Job.Type.Name, filterContext.Job.Args);
    }

    public void OnCreated(CreatedContext filterContext)
    {
        _log.Information("Job {JobId} has been created.", filterContext.BackgroundJob.Id);
    }

    public void OnPerforming(PerformingContext filterContext)
    {
        if (_log == null)
            _log = GetLogger();

        _log.Information("Job {JobId} is performing.", filterContext.BackgroundJob.Id);
    }

    public void OnPerformed(PerformedContext filterContext)
    {
        _log.Information("Job {JobId} has performed.", filterContext.BackgroundJob.Id);

        if (filterContext.Exception != null)
        {
            _log.Error(
                filterContext.Exception,
                "Job {JobId} failed due to an exception.",
                filterContext.BackgroundJob.Id);
        }

        _log = null;
    }

    private ILogger GetLogger()
    {
        return Log.ForContext(GetType()).ForContext("HangfireRequestId", Guid.NewGuid());
    }
}

而且注册...

GlobalJobFilters.Filters.Add(new JobLoggerAttribute());

移除 AutomaticRetryAttribute...

var automaticRetryFilter = GlobalJobFilters.Filters.Where(x => x.Instance is AutomaticRetryAttribute).Single();
GlobalJobFilters.Filters.Remove(automaticRetryFilter.Instance);

1
这个实际上是如何保证线程安全的?_log字段在工作线程之间是共享的。 - Bryan Crosby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接