使用EventSource进行ETW日志记录时可能会错过事件的风险

23
我正在使用EventSource类对我的.NET 4.5应用程序进行仪表化,以发出ETW事件。目标是能够捕获其中的一些事件(错误级别事件)进行错误日志记录。
在阅读和测试后,我对这种错误日志记录方法的可靠性表示担忧,特别是关于事件丢失或缺失的可能性。如果我的错误日志记录不起作用,我需要应用程序关闭(在我的情况下,它运行未报告的错误是不安全的)。在使用ETW和EventSource时,我如何确保我的错误被正确记录?
显然,答案的一部分将取决于谁在监听这些事件。在我的情况下,我计划使用最新的MS企业库中的“语义日志应用程序块”。
以下是Microsoft讨论可能导致事件丢失的原因之一: 关于事件跟踪 在那里,他们列出了这些可能导致事件丢失的原因。
总事件大小超过64K。这包括ETW头以及数据或有效载荷。用户无法控制这些丢失的事件,因为应用程序配置了事件大小。
ETW缓冲区大小小于总事件大小。用户无法控制这些丢失的事件,因为应用程序记录事件时配置了事件大小。
对于实时日志记录,实时使用者未能快速消耗事件或根本不存在,然后备份文件正在填充。如果在记录事件时停止并启动事件日志服务,则可能会出现此问题。用户无法控制这些丢失的事件。
当记录到文件时,磁盘速度跟不上记录速度。
为了查看是否使用EventSource类(例如,它是否以某种方式截断大型有效载荷)来缓解这些问题,我进行了一些测试。我尝试记录长字符串,但在30,000至35,000个字符之间失败(与最大64KB事件有效载荷相吻合)。据我所知,对于太大的字符串,它只是静默地什么也不做,在我的语义日志应用程序块日志中根本没有事件。前后的事件都像往常一样被写入。
任何时候当我的有效载荷中有一个字符串,我都必须通过某种截断器传递它吗?我还需要手动避免生成“太快”的事件吗(如何可能)?
Microsoft Patterns and Practices 应该引导我们走向良好的...模式和实践...所以也许我在这里漏掉了什么。
更新:
好吧,显然在消费应用程序中有一些关于“事件过快”的通知。我今天第一次收到这个:
级别:警告,消息:由于跟踪会话中的缓冲区溢出或架构同步延迟,某些事件将丢失:Microsoft-SemanticLogging-Etw-svcRuntime 然后在关闭会话时:
级别:警告,消息:在跟踪会话“Microsoft-SemanticLogging-Etw-svcRuntime”中检测到 1 个事件的丢失。
更新2: 企业库开发人员指南描述了我刚提到的行为。
您需要监控由语义化日志应用程序块生成的日志消息,以确定是否存在缓冲区溢出和消息丢失的迹象。例如,具有事件ID 900和901的日志消息表示汇聚的内部缓冲区已经溢出;在进程外场景中,事件ID 806和807表示ETW缓冲区已经溢出。您可以修改汇聚器的缓冲配置选项,以降低缓冲区溢出的可能性。

我的问题仍然存在,我能否使用语义化日志记录同时确保我的应用程序在丢失错误时不运行?正常的跟踪事件可能会被丢弃...

我目前的想法是使用旧式的日志技术将"关键"错误记录到单独的类中,并将较不严重的错误(以及调试类型的事件)通过ETW管道进行处理。这并不太糟糕...如果找不到更好的建议,我可能会将其发布为解决方案。

更新3:

我收到的“缺少事件”警告与缓冲区溢出无关,事实证明,如果将空字符串作为有效载荷值传递,则会收到此消息。


仅记录小消息,使用自己创建的其他机制将较大的消息发送到文件中,这样不是更好吗?似乎“大消息”和“高性能”是矛盾的目标。我想知道需要这样大消息的错误类型是什么。 - Frank Hileman
可能没有什么会那么大……我的意思是,我见过“相当大”的堆栈跟踪,特别是来自Parallel.ForeachAggregateException,但即使如此,这也不太可能。此时,我将“错误”记录到两个来源……一个是进程外的ETW,另一个是传统的日志管道,它在进程内阻塞,直到消息被写入。总的来说,这很好……我可能可以使用语义日志记录块完成所有这些工作(使用进程外组件进行各种日志记录/跟踪,并使用进程内事件侦听器进行实际错误记录)。 - TCC
我还应该说,最终我可能需要一个非ETW的进程内组件来处理错误,以正确响应瞬态错误条件并实现重试和冷却逻辑。因此,在这种情况下增加一些额外的错误日志记录并不会增加太多负担。这意味着某些错误在代码中被创建和维护两次(作为ETW事件和我的其他错误日志记录类),但没关系。 - TCC
1
我认为你的结论是正确的,冗长的消息最好在ETW中处理,这样你可以使用有趣的工具来分析它们,而不太常见的关键错误则必须至少在可靠的日志中重复。第一类消息可能被认为是性能或额外深度分析消息,而第二类则是错误消息。看起来ETW可以用于两者,但不能在同一个通道中使用。 - Frank Hileman
你的思路对我的项目有所帮助...我也遇到了类似的情况。 - Frank Hileman
3个回答

