故障排除

编辑

某些功能无法按预期工作?以下是一些找出问题所在的方法。

第一步,请检查您的堆栈是否与当前支持的技术兼容。

如果您无法找出问题所在,请不要担心。在APM 讨论论坛中创建一个主题,我们将帮助您。

如果您这样做,请附加您的调试日志,以便我们分析问题。将完整日志上传到类似https://gist.github.com的服务。日志应包含从应用程序启动到执行第一个请求的所有内容。除了代理和应用程序日志外,请查找所有服务标准输出和标准错误日志中的[elastic-apm-agent]条目,当日志记录不可用时,我们有时会在其中打印有用的信息。

更新到最新代理版本

编辑

代理会频繁更新,并且版本发布与堆栈中的其他组件没有紧密关联。

因此,尝试更新到最新发布的代理版本通常是推荐的第一步故障排除步骤。如果可能,尝试使用最新快照甚至更好,因为它包含尚未发布的修复程序。

有关更多详细信息,请参阅升级文档

与其他代理一起运行

编辑

与许多其他 Java 代理一样,我们的代理通过在运行时将字节码注入到类中来检测类。我们的字节码检测保证只产生有效的字节码,并且永远不会更改类的“模式”。如果您除了我们提供的还使用其他具有相同保证的 Java 代理,则它们不应该相互干扰。但是,某些 Java 代理不符合这些保证,在这种情况下,可能无法与我们的代理并行使用。如果您遇到错误,首先要做的事情之一就是删除任何其他代理,以检查这是否确实是代理不匹配的情况。我们仍然可以找到一种方法使它们一起工作,但是此信息对于我们提供帮助的能力至关重要,因此请确保在报告此类问题时包含它。

日志记录

编辑

有几个与日志记录相关的配置选项。其中最重要的是log_level

将日志级别设置为DEBUG甚至TRACE以获取有关代理行为的更多信息。

  • DEBUG显示

    • 代理读取的代理配置
    • 事务和跨度创建、激活和停用事件
  • TRACEDEBUG更详细

    • 每次激活或停用事务或跨度时都会打印堆栈跟踪
    • 发送到 apm-server 的所有数据都包含在 JSON 格式中

寻求帮助时,请始终发布日志文件的全部内容。使用此过程确保在报告潜在问题时获得一致的日志。

代理启动时,您应该看到类似以下的日志

[main] INFO co.elastic.apm.agent.configuration.StartupInfo - Starting Elastic APM (unknown version) on Java 10 (Oracle Corporation) Mac OS X 10.13.6
[apm-server-healthcheck] INFO co.elastic.apm.agent.report.ApmServerHealthChecker - Elastic APM server is available: {"build_date":"2018-11-05T07:58:08Z","build_sha":"dffb98a72a262ca22adad0152f0245ea743ea904","version":"7.0.0-alpha1"}
[main] DEBUG co.elastic.apm.agent.configuration.StartupInfo - service_name: 'elastic-apm-test' (source: Java System Properties)
[main] DEBUG co.elastic.apm.agent.configuration.StartupInfo - log_level: 'DEBUG' (source: Java System Properties)

在发布日志文件之前,请确保对您的应用程序执行一些请求。每个请求至少应在日志中添加一些类似以下的行

