自定义启发式算法
我们用“启发式算法”一词来指代那些简单而稳健的算法,用于检测诸如高 CPU 占用或愤怒点击(click rage)之类的问题。在这里,我们将通过一个示例来理解启发式算法的工作原理,以及如何创建你自己的检测器,并在 DevTools 中查看其结果。
启发式算法的模板位于路径 openreplay/backend/pkg/handlers/custom 中。
package custom
import . "openreplay/backend/pkg/messages"
type CustomHandler struct {
lastTimestamp uint64
}
func (h *CustomHandler) Handle(message Message, messageID uint64, timestamp uint64) Message {
h.lastTimestamp = timestamp
return nil
}
func (h *CustomHandler) Build() Message {
return nil
}
创建好启发式方法后,可以通过修改路径 openreplay/backend/cmd/heuristics 下的
main.go 文件来添加它:导入自定义模块,并将自定义函数加入到消息处理器(message processor handler)中。
package main
import (
"log"
"openreplay/backend/internal/config/heuristics"
"openreplay/backend/pkg/handlers"
web2 "openreplay/backend/pkg/handlers/web"
"openreplay/backend/pkg/intervals"
logger "openreplay/backend/pkg/log"
"openreplay/backend/pkg/messages"
"openreplay/backend/pkg/queue"
"openreplay/backend/pkg/queue/types"
"openreplay/backend/pkg/sessions"
"os"
"os/signal"
"syscall"
"time"
// Import custom modules here
)
func main() {
log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile)
// Load service configuration
cfg := heuristics.New()
// HandlersFabric returns the list of message handlers we want to be applied to each incoming message.
handlersFabric := func() []handlers.MessageProcessor {
return []handlers.MessageProcessor{
// web handlers
&web2.ClickRageDetector{},
&web2.CpuIssueDetector{},
&web2.DeadClickDetector{},
&web2.MemoryIssueDetector{},
&web2.NetworkIssueDetector{},
&web2.PerformanceAggregator{},
// Add custom handlers here
}
}
...
}
Quick Return 示例
Section titled Quick Return 示例在这个示例中,我们将创建 Quick Return 启发式算法,每当我们在不到五秒内返回到同一个 url 时,它都会发出一个信号。用于检测此类事件的实现方法将利用 SetPageLocation 和 MouseClick 这两个事件。每当发生鼠标点击时,我们都会更新当前时间戳;如果在接下来的五秒内收到指向当前网页的 SetPageEvent 消息,那么我们就返回一个 QuickReturn 事件。
首先,让我们在路径 openreplay/backend/pkg/handlers/custom 下创建文件 quickreturn.go,其中包含如下所示的代码
package custom
import (
"log"
"encoding/json"
. "openreplay/backend/pkg/messages"
)
type QuickReturnDetector struct {
timestamp uint64
currentPage string
lastPage string
}
type CustomPayload struct {
Timestamp uint64 `json:"timestamp"`
CurrentPage string `json:"current_page"`
}
// If received SetPageLocation on same
func (h *QuickReturnDetector) HandleSetPageLocation(msg *SetPageLocation, messageID uint64, timestamp uint64) Message {
if (h.timestamp + 5000 >= msg.NavigationStart && h.lastPage == msg.URL) {
h.timestamp = msg.NavigationStart
return h.Build()
}
h.lastPage = h.currentPage
h.currentPage = msg.URL
h.timestamp = msg.NavigationStart
return nil
}
// detect when a button is clicked (selector must have string 'button' in it)
func (h *QuickReturnDetector) HandleMouseClick(msg *MouseClick, messageID uint64, timestamp uint64) {
h.timestamp = timestamp
}
func (h *QuickReturnDetector) Handle(message Message, messageID uint64, timestamp uint64) Message {
switch msg := message.(type) {
case *SetPageLocation:
if msg.NavigationStart != 0 {
return h.HandleSetPageLocation(msg, messageID, timestamp)
}
case *MouseClick:
h.HandleMouseClick(msg, messageID, timestamp)
}
return nil
}
func (h *QuickReturnDetector) Build() Message {
payload := &CustomPayload{
CurrentPage: h.currentPage,
Timestamp: h.timestamp,
}
payloadData, err := json.Marshal(payload)
if err != nil {
log.Println("JSON encoding error", err)
return nil
}
payloadString := string(payloadData)
event := &CustomEvent {
MessageID: 122,
Timestamp: h.timestamp,
Name: "quickreturn",
Payload: payloadString,
}
return event
}
现在启发式算法已经创建好了,只需要启用它即可。为此,我们必须按如下方式将该启发式算法添加到路径 openreplay/backend/cmd/heuristics 下的 main.go 文件中:
package main
import (
"log"
"openreplay/backend/internal/config/heuristics"
"openreplay/backend/pkg/handlers"
web2 "openreplay/backend/pkg/handlers/web"
"openreplay/backend/pkg/intervals"
logger "openreplay/backend/pkg/log"
"openreplay/backend/pkg/messages"
"openreplay/backend/pkg/queue"
"openreplay/backend/pkg/queue/types"
"openreplay/backend/pkg/sessions"
"os"
"os/signal"
"syscall"
"time"
// Add custom module
custom "openreplay/backend/pkg/custom"
)
func main() {
log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile)
// Load service configuration
cfg := heuristics.New()
// HandlersFabric returns the list of message handlers we want to be applied to each incoming message.
handlersFabric := func() []handlers.MessageProcessor {
return []handlers.MessageProcessor{
// web handlers
&web2.ClickRageDetector{},
&web2.CpuIssueDetector{},
&web2.DeadClickDetector{},
&web2.MemoryIssueDetector{},
&web2.NetworkIssueDetector{},
// The new handler
&custom.QuickReturnDetector{},
}
}
...
}
现在启发式算法已经可用,消息将作为 Custom Event 发送。 请注意,在能够使用新的启发式算法之前,你需要重新部署你的后端。为此,请按照 deploy from source 页面中 build and deploy 部分的步骤操作。
使用自定义事件
Section titled 使用自定义事件现在你的自定义启发式算法已经在发送一个新的自定义事件,接下来你要做的就是以某种方式使用这个事件。 对于前面的示例,你有两种选择:
- 你可以按自定义事件筛选会话,确保只列出存在该“问题”(在我们的例子中是快速返回问题)的会话。
- 你可以在播放器的 DevTools 部分的 Events 选项卡中找到该事件。这样你就可以精确定位事件被触发的确切时刻。
按自定义事件筛选会话
Section titled 按自定义事件筛选会话要按你的新自定义事件筛选会话,只需使用 Omnisearch 功能:点击搜索栏,并选择“Custom Events”作为筛选类型。 完成后,你就可以输入事件名称了;当你开始输入时,名称应该会自动补全,如下图所示:

请注意,如果自动补全不起作用,或者它没有返回你正在查找的事件名称,那么数据库中就不存在这样命名的事件。在这种情况下,你应该再次检查你的逻辑,以确保你正确地触发了自定义事件的发送。
在 DevTools 中查找自定义事件
Section titled 在 DevTools 中查找自定义事件如果你想准确了解自定义事件是何时被触发的,那么你需要重放一个会话,并点击 DevTools 的“Events”选项卡。 你会发现它位于屏幕的右下角。 点击后,它会打开,你将看到事件列表,其中包括你的自定义事件,如下图所示:

在这个简单的过程中,我们创建了一个新的检测器。当然,你的算法可以更加复杂,并包含不同类型事件的组合。