我可能在这里缺少一些基本的东西,但是否可以在自定义NLog事件中检索HttpContext.Current
?
我试图为每个请求提供一个唯一的Guid,这样我就可以将日志消息与单个事件关联起来(即,将单个请求的每个日志事件绑定在一起)。因此,我想将这个Guid存储在HttpContext.Current.Items
中,然后在NLog目标中检索它,并将其包含在日志消息中。
以下是我想要访问HttpContext.Current
:的示例目标
[Target("AzureTableTarget")]
public class AzureTableTarget : TargetWithLayout
{
public AzureTableTarget()
{
_appSettings = IoCResolver.Get<IAppSettings>();
}
protected override void Write(LogEventInfo logEvent)
{
var correlationId = HttpContext.Current; //This is always null
var batchOperation = new TableBatchOperation();
CxLogEventBuilder.Build(_appSettings, logEvent).ForEach(batchOperation.Insert);
_loggingTable.ExecuteBatchAsync(batchOperation);
}
}
现在在NLog目标(适用于ASP.NET和ASP.NET Core)中检索HTTP上下文更容易
- 安装NLog.Web(ASP.NET)或NLog.Web.AspNetCore(ASP.NET Core)包
- 对于ASP.NET核心,请遵循ASP.NET核心-NLog设置
- 从
AspNetLayoutRendererBase
继承(命名空间NLog.Web.LayoutRenderers
) - 调用
var context = HttpContextAccessor.HttpContext;
获取请求
示例:
[LayoutRenderer("aspnet-sessionid")]
[ThreadSafe]
public class AspNetSessionIdLayoutRenderer : AspNetLayoutRendererBase
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
var context = HttpContextAccessor.HttpContext;
var contextSession = context?.Session();
if (contextSession == null)
{
InternalLogger.Debug("HttpContext Session Lookup returned null");
return;
}
builder.Append(contextSession.SessionID); // ASP.NET Core: contextSession.Id
}
}
PS:目前有许多用于ASP.NET(Core)的预定义渲染器:https://nlog-project.org/config/?tab=layout-渲染器&search=aspnet
如果您的自定义目标应该捕获一个(或多个)特定于上下文的值,那么我建议您的目标从TargetWithContext(或AsyncTaskTarget)继承。
它提供了设置和捕获contextproperty
项目的能力。可以指定布局以捕获上下文详细信息的位置。可以从HttpContext:轻松获得可能的上下文详细信息的示例
https://nlog-project.org/config/?tab=layout-渲染器&search=包:nlog.web.aspnetcore
有关编写自定义目标的更多详细信息:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target-for-structured-logging
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-async-target
Btw。已经存在这个自定义目标,它很好地继承了AsyncTaskTarget
:
https://www.nuget.org/packages/NLog.Extensions.AzureCosmosTable/
这篇关于使用HttpContext.Current的文章可能会有所帮助。对您来说,关键可能是当控制从一个线程传递到另一个HttpContext.Current时,新线程中的值可以为null。
这里是SO上的另一个问题/答案,它描述了HttpContext.Current在web服务的上下文中为null。接受的答案建议在web.config文件中启用ASP.Net兼容性。
我不知道这两种都会有帮助,但它们可能会。我通过谷歌搜索"HttpContext.Current为null"找到了它们,这产生了相当多的点击量。我只做了很少的ASP.NET开发,所以根据我个人的经验,我不能真正评论HttpContext.Current。
考虑到您的用例,我建议您查看System.Diagnostics.CorrelationManager.ActivityId.
ActivityId的一个很好的特性是它从父线程"流"到子线程(包括线程池线程)。我认为它与任务和并行操作配合得很好。工作良好,这意味着在父线程中设置的ActivityId在子线程中具有预期值。
ActivityId没有LayoutRenderer,但写一个就足够容易了。请参阅此处的示例(针对NLog 1.0编写):
最有用的NLog配置
我很确定"EstimatedBufferSize"的东西已经不需要了,所以类似的东西可能会起作用:
[LayoutRenderer("ActivityId")]
class ActivityIdLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Trace.CorrelationManager.ActivityId);
}
}
如果采用此方法,您可能会考虑向ActivityIdLayoutRenderer添加Format属性,以便指定guid格式。看看(我的)这个答案。它包含了许多关于使用guid的有用信息。
NewGuid与System.Guid.NewGuid().ToString("D");
请参阅这个源文件(在NLog的git存储库中),了解如何实现和使用这样一个Format属性的示例:
https://github.com/NLog/NLog/blob/master/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs