Heurísticas personalizadas
Usamos el término heurísticas para referirnos a algoritmos sencillos y robustos empleados para detectar problemas como un uso elevado de CPU o el click rage. Aquí usaremos un ejemplo para entender cómo funcionan las heurísticas, cómo crear tu propio detector y ver sus resultados en las DevTools.
Plantillas
Section titled PlantillasUna plantilla para las heurísticas se encuentra en la ruta 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
}
Cuando se ha creado el método de la heurística, se puede añadir modificando el archivo main.go en la ruta
openreplay/backend/cmd/heuristics, importando el módulo personalizado y agregando la función personalizada al handler del procesador de mensajes.
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
}
}
...
}
Ejemplo de Quick Return
Section titled Ejemplo de Quick ReturnPara este ejemplo, crearemos la heurística Quick Return, que enviará una señal cada vez que volvamos a la misma url en menos de cinco segundos. El método implementado para detectar este evento aprovechará los eventos SetPageLocation y MouseClick. Cada vez que se produce un clic del ratón, actualizamos la marca de tiempo actual y, si se recibe un mensaje SetPageEvent dentro de los cinco segundos siguientes apuntando a la página web actual, entonces devolvemos un evento QuickReturn.
Para empezar, vamos a crear el archivo quickreturn.go en la ruta openreplay/backend/pkg/handlers/custom con el código que se muestra a continuación
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
}
Ahora que la heurística está creada, solo queda habilitarla. Para ello, tenemos que añadir la heurística al archivo main.go en la ruta openreplay/backend/cmd/heuristics de la siguiente manera:
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{},
}
}
...
}
La heurística ya está disponible y los mensajes se envían como un Custom Event. Ten en cuenta que tendrás que volver a desplegar tu back-end antes de poder aprovechar la nueva heurística. Para ello, sigue los pasos de la sección build and deploy de la página deploy from source.
Cómo aprovechar el evento personalizado
Section titled Cómo aprovechar el evento personalizadoAhora que tu heurística personalizada envía un nuevo evento personalizado, lo siguiente que tienes que hacer es darle algún uso a ese evento. Con el ejemplo anterior, tienes dos opciones:
- Puedes filtrar las sesiones por evento personalizado, asegurándote de que solo se listan las que tienen ese “problema” (en nuestro caso, el problema del quick return).
- Puedes encontrar el evento dentro de la pestaña Events de la sección DevTools del reproductor. De esta forma puedes identificar el momento exacto en el que se dispara el evento.
Filtrar sesiones por evento personalizado
Section titled Filtrar sesiones por evento personalizadoPara filtrar las sesiones por tu nuevo evento personalizado, simplemente utiliza la función Omnisearch, haciendo clic en la barra de búsqueda y seleccionando “Custom Events” como tipo de filtro. Una vez hecho esto, podrás introducir el nombre del evento; debería autocompletarse el nombre en cuanto empieces a escribir, como puedes ver en la siguiente imagen:

Ten en cuenta que si el autocompletado no funciona o no devuelve el nombre del evento que buscas, entonces no existe ningún evento con ese nombre en la base de datos. En ese caso, deberías revisar tu lógica para asegurarte de que estás disparando correctamente el envío del evento personalizado.
Encontrar el evento personalizado en las DevTools
Section titled Encontrar el evento personalizado en las DevToolsSi quieres entender exactamente cuándo se dispara el evento personalizado, tendrás que reproducir una sesión y hacer clic en la pestaña “Events” de las DevTools. La encontrarás situada en la esquina inferior derecha de la pantalla. Una vez que hagas clic, se abrirá y verás la lista de eventos, incluido el tuyo personalizado, como se ve en la siguiente imagen:

En este sencillo proceso hemos creado un nuevo detector. Por supuesto, tu algoritmo puede ser más sofisticado e incluir una combinación de diferentes tipos de eventos.
¿Tienes preguntas?
Section titled ¿Tienes preguntas?Si encuentras algún problema, conéctate a nuestro Slack o consulta nuestro Foro y obtén ayuda de nuestra comunidad.