9
EventSource 类有两个版本,一个包含在 .NET Framework 中,另一个在 NuGet 包 Microsoft EventSource Library 中。我假设您使用 NuGet 包,因为它包含更新的代码。 EventSource 基类的构造函数有一个重载,它带有一个布尔类型的参数 throwOnEventWriteErrors,其文档如下(NuGet 包版本 1.0.26.0):
“默认情况下,调用 'WriteEvent' 方法时不会因错误而抛出异常(它们会默默地丢弃事件)。这是因为在大多数情况下,用户认为日志记录并不重要,不希望由于日志记录失败而导致程序崩溃。但是对于那些认为日志记录很重要的应用程序,如果写入事件失败,调用方希望能够及时处理异常,设置 'throwOnEventWriteErrors' 将导致在 WriteEvent 失败时引发异常。请注意,EventWrite 的成功并不一定意味着事件已到达目标位置,只表明写入操作没有失败。”
不幸的是,最后一句话包含了一个警示,但是如果您查看 EventSource 的源代码,您会发现来自操作系统调用的底层返回值用于引发不同的异常,以处理 NoFreeBuffersEventTooBig(以及其他错误)。
因此,如果您打开了 throwOnEventWriteErrors,则如果 EventSource 类无法将事件传递到 ETW,则会引发异常。然而,如果由于其他原因 ETW 失败,则不会引发任何异常,但如果您确保正确配置了 ETW 通道,这种情况应该很少发生。但是,由于您无法容忍丢失任何错误事件,因此应该测试极端错误情况,以确保 ETW 的行为符合您的期望。

谢谢Martin...当时我没有使用NuGet包,因为没有什么区别...我并不知道EventSource类是更新了这个参数,我会开始使用NuGet包,对于大多数情况,我会满意这种错误日志记录...也许有一些特殊情况需要额外关注,但这应该更符合我的习惯...我认为日志记录几乎在我做的所有事情中都很宝贵,哈哈...也许是因为很多它是无人值守的Windows服务之类的。 - TCC
@TCC:如果您开始使用NuGet包,您可能还会使用“EventRegister”工具在构建期间创建ETW清单。不幸的是,通道的大小参数并不适用于高容量/大数据包场景,这将导致数据丢失。我已经在我的构建中添加了一个XSLT转换来修改机器生成的清单中的通道,因为手动编写清单很繁琐且容易出错。 - Martin Liversage
2
请注意,具有throwOnEventWriteErrors的EventSource受保护构造函数重载自.NET 4.5.1起可在不使用NuGet包的情况下使用。 - bojingo

7
在上面的讨论中,有两个重要的点没有明确表述。
1. 所有与事件丢失相关的问题都与ETW(Windows事件跟踪)有关,而不是EventSource。逻辑上,EventSources与EventListeners通信,并且有一个内置的监听器将其转发到ETW。显然,当你谈论事件丢失时,链中的任何一个环节的限制都会影响数据流经过该链。因此,保证完全可靠性的一种方法是使用一个不使用ETW但直接到达所需数据位置的EventListener。我相信(语义日志应用程序块)有这样的监听器。
2. ETW已成功用于可靠地转发事件,但您必须遵守上述约束条件(事件大小必须保持小于64K,并且必须控制事件速率)。请注意,如果速率太高,您会知道这一点,因为WriteEvent将失败,因此您可以在暂停后重试,从而使某些内容完全可靠(以减慢程序速度为代价)。请注意,如果您真的在谈论错误(不应以极高的速率发生),则此类数据丢失并不是一个有趣的问题,如果它们以高速率发生,则很可能是冗余的(同一事物频繁触发)。
因此,总之,EventSource默认支持可靠事件,ETW默认不支持可靠事件,但可以使其支持,但通常情况下,ETW的默认值已经足够了。

感谢您提供权威的观点和澄清,Vance。我在生产代码中使用ETW或EventSource的默认设置时没有遇到任何问题。 - TCC

-1

尝试查看语义日志(MS Enterprise Library 6)http://msdn.microsoft.com/en-us/library/dn440729(v=pandp.60).aspx

您可以使用事件源并创建侦听器将日志记录到事件查看器、文件或数据库中(或创建自定义解决方案)

更新:即使在IoC场景下,我也捕获到了事件ID 806/807。在拦截器中,有一部分代码实例化了我的EventSource类:如果您错过了第一个实例的引用,则所有其他实例在构造函数上失败并在编写事件时引发事件ID 806/807

对于记录大数据,可以应用消息分割技术


是的,我在我的问题中特别提到了我正在使用那个库。 - TCC

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