Maison >développement back-end >Tutoriel Python >Surveillance avancée d'Open edX avec AppSignal pour Python

Surveillance avancée d'Open edX avec AppSignal pour Python

Susan Sarandon
Susan Sarandonoriginal
2024-11-29 19:54:12946parcourir

Dans la première partie de cette série, nous avons exploré comment AppSignal peut améliorer considérablement la robustesse des plates-formes Open edX. Nous avons vu les défis auxquels Open edX est confronté à mesure de son évolution et comment les fonctionnalités d'AppSignal, notamment la surveillance des performances en temps réel et le suivi automatisé des erreurs, fournissent des outils essentiels aux équipes DevOps. Notre présentation pas à pas a couvert la configuration initiale et l'intégration d'AppSignal avec Open edX, soulignant les avantages immédiats de ce puissant cadre d'observabilité.

Dans ce deuxième article, nous approfondirons les capacités de surveillance avancées offertes par AppSignal. Cela inclut la diffusion des journaux d'Open edX vers AppSignal, la surveillance des travailleurs en arrière-plan avec Celery et le suivi des requêtes Redis. Nous démontrerons comment ces fonctionnalités peuvent être exploitées pour relever des défis opérationnels spécifiques, en garantissant que notre plateforme d'apprentissage reste sécurisée dans diverses circonstances.

À la fin de cet article, vous saurez comment utiliser AppSignal à son plein potentiel pour maintenir et améliorer les performances et la fiabilité de votre plateforme Open edX.

Streaming des journaux vers AppSignal

L'une des fonctionnalités les plus puissantes d'AppSignal est la gestion centralisée des journaux.

Généralement chez Open edX, l'équipe d'assistance signale un problème avec le site et un ingénieur peut immédiatement se connecter en SSH au serveur pour vérifier les journaux des applications Nginx, Mongo, MySQL et Open edX.

Un espace de stockage centralisé qui héberge les journaux sans que vous ayez besoin de vous connecter en SSH au serveur est une fonctionnalité vraiment puissante. Nous pouvons également configurer des notifications en fonction de la gravité d'un problème.

Voyons maintenant comment nous pouvons diffuser nos journaux d'Open edX vers AppSignal.

Créer une source

Sous la section Logging, cliquez sur Gérer les sources et créez une nouvelle source, avec HTTP comme plateforme et JSON comme le format. Après avoir créé la source, AppSignal fournit un point de terminaison et une CLÉ API sur lesquels nous pouvons POST nos journaux.

Pour avoir plus de contrôle sur la transmission des journaux, nous pouvons écrire un simple script Python qui lit les journaux de notre Open edX local, les prétraite et déplace les plus importants vers AppSignal. Par exemple, j'ai écrit le script suivant pour déplacer uniquement les journaux d'ERREUR vers AppSignal (en ignorant les journaux INFO et AVERTISSEMENT) :

import requests
import json
from datetime import datetime
import logging

# Setup logging configuration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# File to keep track of the last processed line
log_pointer_file = '/root/.local/share/tutor/data/lms/logs/processed.log'
log_file = '/root/.local/share/tutor/data/lms/logs/all.log'

# APpSignal API KEY
api_key = "MY-API-KEY"  # Replace with your actual API key
# URL to post the logs
url = f'https://appsignal-endpoint.net/logs?api_key={api_key}'

def read_last_processed():
    try:
        with open(log_pointer_file, 'r') as file:
            content = file.read().strip()
            last_processed = int(content) if content else 0
            logging.info(f"Last processed line number read: {last_processed}")
            return last_processed
    except (FileNotFoundError, ValueError) as e:
        logging.error(f"Could not read from log pointer file: {e}")
        return 0

def update_last_processed(line_number):
    try:
        with open(log_pointer_file, 'w') as file:
            file.write(str(line_number))
            logging.info(f"Updated last processed to line number: {line_number}")
    except Exception as e:
        logging.error(f"Could not update log pointer file: {e}")

