Nous développons l'interface la plus pratique au monde* pour visualiser les journaux

Nous développons l'interface la plus pratique au monde* pour visualiser les journaux Si vous avez déjà utilisé des interfaces Web pour afficher les journaux, vous avez probablement remarqué à quel point ces interfaces sont généralement lourdes et (souvent) peu pratiques et peu réactives. Certains auxquels vous pouvez vous habituer, d'autres sont absolument terribles, mais il me semble que la raison de tous les problèmes est que nous abordons mal la tâche de visualisation des journaux : nous essayons de créer une interface Web où la CLI (interface de ligne de commande) fonctionne mieux. Personnellement, je suis très à l'aise avec tail, grep, awk et autres, et donc pour moi, l'interface idéale pour travailler avec les journaux serait quelque chose de similaire à tail et grep, mais qui pourrait également être utilisée pour lire les journaux provenant de nombreux serveurs. C'est-à-dire, bien sûr, lisez-les sur ClickHouse !

*selon l'opinion personnelle de l'utilisateur habra tu gères

Rencontrez logscli

Je n'ai pas trouvé de nom pour mon interface, et, pour être honnête, elle existe plutôt sous forme de prototype, mais si vous souhaitez voir immédiatement le code source, alors vous êtes les bienvenus : https://github.com/YuriyNasretdinov/logscli (350 lignes de code Go sélectionné).

opportunités

Mon objectif était de créer une interface qui semble familière à ceux qui sont habitués à tail/grep, c'est-à-dire qu'elle prend en charge les éléments suivants :

  1. Affichez tous les journaux, sans filtrage.
  2. Laisser les lignes contenant une sous-chaîne fixe (drapeau -F у grep).
  3. Laissez les lignes qui correspondent à l'expression régulière (drapeau -E у grep).
  4. Par défaut, l'affichage s'effectue dans l'ordre chronologique inverse, puisque les journaux les plus récents sont généralement intéressants en premier.
  5. Afficher le contexte à côté de chaque ligne (options -A, -B и -C у grep, imprimant N lignes avant, après et autour de chaque ligne correspondante, respectivement).
  6. Visualisez les logs entrants en temps réel, avec ou sans filtrage (essentiellement tail -f | grep).
  7. L'interface doit être compatible avec less, head, tail et autres - par défaut, les résultats doivent être renvoyés sans restrictions sur leur nombre ; les lignes sont imprimées sous forme de flux tant que l'utilisateur souhaite les recevoir ; signal SIGPIPE devrait interrompre silencieusement la diffusion des journaux, tout comme ils le font tail, grep et d'autres utilitaires UNIX.

exécution

Je suppose que vous savez déjà comment transmettre des journaux à ClickHouse. Sinon, je recommande de l'essayer lsd и chatonet cet article sur la livraison des journaux.

