要为 JProfiler 开发自定义探针 (probe),您应该了解一些基本概念和术语。 所有 JProfiler 探针 (probe) 的共同基础是它们会拦截特定的方法,并利用拦截到的方法参数和其他数据源构建一个包含您希望在 JProfiler UI 中查看的有用信息的字符串。
定义探针 (probe) 时的最初问题是如何指定被拦截的方法,并获得一个可使用方法参数和其他相关对象来构建字符串的环境。在 JProfiler 中,有三种不同的方式来实现这一点:
- 脚本探针 (script probe)完全在 JProfiler UI 中定义。您可以在调用树 (call tree) 中右键单击某个方法,选择脚本探针操作,并在内置代码编辑器中输入字符串表达式。这非常适合用于探针实验,但仅公开了自定义探针功能的极小部分。
- 嵌入式探针 (embedded probe) API 可以从您自己的代码中调用。如果您编写库 (library)、数据库驱动程序或服务器,您可以在产品中附带探针。任何使用 JProfiler 对您的产品进行分析的人,其探针都会自动添加到 JProfiler UI 中。
- 借助 注入式探针 (injected probe) API,您可以在 IDE 中利用 JProfiler 探针系统的全部功能为第三方软件编写探针。该 API 利用注解来定义拦截并注入方法参数和其他有用对象。
接下来的问题是:JProfiler 应该如何处理您创建的字符串?有两种不同的可用策略:有效负载 (payload) 创建或调用树拆分。
有效负载 (payload) 创建
探针构建的字符串可用于创建探针事件。该事件具有设置为该字符串的描述、等于被拦截方法调用时间的时间跨度,以及关联的调用栈 (call stack)。在对应的调用栈处,探针描述和耗时会被累加并作为有效负载 (payload) 保存到调用树 (call tree) 中。虽然事件在达到特定最大数量后会被合并,但调用树中累加的有效负载会显示整个记录周期的总数。如果 CPU 数据和您的探针都在记录中,则探针调用树视图 (view) 将显示合并的调用栈,其中有效负载字符串作为叶节点,并且 CPU 调用树视图 (view) 将包含指向探针调用树视图 (view) 的带注解链接。
就像 CPU 数据一样,有效负载 (payload) 可以在调用树 (call tree) 或热点 (hot spot) 视图 (view) 中显示。热点 (hot spot) 显示哪些有效负载消耗了大部分时间,而回溯跟踪则向您显示代码的哪些部分负责创建这些有效负载。为了获得良好的热点 (hot spot) 列表,有效负载字符串不应包含任何唯一 ID 或时间戳,因为如果每个有效负载字符串都不同,就不会有累加,也不会有明确的热点 (hot spot) 分布。例如,在预编译 JDBC 语句的情况下,参数不应包含在有效负载字符串中。
脚本探针根据所配置脚本的返回值自动创建有效负载 (payload)。注入式探针与此类似,它们从带有 PayloadInterception
注解的拦截处理方法中返回有效负载描述,可以作为字符串返回,也可以作为用于高级功能的 Payload 对象返回。另一方面,嵌入式探针通过调用带有有效负载描述参数的
Payload.exit 来创建有效负载,其中 Payload.enter 和 Payload.exit 之间的时间被记录为探针事件持续时间。
如果您正在记录在不同调用点 (call-site) 发生的对服务的调用,那么有效负载 (payload) 创建最为有用。典型的例子是数据库驱动程序,其中有效负载字符串是某种形式的查询字符串或命令。探针采用调用点 (call-site) 的视角,其中测量的实际工作由另一个软件组件执行。
调用树拆分
探针还可以采用执行站点 (execution site) 的视角。在这种情况下,被拦截的方法如何被调用并不重要,重要的是在其之后执行了哪些方法调用 (method call)。典型的例子是针对 Servlet 容器 (container) 的探针,其中提取的字符串是 URL。
与创建有效负载 (payload) 相比,现在更重要的是能够针对探针构建的每个不同字符串拆分调用树。对于每个此类字符串,调用树 (call tree) 中都会插入一个拆分节点,其中包含所有对应调用的累加调用树。原本只有一棵累加调用树的地方,现在有一组拆分节点将调用树分割成可以单独分析的不同部分。
多个探针可以产生嵌套拆分。单个探针默认仅产生一个拆分级别,除非它被配置为可重入 (Reentrant),这在脚本探针中不受支持。
在 JProfiler UI 中,调用树拆分并不与脚本探针功能捆绑在一起,而是一个名为“拆分方法”的独立功能。它们仅拆分调用树
(call tree) 而不创建有效负载 (payload),因此不需要带有名称和描述的探针视图 (view)。注入式探针从带有 SplitInterception
注解的拦截处理方法返回拆分字符串,而嵌入式探针则使用拆分字符串调用 Split.enter。
遥测 (telemetry)
自定义探针具有两个默认遥测 (telemetry):事件频率和平均事件持续时间。注入式探针和嵌入式探针支持额外的遥测 (telemetry),这些遥测是通过探针配置类中带有注解的方法创建的。在 JProfiler UI 中,脚本遥测独立于脚本探针功能,位于“Telemetries”部分中工具栏的 Configure Telemetries 按钮下。
遥测 (telemetry) 方法每秒轮询一次。在 Telemetry 注解中,您可以配置单位和缩放因子。使用 line 属性,可以将多个遥测合并到一个遥测视图 (view)
中。使用 TelemetryFormat 的 stacked 属性,可以使线条具有累加性,并将其显示为堆叠折线图形 (graph)。嵌入式探针和注入式探针中与遥测相关的
API 是等效的,但仅适用于它们各自的探针类型。
控制对象 (control object)
有时,将探针事件与 JProfiler 中称为“控制对象 (control object)”的关联长生命周期对象绑定在一起非常有用。例如,在数据库探针中,该角色由执行查询的物理连接承担。此类控制对象可以通过嵌入式 API 和注入式探针 API 进行打开和关闭,从而在探针事件视图 (view) 中生成相应的事件。创建探针事件时,可以指定控制对象,以便该探针事件计入探针的“Control objects”视图 (view) 中显示的统计信息。
控制对象 (control object) 具有显示名称,必须在打开它们时指定。如果在创建探针事件时使用了新的控制对象,则探针必须在其配置中提供名称解析器。
此外,探针可以通过枚举类定义自定义事件类型。创建探针事件时,可以指定这些类型之一,该类型会显示在事件视图 (view) 中,您可以在其中筛选单个事件类型。更重要的是,探针的时间线视图(将控制对象显示为时间轴上的线条)会根据事件类型进行着色 (colorization)。对于没有自定义事件类型的探针,着色 (colorization) 显示未记录事件的空闲状态以及探针事件持续期间的默认事件状态。使用自定义类型,您可以区分状态,例如“read”和“write”。
记录
与所有探针一样,自定义探针默认不记录数据,您必须根据需要启用和禁用记录。虽然您可以在探针视图 (view) 中使用手动启动/停止操作,但通常需要在开始时开启探针记录。因为 JProfiler 无法提前知道自定义探针,所以记录配置文件 (recording profile) 中有一个适用于所有自定义探针的 Custom probes 复选框。
类似地,对于启动和停止探针记录的触发器 (trigger) 操作,您可以选择 All custom probes。
对于编程式记录,您可以调用
Controller.startProbeRecording(Controller.PROBE_NAME_ALL_CUSTOM, ProbeRecordingOptions.EVENTS)
来记录所有自定义探针,或者传入探针的类名以进行更具体的指定。











