Heuristiques personnalisées

Comment étendre le service d'heuristiques avec votre propre algorithme

Heuristiques personnalisées

Nous utilisons le terme heuristiques pour désigner des algorithmes simples et robustes servant à détecter des problèmes tels qu’une utilisation élevée du CPU ou le click rage. Nous allons ici prendre un exemple pour comprendre comment fonctionnent les heuristiques, comment créer votre propre détecteur et voir ses résultats dans les DevTools.

Un modèle pour les heuristiques se trouve dans le chemin 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
}

Une fois la méthode de l’heuristique créée, elle peut être ajoutée en modifiant le fichier main.go dans le chemin openreplay/backend/cmd/heuristics, en important le module personnalisé et en ajoutant la fonction personnalisée au handler du processeur de messages.

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
                }
        }
        ...
}

Pour cet exemple, nous allons créer l’heuristique Quick Return, qui enverra un signal chaque fois que nous revenons à la même url en moins de cinq secondes. La méthode mise en œuvre pour détecter un tel événement tirera parti des événements SetPageLocation et MouseClick. Chaque fois que survient un clic de souris, nous mettons à jour l’horodatage actuel et, si un message SetPageEvent est reçu dans les cinq secondes suivantes en pointant vers la page web actuelle, alors nous renvoyons un événement QuickReturn.

Pour commencer, créons le fichier quickreturn.go dans le chemin openreplay/backend/pkg/handlers/custom contenant le code présenté ci-dessous

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
}

Maintenant que l’heuristique est créée, il ne reste plus qu’à l’activer. Pour ce faire, nous devons ajouter l’heuristique au fichier main.go dans le chemin openreplay/backend/cmd/heuristics comme suit :

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{},
                }
        }
        ...
}

L’heuristique est désormais disponible et les messages sont envoyés sous forme de Custom Event. Notez que vous devrez redéployer votre back-end avant de pouvoir tirer parti de la nouvelle heuristique. Pour ce faire, suivez les étapes de la section build and deploy de la page deploy from source.

Utiliser l’événement personnalisé

Section titled Utiliser l’événement personnalisé

Maintenant que votre heuristique personnalisée envoie un nouvel événement personnalisé, la prochaine chose à faire est d’utiliser cet événement d’une manière ou d’une autre. Avec l’exemple précédent, vous avez deux options :

  1. Vous pouvez filtrer les sessions par événement personnalisé, en veillant à ne lister que celles qui présentent ce « problème » (dans notre cas, le problème du quick return).
  2. Vous pouvez trouver l’événement dans l’onglet Events de la section DevTools du lecteur. Ainsi, vous pouvez identifier le moment exact où l’événement est déclenché.

Filtrer les sessions par événement personnalisé

Section titled Filtrer les sessions par événement personnalisé

Pour filtrer les sessions par votre nouvel événement personnalisé, utilisez simplement la fonctionnalité Omnisearch, en cliquant sur la barre de recherche et en sélectionnant « Custom Events » comme type de filtre. Une fois cela fait, vous pourrez saisir le nom de l’événement ; il devrait s’autocompléter dès que vous commencez à taper, comme vous pouvez le voir dans l’image ci-dessous :

Custom event filter

Notez que si l’autocomplétion ne fonctionne pas ou ne renvoie pas le nom de l’événement que vous recherchez, alors il n’existe aucun événement portant ce nom dans la base de données. Dans ce cas, vous devriez revérifier votre logique pour vous assurer que vous déclenchez correctement l’envoi de l’événement personnalisé.

Trouver l’événement personnalisé dans les DevTools

Section titled Trouver l’événement personnalisé dans les DevTools

Si vous voulez comprendre exactement quand l’événement personnalisé est déclenché, vous devrez rejouer une session et cliquer sur l’onglet « Events » des DevTools. Vous le trouverez situé dans le coin inférieur droit de l’écran. Une fois cliqué, il s’ouvrira et vous verrez la liste des événements, y compris le vôtre personnalisé, comme on le voit dans l’image ci-dessous :

Events listed in devTools

Dans ce processus simple, nous avons créé un nouveau détecteur. Bien entendu, votre algorithme peut être plus sophistiqué et inclure une combinaison de différents types d’événements.

Si vous rencontrez des problèmes, connectez-vous à notre Slack ou consultez notre Forum et obtenez de l’aide de notre communauté.