我觉得这应该比事实简单得多,或者我只是想得太多。
我有一个.NET Core 3.1 Web API应用程序,该应用程序使用HangFire在后台处理某些作业。我还配置了Application Insights以从.NET Core API记录遥测。
我可以在Application Insights中看到记录事件和相关性遥测数据。但是,每个事件/日志/依赖项都是根据唯一的OperationId和Parent ID记录的。
我正在尝试确定如何确保针对在后台作业中排队的原始请求的OperationId和/或父ID记录已记录的任何活动或在后台作业的上下文中使用的任何依赖项。
当我排队一个作业时,我可以获取传入的HTTP请求的当前OperationId,然后将其与该作业一起推入HangFire队列。然后执行作业时,我可以取回该OperationId。然后,我需要做的是使OperationID在作业执行的整个上下文/生命周期中都可用,以便将其附加到发送给Application Insightd的任何遥测中。
我以为我可以创建一个IJobContext接口,可以将其注入执行作业的类中。在这种情况下,我可以推送OperationID。然后,我可以创建一个ITelemetryInitializer,它也将IJobContext作为依赖项。然后,我可以在ITelemetryInitializer中设置要发送到Application Insights的遥测的OperationID和ParentId。这是一些简单的代码:
public class HangFirePanelMessageQueue : IMessageQueue
{
private readonly MessageProcessor _messageProcessor;
private readonly IHangFireJobContext _jobContext;
private readonly TelemetryClient _telemetryClient;
public HangFirePanelMessageQueue(MessageProcessor panelMessageProcessor,
IIoTMessageSerializer iotHubMessageSerialiser,
IHangFireJobContext jobContext, TelemetryClient telemetryClient)
{
_messageProcessor = panelMessageProcessor;
_jobContext = jobContext;
_telemetryClient = telemetryClient;
}
public async Task ProcessQueuedMessage(string message, string operationId)
{
var iotMessage = _iotHubMessageSerialiser.GetMessage(message);
_jobContext?.Set(iotMessage.CorrelationID, iotMessage.MessageID);
await _messageProcessor.ProcessMessage(iotMessage);
}
public Task QueueMessageForProcessing(string message)
{
var dummyTrace = new TraceTelemetry("Queuing message for processing", SeverityLevel.Information);
_telemetryClient.TrackTrace(dummyTrace);
string opId = dummyTrace.Context.Operation.Id;
BackgroundJob.Enqueue(() =>
ProcessQueuedMessage(message, opId));
return Task.CompletedTask;
}
}
IJobContext看起来像这样:
public interface IHangFireJobContext
{
bool Initialised { get; }
string OperationId { get; }
string JobId { get; }
void Set(string operationId, string jobId);
}
然后,我将拥有一个ITelemetryInitializer来丰富任何ITelemetry:
public class EnrichBackgroundJobTelemetry : ITelemetryInitializer
{
private readonly IHangFireJobContext jobContext;
public EnrichBackgroundJobTelemetry(IHangFireJobContext jobContext)
{
this.jobContext = jobContext;
}
public void Initialize(ITelemetry telemetry)
{
if (!jobContext.Initialised)
{
return;
}
telemetry.Context.Operation.Id = jobContext.OperationId;
}
}
但是,我遇到的问题是ITelemetryInitializer是一个单例,因此将使用IHangFireJobContext对其实例化一次,然后再对其进行后续的HangFire作业都不会进行更新。
我确实找到了https://github.com/skwasjer/Hangfire.Correlate项目,该项目扩展了https://github.com/skwasjer/Correlate。关联创建一个关联上下文,可以通过类似于IHttpContextAccessor的ICorrelationContextAccessor进行访问。
但是,Correlate的脚注状态为“请考虑.NET Core 3现在已经内置了对W3C TraceContext(blog)的支持,并且还有其他分布式跟踪库,其功能比Correlate更多。” 其中将Application Insights列为更高级的分布式跟踪的替代方法之一。
那么,有谁能帮助我了解在HangFire作业的上下文中创建到Application Insights的任何遥测信息时,我该如何丰富它们?我认为正确的答案是使用ITelemetryInitializer并在该ITelemetry项上填充OperationId,但是,我不确定要向ITelemetryInitialzer中注入什么依赖关系才能访问HangFire作业上下文。
当我排队一个作业时,我可以获取传入的HTTP请求的当前OperationId,然后将其与该作业一起推入HangFire队列。
因此,我是否正确地说您有一个控制器操作将工作推向停火状态?如果是这样,您可以在控制器方法内部进行操作,获取操作ID并将其传递给作业。使用该操作ID使用操作ID开始新操作。该操作以及在该操作期间生成的所有遥测将被链接到原始请求。
我没有进行hangfire集成,但是下面的代码显示了总体思路:一些工作在后台排队等待完成,并且应该链接到有关遥测的请求:
[HttpGet("/api/demo5")]
public ActionResult TrackWorker()
{
var requestTelemetry = HttpContext.Features.Get<RequestTelemetry>();
_taskQueue.QueueBackgroundWorkItem(async ct =>
{
using(var op = _telemetryClient.StartOperation<DependencyTelemetry>("QueuedWork", requestTelemetry.Context.Operation.Id))
{
_ = await new HttpClient().GetStringAsync("http://blank.org");
await Task.Delay(250);
op.Telemetry.ResultCode = "200";
op.Telemetry.Success = true;
}
});
return Accepted();
}
完整的示例可以在这里找到。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句