[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - startTransaction '' 00-2a82cbe3df7a0208f7be6da65be260d1-05e72d045206587a-01 {
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Activating '' 00-2a82cbe3df7a0208f7be6da65be260d1-05e72d045206587a-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.transaction.SpanImpl - startSpan '' 00-2a82cbe3df7a0208f7be6da65be260d1-b2ffa0401105e3d8-01 {
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Activating 'APIRestController#products' 00-2a82cbe3df7a0208f7be6da65be260d1-b2ffa0401105e3d8-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.transaction.SpanImpl - startSpan '' 00-2a82cbe3df7a0208f7be6da65be260d1-49b9d805eca42ec6-01 {
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Activating '' 00-2a82cbe3df7a0208f7be6da65be260d1-49b9d805eca42ec6-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Deactivating 'SELECT' 00-2a82cbe3df7a0208f7be6da65be260d1-49b9d805eca42ec6-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.transaction.SpanImpl - endSpan 'SELECT' 00-2a82cbe3df7a0208f7be6da65be260d1-49b9d805eca42ec6-01
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Receiving SPAN event (sequence 23)
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Starting new request to http://127.0.0.1:8200/intake/v2/events
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Deactivating 'APIRestController#products' 00-2a82cbe3df7a0208f7be6da65be260d1-b2ffa0401105e3d8-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.transaction.SpanImpl - endSpan 'APIRestController#products' 00-2a82cbe3df7a0208f7be6da65be260d1-b2ffa0401105e3d8-01
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Scheduling request timeout in 10s
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Receiving SPAN event (sequence 24)
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - Deactivating 'APIRestController#products' 00-2a82cbe3df7a0208f7be6da65be260d1-05e72d045206587a-01 on thread 66
[http-nio-8080-exec-10] DEBUG co.elastic.apm.agent.impl.ElasticApmTracer - endTransaction 'APIRestController#products' 00-2a82cbe3df7a0208f7be6da65be260d1-05e72d045206587a-01
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Receiving TRANSACTION event (sequence 25)

如果在日志中没有看到任何内容,则您使用的技术堆栈可能不受支持

之后,您应该看到指示代理已成功将数据发送到 APM 服务器的日志

[apm-request-timeout-timer] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Request flush because the request timeout occurred
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Receiving FLUSH event (sequence 26)
[apm-reporter] DEBUG co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Flushing 10912 uncompressed 2667 compressed bytes

如果 APM 服务器响应 400,则可能表示 JSON 验证错误。日志将包含无法验证的实际文档

[apm-reporter] INFO co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Backing off for 0 seconds (±10%)
[apm-reporter] WARN co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - Server returned HTTP response code: 400 for URL: http://127.0.0.1:8200/intake/v2/events
[apm-reporter] WARN co.elastic.apm.agent.report.IntakeV2ReportingEventHandler - {"accepted":13,"errors":[{"message":"Problem validating JSON document against schema: I[#] S[#] doesn't validate with \"span#\"\n  I[#] S[#/allOf/2] allOf failed\n    I[#] S[#/allOf/2/required] missing properties: \"transaction_id\"","document":"{\"span\":{\"name\":\"OpenTracing product span\",\"timestamp\":29352159207,\"id\":\"aeaa7e0ac95acad6\",\"trace_id\":\"d88b5cbfc4536f9a700cd114a53bfeae\",\"parent_id\":\"082fd71ce7e4089a\",\"duration\":17.992,\"context\":{\"tags\":{\"productId\":\"1\"}},\"type\":\"unknown\"}}"}]}

捕获日志过程

编辑

理想情况下,这应该在生产环境之外进行。

  • 它需要重新启动应用程序
  • 使用 DEBUG 日志级别时,日志可能非常冗长,使用 TRACE 日志级别时,日志将更加冗长。
  • 日志中只有少量事务可以使调查更容易,生产流量会产生噪声。
  • 如果不可能,我们仍然可以在生产环境中使用它几分钟,当使用外部配置文件(文档)时,log_level可以在运行时更改。

大多数调查都需要使用DEBUG日志级别,因此除非要求使用TRACE,否则请使用DEBUG

在这里,我们将代理日志文件称为/tmp/agent.log,但可以使用任何其他位置。

  1. 使用log_level=debuglog_level=trace文档)和log_file=/tmp/agent.log文档)配置代理
  2. 截断/tmp/agent.log文件并重新启动应用程序
  3. 执行几个代理无法正确捕获的事务
  4. 复制/tmp/agent.log文件并将其发送回进行调查

代理匹配启发式方法

编辑

代理依靠启发式方法来有效地定义哪些类需要检测或不需要检测,以防止检测开销。

这些启发式方法基于包和类名称,并受application_packagesjms_listener_packages配置的影响。但是,如果检测未按预期应用,或者如果您想在不了解应用程序内部结构的情况下调查创建配置,则禁用这些启发式方法进行调查可能相关

  1. 通过设置enable_type_matching_name_pre_filtering=false禁用名称启发式方法并启用代理日志
  2. 重新启动应用程序,由于额外的开销,它将比平时慢
  3. 通过过滤包含Method match字符串的行来分析代理日志,以识别哪些类/方法已被检测。
  4. 使用适用的值正确配置application_packagesjms_listener_packages

调试

编辑

有时仅仅阅读日志不足以调试问题。由于代理是开源的并在 Maven Central 上发布,因此调试代理代码非常容易。

为了让您的 IDE 下载源代码,首先声明对代理的依赖项。

代理是通过-javaagent标志添加的。因此,您必须确保以不会两次将代理添加到类路径的方式声明依赖项。例如,当您正在开发 Web 应用程序时,请确保代理未打包在您的 war 文件的WEB-INF/lib中。在 IDE 的调试窗口中,请确保apm-agent未出现在-classpath中。

pom.xml。

<dependency>
    <groupId>co.elastic.apm</groupId>
    <artifactId>apm-agent</artifactId>
    <version>${elastic-apm.version}</version>
    <scope>provided</scope>
</dependency>

即使将scope设置为provided,IntelliJ 有时也会将代理添加到类路径。一种解决方法是将scope设置为test

build.gradle。

compileOnly "co.elastic.apm:apm-agent:$elasticApmVersion"

在 1.26.0 之前的版本中,您需要声明对elastic-apm-agent模块的依赖项,而不是apm-agent

常见问题

编辑

Kibana APM 应用程序中没有数据

编辑

此问题的最常见原因是代理和 APM 服务器之间的连接问题。

如果 APM 服务器未收到来自代理的数据,请检查代理是否能够建立与服务器的连接。在代理日志中,查找包含Elastic APM server is availableElastic APM server is not available的日志。

如果您看到消息Elastic APM server is not available,则表示代理在连接到 APM 服务器时遇到问题。检查server_url的设置,并确保代理能够连接到服务器。尝试从代理运行的机器上执行curl -v <apm-server-url>。服务器应以 200 状态代码响应。

如果 APM 服务器未成功响应,请查看 APM 服务器日志以验证服务器是否正在运行。还要确保配置防火墙,以便代理运行的主机可以打开到 APM 服务器的 HTTP 连接。

Kibana APM 应用程序显示“未知路由”

编辑

默认情况下,事务使用处理请求的 Servlet 名称命名。因此,如果请求未到达 Servlet,代理将默认将事务命名为“未知路由”

这可能发生的原因有两个

  1. 请求到达 Servlet,但代理无法正确检测到它们。
  2. 请求未到达 Servlet。它可能是由过滤器、静态资源等处理的。

    请求到达 Servlet

    代理具有一个预过滤器启发式方法,只考虑名称以Servlet结尾的类。可以通过设置内部配置enable_type_matching_name_pre_filtering=false来禁用此启发式方法。

    请注意,这会影响所有插件。预计应用程序启动时间会有少量开销增加。

    请求未到达 Servlet

    可以更改默认事务命名以使用 URL 路径。有关更多信息,请参阅use_path_as_transaction_name ( [1.0.0] 在 1.0.0 中添加。自 1.22.0 版本起,可以在运行时更改此值 )

    不幸的是,如果事务路径相似,这可能会创建大量重复的事务。例如,在/usr/{id}中,其中{id}是用户 ID,最终事务数量可能与用户数量一样多。您可以使用url_groups(已弃用)来减轻这个问题,它允许在事务 URL 中使用通配符。

如果建议的修复方案无法解决问题,或者需要自定义名称,则可以使用我们的 API 在整个请求处理流程中手动设置事务名称。

针对旧版 Java 版本编译的库

编辑

如果您的应用程序中出现类似这样的警告,则表示您正在使用针对非常旧的 Java 版本编译的库。

org.apache.commons.dbcp.DelegatingStatement uses an unsupported class file version (pre Java 5) and can't be instrumented.
Consider updating to a newer version of that library.

这主要涉及 JDBC 驱动程序。将它们更新到较新的版本应该可以解决问题。

找不到 Premain-Class 清单属性

编辑

如果您使用的是带有-javaagent标志针对应用程序服务器的手动设置,并且看到Failed to find Premain-Class manifest attribute错误以及启动失败,则您可能指向了错误的 jar 文件。

指向的正确 jar 文件应为elastic-apm-agent-<version>.jar形式,有关如何下载此文件的更多信息,请参阅手动设置说明

与 APM 服务器的通信

编辑

unable to find valid certification path to requested target - 服务器身份验证失败。请查看APM 服务器证书身份验证

java.net.SocketException: Broken pipe - 一个可能的原因是客户端身份验证失败。请查看代理证书身份验证

对于其他与 SSL/TLS 相关的問題,请查看JSSE 故障排除部分。您可以将-Djavax.net.debug=all添加到 JVM 命令行以获取有关问题的更多详细信息。

不常见的问题

编辑

JVM 崩溃

编辑

通常情况下,JVM 崩溃表明 JVM 中的错误是由在被跟踪应用程序及其依赖项的特定配置中安装 Java 代理而导致的。因此,首先尝试将 JVM 升级到最新的次要版本。

已知问题

  • 更新 40 之前的早期 Java 8 版本不受支持,因为它们存在一些错误,这些错误可能会在 Java 代理处于活动状态时导致 JVM 崩溃,因此代理在这些版本上将无法启动
  • 类似地,更新 60 之前的 Java 7 版本不受支持,因为它们在invokedynamic方面存在错误。
  • 已知更新 60 后的 Java 7 版本和更新 40 之前的早期 Java 8 版本会在启动后(有时很久之后)的某个随机点与代理版本 1.18.0-1.20.0 崩溃,这是由于一个错误导致 C2 编译器创建错误的本机代码。此类崩溃的症状是不确定的。为了防止此类崩溃,我们在代理版本 1.21.0 中添加了内置的延迟以进行代理初始化,这将自动应用于这些 Java 版本。如果代理版本 > 1.20.0 仍然发生崩溃,请尝试以下方法之一

    1. -XX:CompileCommand=exclude,java.lang.invoke.LambdaForm*::*添加到命令行以避免有问题的 JIT 编译。
    2. 通过设置elastic.apm.delay_agent_premain_ms系统属性来增加默认值(3000 毫秒)的延迟,以指示要延迟的毫秒数,例如通过命令行:-Delastic.apm.delay_agent_premain_ms=10000
  • profiling_inferred_spans_enabled [1.15.0] 在 1.15.0 中添加。 实验性)设置为true时,它使用一个本机库从 JVM 收集低级信息。到目前为止,所有已知问题都已修复。如果您认为崩溃可能与此相关,请尝试禁用它。我们持续升级到最新的异步分析器版本,因此将您的代理升级到最新版本可能已经包含修复程序。

每当遇到 JVM 崩溃时,请通过我们的论坛或通过在我们的GitHub 存储库上创建一个问题来报告。查找崩溃日志(例如hs_err_pid<PID>.log)并在报告时提供它,以及描述您的设置和场景的所有因素。

JVM 挂起

编辑

如果您的 JVM 在附加 Java 代理时挂起,请创建一个线程转储(例如,通过jstack),并通过我们的论坛或通过在我们的GitHub 存储库上创建一个问题来报告。

使用jlink的自定义 Java 运行时

编辑

如果您使用jlink创建自定义运行时,请确保添加以下模块:--add-modules java.base,java.logging,java,jdk.zipfs,java.management,jdk.management

禁用代理

编辑

如果代理意外导致生产应用程序中断,您可以在进行故障排除时禁用代理。

使用动态配置,您可以通过将recording设置为false来禁用事件的记录。

如果这不起作用,您可以通过将enabled设置为false来完全禁用代理。您需要重新启动应用程序才能使此更改生效。

不受支持的框架版本

编辑
  • JSF - 一些 2.2.x 版本的 myfaces 在 JDK 15 上不受支持 - 请参阅相关错误