【问题标题】:How to properly handle error logs?如何正确处理错误日志?
【发布时间】:2010-10-11 18:08:32
【问题描述】:

在发布此问题之前,我尝试进行了多次搜索。如果这是重复的,请告诉我,我会删除它。

我的问题围绕着处理通过我们的网络应用程序产生的错误的正确方法。我们目前通过 log4j 记录所有内容。如果发生错误,它只会在屏幕上显示“发生错误。已通知 IT 部门并将尽快纠正此问题”。这不会告诉用户任何事情......但是当我们尝试重现错误时,它也不会告诉开发人员任何事情。我们必须转到错误日志文件夹并尝试查找此错误。我还要提一下,该文件夹中充满了过去一周的日志。每次出现错误时,都会为该用户创建一个日志文件,并将电子邮件发送给分配给处理错误的 IT 人员。此电子邮件未提及日志文件名,但它是日志文件中写入的相同错误文本的副本。

因此,如果 Alicia 在 7:15 遇到问题,但同一分钟还发生了 10 个其他错误,我必须检查每个日志文件以尝试找到她的错误。

我向我的同事提出的建议是将错误日志表添加到数据库中。这将为每个错误向表中写入一条记录,记录它是针对谁、错误、发生在哪个页面等。这样做的好处是我们可以从表中返回主键值 (error_log_id) 和在页面上显示类似“已记录错误参考 ID (1337) 并通知适当的 IT 人员。请保留此参考 ID 以备将来使用”之类的消息。当我们收到电子邮件时,它会告诉我们错误的 ID 以供快速参考。或者如果用户是持久的,他们可以通过 id 联系我们,我们可以很快找到错误。

您如何设置错误日志记录?顺便说一句,我们的系统使用连接到 SQL Server 数据库的 Java Servlet。

【问题讨论】:

    标签: java servlets error-handling


    【解决方案1】:

    我反对将错误日志存储在数据库中的想法。日志系统应该尽可能简单,并且不涉及并非 100% 需要写入日志记录的组件。

    登录数据库时,事情可能会变得相当复杂 - 例如。您可能无法记录任何与数据库相关的错误(如何记录由于 DB 没有响应而发生的错误,例如由于负载过重或基础设施错误);我看到的另一个问题是可能需要单独的事务进行日志记录等。

    另一方面,为错误提供参考 ID 并不是一个坏主意,但同样,这也意味着增加日志系统的复杂性(例如,您将如何在应用程序的所有层中传播参考 ID什么时候出现错误?)

    在我参与的项目中,一般准则是尽可能详细地记录错误,并包含尽可能多的上下文信息(为了编写日志,我们通常使用“常规”方法 - log4j 或类似)。通常,即使对于负载较重的系统,这也很有效。

    【讨论】:

      【解决方案2】:

      如果多台服务器正在运行并且每台服务器都在自己身上留下日志消息,那么很难跟踪它们。因此,应该有人或工具按时间顺序收集和分类它们。 有一个发送所有消息的中心点是一种好方法。

      【讨论】:

        【解决方案3】:

        一个可能的解决方案,让您的错误页面包含一个“发送电子邮件到任何地方”链接。当用户点击这封电子邮件时,电子邮件的正文可能会以几行空行开头,后跟如下内容:

        ----请不要修改此行下面的信息。---

        错误详情

        任何通过此链接投诉的用户都会自动向您发送您需要的信息,如果您重现错误,您可以快速访问错误消息。您甚至可能有一个用于发送电子邮件的表单,这样用户就不会看到这一点(这对某些人来说可能很重要),但是您依赖于您的系统至少能够发送电子邮件。

        实际上,我发现在这样的错误页面上的 HTML 注释中打印错误详细信息很有用,这样我就可以自己随时了解它们。

        我同意上述大卫的观点,我不喜欢将此类信息存储在数据库中。

        【讨论】:

          【解决方案4】:

          有关日志记录的策略,您可以查看讨论Logging best practices

          【讨论】:

            【解决方案5】:

            我回答了一个类似的问题here,但我会根据您的问题调整该答案。

            我们为此目的使用 requestID - 在处理的最开始(在过滤器中)为每个传入 (HTTP) 请求分配一个请求 ID,然后将其记录在每个日志行上,这样您以后可以通过该 ID 轻松地 grep 这些日志并找到所有相关行。

            如果您认为将 ID 添加到每个日志语句非常繁琐,那么您并不孤单 - java 日志框架通过使用 Mapped Diagnostic Context (MDC) 使其变得透明(至少 log4j 和 logback 有这个)。

            RequestID 也可以作为一个方便的参考编号,以便在出现错误时吐出(正如您已经建议的那样)。但是,正如其他人所评论的那样,将这些详细信息加载到数据库是不明智的——最好使用文件系统。或者,最简单的方法是只使用 requestID - 然后在发生错误时您不需要做任何特别的事情。它只是帮助您找到正确的日志文件并在该文件中搜索。

            一个 requestID 是什么样子的?

            我们使用以下模式:

            :.

            In 包含以下变量:

            • instanceName 唯一标识特定部署环境中的特定 JVM / .
            • currentTimeInMillis 是不言自明的。我们选择以人类可读的格式“yyyyMMddHHmmssSSS”来表示它,因此很容易从中读取请求开始时间(注意:SimpleDateFormat 不是线程安全的,因此您需要同步它或在每个请求上创建一个新的)。
            • counter 是特定毫秒内的请求计数器 - 在极少数情况下,您可能需要在一毫秒内生成多个请求 ID

            如您所见,ID 格式已设置为保证 currentTimeInMillis.counter 组合在特定 JVM 中是唯一的,并且整个 ID 保证是全局唯一的(好吧,不是真正意义上的“全局”,但它对于我们的目的来说已经足够全局了),不需要涉及数据库或其他一些中心节点。此外,使用 instanceName 变量可以限制您以后需要查看的日志文件数量以查找该请求。

            那么,最后一个问题:“这在单 JVM 解决方案中很好,但你如何将它扩展到多个 JVM,通过某种网络协议进行通信?”

            当我们使用Spring Remoting 进行远程处理时,我们实现了自定义RemoteInvocationFactory(从上下文中获取请求ID 并将其保存到RemoteInvocation attributes)和RemoteInvocationExecutor(从属性中获取请求ID 并添加它到另一个 JVM 中的诊断上下文)。

            不确定如何使用纯 RMI 或其他远程处理方法来实现它。

            【讨论】:

              【解决方案6】:

              我过去曾使用过您建议的方法(登录到数据库),它非常很有帮助。

              您不仅可以通过 SQL 获取错误,还可以生成最常出现的错误的报告并首先处理它们。

              在我们所做的设计中,equals 堆栈跟踪属于相同的记录(因为它们完全起源于同一个地方)

              我们有一个小型应用程序汇集了该数据库,我们知道随后生成了一个新异常,而不是收到一封与前几周剩余时间相加的电子邮件被完全忽略。

              当然,这个数据库设计对于我们的应用程序来说是非常具体的,并且可以进行额外的识别,我们有软件版本、构建、有时输入参数等等等等。

              随着时间的推移,系统管理员会了解如何处理每种异常并相应地进行处理。

              但是!无论如何,您的应用程序可能不会那么大。可能您只需解析日志文件即可。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-12-06
                • 2012-12-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-05-13
                • 2011-12-04
                相关资源
                最近更新 更多