对于 C# 日志记录,如何以最小的开销获取调用堆栈深度?
我已经为 Log4net 创建了一个包装器(我可能会放弃它而转而使用 NLog;我尚未决定),并且我缩进记录的消息结果以给出调用结构的想法。例如:
2011-04-03 00:20:30,271 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessAdminCommand - ProcStart - User Info Repository
2011-04-03 00:20:30,271 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.StartOneProcess - User Info Repository
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.SetProcessStatus - Process = User Info Repository, status = ProcStarting
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.SendProcessStatusInfo
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MhlAdminLayer.SendToAllAdministrators - ProcessTable
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MReflection.CopyToBinary
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MReflection.CopyToBinary - False
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MhlBasicLayer.SendToAllConnections - 228 - True - False
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MmlNonThreaded.SendObject - 228
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MllTcpSocket.SendMessage - 228 - True
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CreateFromBinary
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CopyFromBinary - Bytes = 71
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.MessagingCallback - User Info Repository - ProcessInfoAndRequests
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessProcessInfoAndRequests - User Info Repository
我使用 System.Diagnostics.StackTrace 并计算 StackFrames 来执行此操作。
现在的问题是:有没有更有效的方法来做到这一点?我只需要确定(相对)调用堆栈深度,即当前深度加上或减去上次调用日志包装器时的深度。 (请注意,我实际上并没有使用 StackFrame 对象 - 否则我会得到方法名称。)
我希望有一些简单的高性能方法来查询调用堆栈深度或堆栈使用情况。
I have created a wrapper for Log4net (which I may be dropping in favor of NLog; I haven't decided yet), and I indent the logged messages result to give an idea of calling structure. For example:
2011-04-03 00:20:30,271 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessAdminCommand - ProcStart - User Info Repository
2011-04-03 00:20:30,271 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.StartOneProcess - User Info Repository
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.SetProcessStatus - Process = User Info Repository, status = ProcStarting
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.SendProcessStatusInfo
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MhlAdminLayer.SendToAllAdministrators - ProcessTable
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MReflection.CopyToBinary
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MReflection.CopyToBinary - False
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MhlBasicLayer.SendToAllConnections - 228 - True - False
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MmlNonThreaded.SendObject - 228
2011-04-03 00:20:30,411 [CT] DEBUG - Merlinia.CommonClasses.MllTcpSocket.SendMessage - 228 - True
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CreateFromBinary
2011-04-03 00:20:32,174 [10] DEBUG - Merlinia.CommonClasses.MReflection.CopyFromBinary - Bytes = 71
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.MessagingCallback - User Info Repository - ProcessInfoAndRequests
2011-04-03 00:20:32,174 [CT] DEBUG - Merlinia.ProcessManager.CentralThread.ProcessProcessInfoAndRequests - User Info Repository
I do this using System.Diagnostics.StackTrace and counting StackFrames.
Now here's the question: Is there any more efficient way of doing this? I only need to determine the (relative) call stack depth, i.e., is the current depth plus or minus what it was the last time my logging wrapper was called. (Note that I do not actually use the StackFrame objects - I get the method names otherwise.)
I'm hoping for some simple high-performance way of querying the call stack depth or stack usage.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
只需使用 StackTrace.FrameCount 属性,然后将其与之前记录的
FrameCount
进行比较。仅供参考,FrameCount
可能是检索实际帧计数的最快方法,因为它仅将内部m_iNumOfFrames
字段返回给您。Simply use the StackTrace.FrameCount property, and compare it to the previously recorded
FrameCount
. FYI,FrameCount
is probably the fastest method to retrieve the actual frame count, since it only returns the internalm_iNumOfFrames
field back to you.经过六年半的可靠服务后,在应用了 Microsoft 的更新(其中包括对 .Net Framework 4.5 的更改)后,我突然发现我的许多程序在 2017 年底崩溃了。这就是编写依赖于 mscorlib.dll 中内部未记录数据结构的代码所得到的结果。
此版本的代码再次工作,并且还被设计为在面对未来可能更新的 mscorlib.dll 时稍微更加健壮 - 希望它只是优雅地失败并始终返回零。但仍然无法保证未来对 mscorlib.dll 的更改不会导致此代码将来崩溃。
After six and a half years of trusty service, I suddenly experienced that many of my programs were crashing in late 2017, after having applied an update from Microsoft that included changes to .Net Framework 4.5. That's what you get for writing code that depends on internal undocumented data structures in mscorlib.dll.
This version of the code works again, and is also designed to be slightly more robust in the face of possible future updates to mscorlib.dll - it hopefully just fails gracefully and always returns zero. But there are still no guarantees against future changes to mscorlib.dll resulting in future crashes in this code.
感谢 Teoman Soygul,特别是 Oren Eini,他的博客 Teoman 提供了链接。
以下是一些“概念验证”代码,我认为这是我将使用的解决方案 - 尽管我必须承认我还没有进行任何计时测试。
编辑:在 Microsoft 于 2017 年末更新 mscorlib.dll 后,此版本不适用于 .Net Framework 4.5。请参阅我发布的有关更新版本的另一个答案。 (为了后代,我留下这个答案 - 它仍然适用于 .Net Framework 2.0 和 3.5。)
Thanks to Teoman Soygul and especially to Oren Eini, whose blog Teoman provided a link to.
The following is some "proof of concept" code that I think is the solution I'll be using - although I must admit I haven't done any timing tests.
EDIT: This version does not work for .Net Framework 4.5 after an update of mscorlib.dll by Microsoft in late 2017. See another answer I've posted for a newer version. (I'm leaving this answer for the sake of posterity - and it does still work for .Net Framework 2.0 and 3.5.)
Environment.StackTrace.Split(Environment.NewLine).Length
Environment.StackTrace.Split(Environment.NewLine).Length
如果您递归到相同的方法并且只想知道当前方法的深度,我正在使用这种检查,尽管它可能对性能不太好。我猜
new StackTrace()
可以存储在某个地方以避免每次重新创建,但就我而言,这并不重要,所以我这样使用它:只需将
CallingMethod
替换为你的方法的名称If you do recursion to same method and just want to know how deep in the current method you are I am using this kind of check, although it might be not great for performance. I guess
new StackTrace()
could be stored somewhere to avoid recreation each time, but in my case it was not important, so I use it like this:Just replace
CallingMethod
with the name of your method