.NET Core应用Windows本地调试启用Gelf时崩溃求助
我有一个.NET Core应用,生产环境(K8s)运行正常,使用NLog将日志输出到debug.txt和Graylog。但在Windows 10本地调试时,启用Graylog目标会导致应用崩溃并抛出栈溢出错误,禁用Graylog则一切正常。已尝试更新NLog、Gelf组件,移除应用中的Newtonsoft.Json依赖,但问题仍未解决。
NLog配置片段
<target xsi:type="Gelf" name="graylog" endpoint="udp://graylogHost" facility="netcore" SendLastFormatParameter="true" gelfVersion="1.1"> <parameter name="assembly" layout="${appdomain:format={1\}:cached=True}" /> <parameter name="log-level" layout="${level:uppercase=true}" /> <parameter name="callsite" layout="${callsite:className=True:fileName=False:includeSourcePath=False:methodName=True}" /> <parameter name="threadid" layout="${threadid}" /> <!--<parameter name="hostname" layout="${right:inner=${hostname}:length=5}"/>--> <parameter name="hostname" layout="${hostname}" /> <parameter name="activityId" layout="${activityid:whenEmpty=${aspnet-TraceIdentifier:whenEmpty=${activity:property=TraceId}}}" /> <parameter name="hierarchicalId" layout="${activity:property=Id}" /> <parameter name="spanId" layout="${activity:property=SpanId}" /> <parameter name="parentId" layout="${activity:property=ParentId}" /> <parameter name="traceId" layout="${activity:property=TraceId}" /> </target>
错误信息
Stack overflow. Repeat 12112 times: -------------------------------- at System.Reflection.Emit.InternalAssemblyBuilder.Equals(System.Object) at System.Reflection.Emit.InternalModuleBuilder.Equals(System.Object) -------------------------------- at System.Collections.Generic.ObjectEqualityComparer`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].IndexOf(System.__Canon[], System.__Canon, Int32, Int32) at System.Array.IndexOf[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon[], System.__Canon, Int32, Int32) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContract, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContract ByRef, System.Object ByRef) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Newtonsoft.Json.JsonWriter, System.Object, Newtonsoft.Json.Serialization.JsonObjectContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(Newtonsoft.Json.JsonWriter, System.Object, System.Type) at Newtonsoft.Json.JsonSerializer.SerializeInternal(Newtonsoft.Json.JsonWriter, System.Object, System.Type) at Newtonsoft.Json.Linq.JToken.FromObjectInternal(System.Object, Newtonsoft.Json.JsonSerializer) at NLog.Web.AspNetCore.Targets.Gelf.GelfConverter.AddAdditionalField(System.Collections.Generic.IDictionary`2<System.String,Newtonsoft.Json.Linq.JToken>, System.Collections.Generic.KeyValuePair`2<System.Object,System.Object>) at NLog.Web.AspNetCore.Targets.Gelf.GelfConverter.GetGelfJson(NLog.LogEventInfo, System.String, System.String) at NLog.Web.AspNetCore.Targets.Gelf.GelfTarget.Write(NLog.LogEventInfo) at NLog.Targets.Target.Write(NLog.Common.AsyncLogEventInfo) at NLog.Targets.Target.Write(System.Collections.Generic.IList`1<NLog.Common.AsyncLogEventInfo>) at NLog.Targets.Target.WriteAsyncThreadSafe(System.Collections.Generic.IList`1<NLog.Common.AsyncLogEventInfo>) at NLog.Targets.Target.WriteAsyncLogEvents(System.Collections.Generic.IList`1<NLog.Common.AsyncLogEventInfo>) at NLog.Targets.Wrappers.AsyncTargetWrapper.WriteLogEventsToTarget(System.Collections.Generic.IList`1<NLog.Common.AsyncLogEventInfo>, System.String) at NLog.Targets.Wrappers.AsyncTargetWrapper.WriteLogEventsInQueue(Int32, System.String) at NLog.Targets.Wrappers.AsyncTargetWrapper.ProcessPendingEvents(System.Object) at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Threading.TimerQueueTimer.Fire(Boolean) at System.Threading.TimerQueueTimer.System.Threading.IThreadPoolWorkItem.Execute() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart() at System.Threading.Thread.StartCallback()
原因分析
本地调试的动态程序集特性
Windows本地调试时,.NET Core会生成InternalAssemblyBuilder/InternalModuleBuilder这类动态程序集,用于支持热重载、调试符号等功能;而K8s生产环境是发布后的静态程序集,不存在这类动态对象。当Gelf目标序列化${appdomain:format={1\}:cached=True}获取的程序集对象时,Newtonsoft.Json会遍历其内部属性,触发动态程序集的循环引用检查,反复调用Equals方法导致栈溢出。Gelf组件的Newtonsoft.Json依赖
即使移除了应用中的Newtonsoft.Json,NLog的Gelf目标依赖的组件内部仍会引用Newtonsoft.Json。该组件在处理动态程序集对象时,循环引用检测逻辑无法正确处理InternalAssemblyBuilder的嵌套引用,最终引发栈溢出。布局渲染器的返回类型差异
${appdomain:format={1\}}渲染器默认返回Assembly对象而非字符串。生产环境的静态程序集序列化不会触发循环引用,但本地调试的动态程序集则会引发问题,这就是K8s环境正常、本地调试异常的核心原因。
临时解决方案
修改assembly参数的布局,直接输出程序集名称字符串而非对象:
<parameter name="assembly" layout="${assembly-name:cached=True}" />
或者调整${appdomain}的格式化方式确保返回字符串:
<parameter name="assembly" layout="${appdomain:format={0}:cached=True}" />
内容的提问来源于stack exchange,提问作者smartdiv