def parse_log_line(line):
    if 'ERROR' in line:
        parts = line.split('ERROR', 1)
        timestamp = parts[0].strip()
        message_parts = parts[1].strip().split(' - ', 1)
        message = message_parts[1] if len(message_parts) > 1 else ''
        attributes_part = message_parts[0].strip('[]').split('] [')
        # Flatten attributes into a dictionary with string keys and values
        attributes = {}
        for attr in attributes_part:
            key_value = attr.split(None, 1)
            if len(key_value) == 2:
                key, value = key_value
                key = key.rstrip(']:').replace(' ', '_').replace('.', '_')  # Replace spaces and dots in keys
                if len(key)  last_processed:
                json_data = parse_log_line(line)
                if json_data:
                    response_code = post_logs(json_data)
                    if response_code == 200:
                        update_last_processed(i)
                    else:
                        logging.warning(f"Failed to post log, HTTP status code: {response_code}")

if __name__ == '__main__':
    logging.info("Starting log processing script.")
    process_logs()
    logging.info("Finished log processing.")

Voici comment fonctionne le script :

  1. Gestion des fichiers journaux : Tutor enregistre tous les journaux dans le fichier /root/.local/share/tutor/data/lms/logs/all.log. Ce fichier contient MySQL, LMS, CMS, Caddy, Celery et d'autres services. Le script utilise un fichier pointeur /root/.local/share/tutor/data/lms/logs/processed.log qui suit la dernière ligne traitée. Cela garantit que chaque journal n'est traité qu'une seule fois.
  2. Filtrage des erreurs : comme mentionné, nous envoyons uniquement les journaux d'ERREURS à AppSignal.
  3. Analyse et formatage des données : chaque journal d'erreurs est analysé pour extraire des informations clés, telles que l'horodatage et le message d'erreur. Le script formate ces données dans une structure JSON adaptée à la transmission.
  4. Transmission du journal : les données du journal formatées sont envoyées à AppSignal à l'aide d'une requête HTTP POST.

Important : assurez-vous de ne pas envoyer d'informations personnelles identifiables au point de terminaison.

Maintenant, exécutez ce script et il devrait déplacer les journaux d'ERREURS vers AppSignal :

Advanced Open edX Monitoring with AppSignal for Python

Vous pouvez également créer un nouveau déclencheur pour vous avertir dès qu'un événement spécifique comme ERREUR se produit :

Advanced Open edX Monitoring with AppSignal for Python

Surveiller le céleri et Redis à l'aide d'AppSignal