Vous devez d'abord décider du schéma de base. Puisque vous souhaitez généralement recevoir les journaux triés par heure, il semble logique de les stocker de cette façon. S'il existe de nombreuses catégories de journaux et qu'elles sont toutes du même type, vous pouvez alors créer une catégorie de journaux comme première colonne de la clé primaire - cela vous permettra d'avoir une table au lieu de plusieurs, ce qui sera un gros plus lorsque insertion dans ClickHouse (sur les serveurs équipés de disques durs, il est recommandé d'insérer les données pas plus d'environ 1 fois par seconde pour l'ensemble du serveur).

Autrement dit, nous avons besoin approximativement du schéma de table suivant :

CREATE TABLE logs(
    category LowCardinality(String), -- категория логов (опционально)
    time DateTime, -- время события
    millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
    ..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
    message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

Malheureusement, je n'ai pas pu trouver immédiatement de sources ouvertes avec des journaux réalistes que je pourrais récupérer et télécharger, j'ai donc pris cela comme exemple. avis sur les produits d'Amazon avant 2015. Bien entendu, leur structure n’est pas exactement la même que celle des journaux textuels, mais à des fins d’illustration, cela n’a pas d’importance.

instructions pour télécharger des avis Amazon sur ClickHouse

Créons un tableau :

CREATE TABLE amazon(
   review_date Date,
   time DateTime DEFAULT toDateTime(toUInt32(review_date) * 86400 + rand() % 86400),
   millis UInt16 DEFAULT rand() % 1000,
   marketplace LowCardinality(String),
   customer_id Int64,
   review_id String,
   product_id LowCardinality(String),
   product_parent Int64,
   product_title String,
   product_category LowCardinality(String),
   star_rating UInt8,
   helpful_votes UInt32,
   total_votes UInt32,
   vine FixedString(1),
   verified_purchase FixedString(1),
   review_headline String,
   review_body String
)
ENGINE=MergeTree()
ORDER BY (time, millis)
SETTINGS index_granularity=8192

Dans l'ensemble de données Amazon, il n'y a qu'une date pour un examen, mais il n'y a pas d'heure exacte, remplissons donc ces données avec un randon.

Vous n'êtes pas obligé de télécharger tous les fichiers tsv et de vous limiter aux premiers ~ 10 à 20 afin d'obtenir un ensemble de données assez important qui ne rentre pas dans 16 Go de RAM. Pour télécharger des fichiers TSV, j'ai utilisé la commande suivante :

for i in *.tsv; do
    echo $i;
    tail -n +2 $i | pv |
    clickhouse-client --input_format_allow_errors_ratio 0.5 --query='INSERT INTO amazon(marketplace,customer_id,review_id,product_id,product_parent,product_title,product_category,star_rating,helpful_votes,total_votes,vine,verified_purchase,review_headline,review_body,review_date) FORMAT TabSeparated'
done

Sur un disque persistant standard (qui est un disque dur) dans Google Cloud d'une taille de 1000 Go (j'ai pris cette taille principalement pour que la vitesse soit un peu plus élevée, même si peut-être qu'un SSD de la taille requise aurait été moins cher), le téléchargement la vitesse était d'environ ~ 75 Mo/s sur 4 cœurs.

  • Je dois faire une réservation pour que je travaille chez Google, mais j'ai utilisé un compte personnel et cet article n'a rien à voir avec mon travail dans l'entreprise

Je produirai toutes les illustrations avec cet ensemble de données particulier, puisque c'est tout ce que j'avais sous la main.

Afficher la progression de l'analyse des données

Étant donné que dans ClickHouse, nous utiliserons une analyse complète sur une table avec des journaux et que cette opération peut prendre beaucoup de temps et ne produire aucun résultat pendant longtemps si peu de correspondances sont trouvées, il est conseillé de pouvoir afficher le progression de la requête jusqu'à ce que les premières lignes avec le résultat soient reçues. Pour ce faire, il existe un paramètre dans l'interface HTTP qui permet d'envoyer la progression dans les en-têtes HTTP : send_progress_in_http_headers=1. Malheureusement, la bibliothèque Go standard ne peut pas lire les en-têtes au fur et à mesure de leur réception, mais l'interface HTTP 1.0 (à ne pas confondre avec 1.1 !) est prise en charge par ClickHouse, vous pouvez donc ouvrir une connexion TCP brute à ClickHouse et l'y envoyer. GET /?query=... HTTP/1.0nn et recevez les en-têtes et le corps de la réponse sans aucun échappement ni cryptage, donc dans ce cas, nous n'avons même pas besoin d'utiliser la bibliothèque standard.

Journaux en streaming depuis ClickHouse

ClickHouse bénéficie d'une optimisation pour les requêtes avec ORDER BY depuis relativement longtemps (depuis 2019 ?), donc une requête comme

SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC

Il commencera immédiatement à renvoyer les lignes contenant la sous-chaîne "quelque chose" dans leur message, sans attendre la fin de l'analyse.

De plus, il serait très pratique que ClickHouse lui-même annule la demande lorsque la connexion à celle-ci est fermée, mais ce n'est pas le comportement par défaut. L'annulation automatique de la demande peut être activée à l'aide de l'option cancel_http_readonly_queries_on_client_close=1.

Manipulation correcte de SIGPIPE dans Go

Lorsque vous exécutez, disons, la commande some_cmd | head -n 10, exactement comment la commande some_cmd arrête l'exécution lorsque head soustrait 10 lignes ? La réponse est simple : quand head se termine, le tube se ferme et la sortie standard de la commande some_cmd commence à pointer, conditionnellement, « vers nulle part ». Quand some_cmd essaie d'écrire dans un tube fermé, il reçoit un signal SIGPIPE, qui termine silencieusement le programme par défaut.

Dans Go, cela se produit également par défaut, mais le gestionnaire de signal SIGPIPE affiche également "signal: SIGPIPE" ou un message similaire à la fin, et pour effacer ce message, il nous suffit de gérer SIGPIPE nous-mêmes comme nous le souhaitons, c'est-à-dire en silence. sortie:

ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
    <-ch
    os.Exit(0)
}()

Afficher le contexte du message

Souvent, vous souhaitez voir le contexte dans lequel une erreur s'est produite (par exemple, quelle requête a provoqué une panique ou quels problèmes associés étaient visibles avant le crash), et dans grep Cela se fait à l'aide des options -A, -B et -C, qui affichent respectivement le nombre spécifié de lignes après, avant et autour du message.

Malheureusement, je n'ai pas trouvé de moyen simple de faire la même chose dans ClickHouse, donc pour afficher le contexte, une requête supplémentaire comme celle-ci est envoyée à chaque ligne du résultat (les détails dépendent du tri et si le contexte est affiché avant ou après):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1

Comme la requête est envoyée presque immédiatement après que ClickHouse renvoie la ligne correspondante, elle se retrouve dans le cache et en général la requête est exécutée assez rapidement et consomme un peu de CPU (généralement la requête prend environ ~6 ms sur ma machine virtuelle).

Afficher les nouveaux messages en temps réel

Afin d'afficher les messages entrants en temps (presque) réel, nous exécutons simplement la requête toutes les quelques secondes, en nous souvenant du dernier horodatage que nous avons rencontré auparavant.

Exemples de commandes

À quoi ressemblent les commandes logscli typiques dans la pratique ?

Si vous avez téléchargé l'ensemble de données Amazon que j'ai mentionné au début de l'article, vous pouvez exécuter les commandes suivantes :

# Показать строки, где встречается слово walmart
$ logscli -F 'walmart' | less

# Показать самые свежие 10 строк, где встречается "terrible"
$ logscli -F terrible -limit 10

# То же самое без -limit:
$ logscli -F terrible | head -n 10

# Показать все строки, подходящие под /times [0-9]/, написанные для vine и у которых высокий рейтинг
$ logscli -E 'times [0-9]' -where="vine='Y' AND star_rating>4" | less

# Показать все строки со словом "panic" и 3 строки контекста вокруг
$ logscli -F 'panic' -C 3 | less

# Непрерывно показывать новые строки со словом "5-star"
$ logscli -F '5-star' -tailf

références

Le code de l'utilitaire (sans documentation) est disponible sur github à l'adresse https://github.com/YuriyNasretdinov/logscli. Je serais heureux d'entendre vos réflexions sur mon idée d'une interface de console pour afficher les journaux basés sur ClickHouse.

Source: habr.com

Ajouter un commentaire