Celery (une file d'attente de tâches distribuée) est un composant essentiel d'Open edX, responsable de la gestion des tâches en arrière-plan telles que la notation, la génération de certificats et l'envoi groupé d'e-mails. Redis agit souvent en tant que courtier pour Celery, gérant les files d'attente de tâches. Les deux systèmes sont essentiels au traitement asynchrone et peuvent devenir des goulots d'étranglement pendant les périodes de forte utilisation. La surveillance de ces services avec AppSignal fournit des informations précieuses sur l'exécution des tâches et l'état de la file d'attente, vous aidant ainsi à résoudre de manière préventive les problèmes potentiels. Voyons comment nous pouvons surveiller Celery et Redis.

Tout d’abord, installez les packages nécessaires. Ajoutez ce qui suit à la variable OPENEDX_EXTRA_PIP_REQUIREMENTS dans le fichier .local/share/tutor/config.yml :

import requests
import json
from datetime import datetime
import logging

# Setup logging configuration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# File to keep track of the last processed line
log_pointer_file = '/root/.local/share/tutor/data/lms/logs/processed.log'
log_file = '/root/.local/share/tutor/data/lms/logs/all.log'

# APpSignal API KEY
api_key = "MY-API-KEY"  # Replace with your actual API key
# URL to post the logs
url = f'https://appsignal-endpoint.net/logs?api_key={api_key}'

def read_last_processed():
    try:
        with open(log_pointer_file, 'r') as file:
            content = file.read().strip()
            last_processed = int(content) if content else 0
            logging.info(f"Last processed line number read: {last_processed}")
            return last_processed
    except (FileNotFoundError, ValueError) as e:
        logging.error(f"Could not read from log pointer file: {e}")
        return 0

def update_last_processed(line_number):
    try:
        with open(log_pointer_file, 'w') as file:
            file.write(str(line_number))
            logging.info(f"Updated last processed to line number: {line_number}")
    except Exception as e:
        logging.error(f"Could not update log pointer file: {e}")

def parse_log_line(line):
    if 'ERROR' in line:
        parts = line.split('ERROR', 1)
        timestamp = parts[0].strip()
        message_parts = parts[1].strip().split(' - ', 1)
        message = message_parts[1] if len(message_parts) > 1 else ''
        attributes_part = message_parts[0].strip('[]').split('] [')
        # Flatten attributes into a dictionary with string keys and values
        attributes = {}
        for attr in attributes_part:
            key_value = attr.split(None, 1)
            if len(key_value) == 2:
                key, value = key_value
                key = key.rstrip(']:').replace(' ', '_').replace('.', '_')  # Replace spaces and dots in keys
                if len(key)  last_processed:
                json_data = parse_log_line(line)
                if json_data:
                    response_code = post_logs(json_data)
                    if response_code == 200:
                        update_last_processed(i)
                    else:
                        logging.warning(f"Failed to post log, HTTP status code: {response_code}")

if __name__ == '__main__':
    logging.info("Starting log processing script.")
    process_logs()
    logging.info("Finished log processing.")

Cela devrait ressembler à ceci :

- opentelemetry-instrumentation-celery==0.45b0
- opentelemetry-instrumentation-redis==0.45b0

Comme vous pouvez le voir, nous installons des packages opentelemetry pour Celery et Redis.

Maintenant, nous pouvons instrumenter Celery avec work_process_init pour signaler ses métriques à AppSignal.

Advanced Open edX Monitoring with AppSignal for Python

En revenant à notre tableau de bord dans AppSignal, nous devrions voir les rapports Celery et Redis dans la section Performance, avec arrière-plan comme espace de noms.

Advanced Open edX Monitoring with AppSignal for Python

Pour les requêtes Redis, vous pouvez cliquer sur Requêtes lentes :

Advanced Open edX Monitoring with AppSignal for Python

Surveillance pratique : améliorer Open edX avec AppSignal

Dans cette section, nous reviendrons sur les problèmes initiaux décrits dans la première partie de cette série et appliquerons des solutions pratiques de surveillance AppSignal pour garantir que notre plate-forme Open edX reste robuste et fiable. Voici une répartition.

Amélioration des performances du site

Commençons par évaluer les performances globales du site. Dans la section Performances, sous la Liste des problèmes, nous pouvons voir les statistiques clés pour toutes les URL visitées :

  • Temps de réponse : reflète directement l'expérience utilisateur en mesurant le temps nécessaire pour traiter et répondre aux demandes. Les facteurs qui influencent cela incluent les requêtes de base de données et les opérations middleware.
  • Débit : Indique le nombre de demandes traitées dans un délai donné.
  • Mean Response Time : fournit un temps de réponse moyen pour toutes les requêtes adressées à un point de terminaison spécifique. Tout temps de réponse moyen supérieur à 1 seconde est une préoccupation potentielle et met en évidence les domaines qui nécessitent une optimisation.
  • Temps de réponse au 90e centile : par exemple, un temps de réponse au 90e centile de 7 ms pour GET store/ suggère que 90 % des requêtes se terminent en 7 ms ou moins.

Ordonnons maintenant toutes les actions en fonction de la moyenne. Tout élément supérieur à 1 seconde doit être considéré comme un signal d'alarme :

Advanced Open edX Monitoring with AppSignal for Python
Advanced Open edX Monitoring with AppSignal for Python

Comme nous le voyons, les tâches Celery pour rescorer et réinitialiser les tentatives des étudiants, les requêtes LMS pour afficher le contenu du cours et certaines API prennent plus d'une seconde. Nous devons également noter que cela ne concerne qu’un seul utilisateur actif. Si nous avons plus d’utilisateurs simultanés, ce temps de réponse augmentera. Notre première solution consiste à ajouter plus de ressources au serveur (CPU et mémoire) et à effectuer un autre test de performances.

Après avoir identifié les actions dont les temps de réponse moyens dépassent 1 seconde, envisagez des stratégies d'optimisation des performances telles que :

  • Minimiser l'exécution de JavaScript
  • Utiliser des CDN pour le contenu statique
  • Mise en œuvre de techniques de mise en cache.

Surveillance des ressources du serveur

Nous avons parlé de la détection des anomalies et de la surveillance des hôtes dans l'article précédent. Ajoutons des déclencheurs pour les éléments suivants :

  • Utilisation du processeur
  • Utilisation du disque
  • Utilisation de la mémoire
  • Trafic réseau
  • Taux d'erreur

Métriques personnalisées

Deux indicateurs vraiment importants pour notre plateforme sont notre nombre d'utilisateurs actifs et nos inscriptions. Voyons comment nous pouvons mesurer ces métriques à l'aide d'AppSignal.

Tout d'abord, ajoutez Increase_counter à common/djangoapps/student/views/management.py et openx/core/djangoapps/user_authn/views/login.py pour suivre et incrémenter le nombre de connexions et d'inscriptions lorsqu'il y a un nouvel événement.

Advanced Open edX Monitoring with AppSignal for Python

Advanced Open edX Monitoring with AppSignal for Python

Connectons-nous maintenant à Open edX et inscrivons-nous à un cours. Passons ensuite à notre tableau de bord dans AppSignal. Cliquez sur Ajouter un tableau de bord, puis sur Créer un tableau de bord, et donnez-lui un nom et une description.

Cliquez sur Ajouter un graphique, saisissez Utilisateurs actifs comme titre, sélectionnez Ajouter une métrique et utilisez login_count :

Advanced Open edX Monitoring with AppSignal for Python

Votre tableau de bord devrait ressembler à ceci :

Advanced Open edX Monitoring with AppSignal for Python

Vous pouvez suivre les mêmes étapes pour ajouter un graphique pour les inscriptions à l'aide d'une métrique enrollment_count.

Assurer un style cohérent

Pour nous assurer que le style de notre site reste cohérent, ajoutons une nouvelle vérification de disponibilité pour static/tailwind/css/lms-main-v1.css et soyez averti lorsqu'une URL est cassée :

Advanced Open edX Monitoring with AppSignal for Python

Advanced Open edX Monitoring with AppSignal for Python

Envoi des e-mails et gestion des erreurs

Dans la section Erreur du tableau de bord, nous pouvons afficher toutes les erreurs, configurer des notifications pour celles-ci et travailler sur des correctifs dès que possible pour éviter que les utilisateurs ne soient affectés négativement.

Efficacité du travail en arrière-plan pour le classement

Dans la section Surveiller Celery et Redis de cet article, nous avons vu comment instrumenter Celery et Redis à l'aide d'AppSignal. Suivons les mêmes étapes pour activer AppSignal afin que nous puissions voir les tâches notées. Dans le fichier lms/djangoapps/grades/tasks.py, ajoutez les lignes suivantes :

Advanced Open edX Monitoring with AppSignal for Python

Nous devrions maintenant voir quelques éléments à noter sous Performance -> Liste des problèmes.

Surveillance avancée dOpen edX avec AppSignal pour Python

Comme vous pouvez le voir, recalculate_subsection_grade_v3 (notre tâche principale de notation du céleri) prend 212 millisecondes. Pour la reclassement, lms.djangoapps.instructor_task.tasks.reset_problem_attempts et lms.djangoapps.instructor_task.tasks.rescore_problem prennent 1,77 seconde.

Conclusion

Dans cette série en deux parties, nous avons intégré AppSignal à Open edX pour renforcer ses capacités de surveillance. Nous avons commencé par les bases : configurer et comprendre les offres fondamentales d'AppSignal, y compris le suivi des erreurs et la surveillance des performances.

Dans cet article, nous avons expliqué comment diffuser efficacement les journaux de divers services Open edX vers AppSignal, en garantissant que toutes les informations pertinentes étaient centralisées et facilement accessibles. Nous avons également surveillé les tâches asynchrones cruciales gérées par Celery et Redis.

Enfin, nous avons résolu certains défis du monde réel, tels que les réponses lentes du site, les goulots d'étranglement des ressources pendant les périodes d'inscription élevées et les problèmes inattendus comme un style cassé.

À présent, vous devriez avoir une compréhension complète de la façon d'exploiter AppSignal non seulement pour surveiller, mais également pour améliorer considérablement les performances et la fiabilité de votre plate-forme Open edX.

Si vous avez des questions sur Open edX ou si vous avez besoin d'aide supplémentaire, n'hésitez pas à visiter cubite.io ou à me contacter directement à amir@cubite.io.

P.S. Si vous souhaitez lire les articles Python dès leur sortie de presse, abonnez-vous à notre newsletter Python Wizardry et ne manquez jamais un seul article !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn