Flink SQL s'intègre au langage de traitement Simple Log Service (SPL) pour analyser les journaux semi-structurés en champs structurés, sans créer de logstores intermédiaires ni de tables temporaires.
Contexte
Simple Log Service (SLS) est une plateforme d'observabilité et d'analyse cloud native qui fournit des services à grande échelle, peu coûteux et en temps réel pour les journaux, les métriques et les traces. Realtime Compute for Apache Flink est une plateforme d'analyse de big data construite par Alibaba Cloud sur la base d'Apache Flink, largement utilisée pour l'analyse de données en temps réel et la surveillance des risques. Realtime Compute for Apache Flink prend nativement en charge le connecteur SLS, ce qui vous permet d'utiliser SLS comme table source ou table de résultats.
Le connecteur SLS pour Realtime Compute for Apache Flink gère directement les journaux structurés en mappant les champs de journal un à un avec les colonnes de table Flink SQL. Cependant, de nombreux journaux métier ne sont pas entièrement structurés. Par exemple, tout le contenu du journal peut être écrit dans un seul champ, nécessitant des expressions régulières ou un fractionnement par délimiteur pour extraire des champs structurés. Avec SPL dans le connecteur SLS, vous pouvez structurer ces données grâce au nettoyage des journaux et à la normalisation du format.
Données de journal semi-structurées
L'exemple de journal suivant présente un format complexe qui mélange des chaînes JSON avec d'autres types de données. Le journal contient :
Le champ Payload, qui est une chaîne JSON où le champ schedule imbriqué est également une structure JSON.
Le champ requestURL, qui est un chemin d'URL standard.
Le champ error, qui commence par la chaîne
CouldNotExecuteQueryet est suivi d'une structure JSON.Le champ __tag__:__path__, qui contient le chemin du fichier journal, où
service_apourrait être le nom du service.Le champ caller, qui contient le nom du fichier et le numéro de ligne.
{
"Payload": "{\"lastNotified\": 1705030483, \"serverUri\": \"http://test.alert.com/alert-api/tasks\", \"jobID\": \"44d6ce47bb4995ef0c8052a9a30ed6d8\", \"alertName\": \"alert-12345678-123456\", \"project\": \"test-sls-project\", \"projectId\": 123, \"aliuid\": \"1234567890\", \"alertDisplayName\": \"\\u6d4b\\u8bd5\\u963f\\u91cc\\u4e91\\u544a\\u8b66\", \"checkJobUri\": \"http://test.alert.com/alert-api/task_check\", \"schedule\": {\"timeZone\": \"\", \"delay\": 0, \"runImmediately\": false, \"type\": \"FixedRate\", \"interval\": \"1m\"}, \"jobRunID\": \"bf86aa5e67a6891d-61016da98c79b-5071a6b\", \"firedNotNotified\": 25161}",
"TaskID": "bf86aa5e67a6891d-61016da98c79b-5071a6b-334f81a-5c38aaa1-9354-43ec-8369-4f41a7c23887",
"TaskType": "ALERT",
"__source__": "11.199.XXX.XXX",
"__tag__:__hostname__": "iabcde12345.cloud.abc121",
"__tag__:__path__": "/var/log/service_a.LOG",
"caller": "executor/pool.go:64",
"error": "CouldNotExecuteQuery : {\n \"httpCode\": 404,\n \"errorCode\": \"LogStoreNotExist\",\n \"errorMessage\": \"logstore k8s-event does not exist\",\n \"requestID\": \"65B7C10AB43D9895A8C3DB6A\"\n}",
"requestURL": "/apis/autoscaling/v2beta1/namespaces/python-etl/horizontalpodautoscalers/cn-shenzhen-56492-1234567890123?timeout=30s",
"ts": "2024-01-29 22:57:13"
}
Exigences de structuration des données
Pour extraire des informations précieuses de ces journaux, transformez les données en extrayant les champs clés pour l'analyse.
À partir du champ error, extrayez httpCode, errorCode, errorMessage et requestID.
À partir du champ __tag__:__path_, extrayez _service_a en tant que serviceName.
À partir du champ caller, extrayez pool.go en tant que fileName et 64 en tant que fileNo.
À partir du champ Payload, extrayez project. À partir de l'objet schedule imbriqué dans Payload, extrayez type et nommez-le scheduleType.
Renommez le champ __source__ en serviceIP.
Tous les autres champs sont ignorés. La liste finale des champs requis est : httpCode, errorCode, errorMessage, requestID, serviceName, fileName, fileNo, project, scheduleType et serviceIP.
Solutions
Plusieurs méthodes permettent de transformer les données. Les solutions suivantes utilisent SLS et Flink, chacune étant adaptée à différents scénarios.
Solution de transformation des données : Dans la console SLS, créez une tâche de transformation des données pour nettoyer les données et les stocker dans un logstore cible.
Solution Flink : Définissez
erroretpayloadcomme champs dans la table source. Utilisez les fonctions d'expression régulière et JSON de Flink SQL pour analyser ces champs, écrivez les résultats dans une table temporaire, puis effectuez l'analyse sur cette table.Solution SPL : Configurez une instruction SPL dans le connecteur Flink SLS pour transformer les données. La table source Flink est ensuite définie avec le schéma final et structuré.
L'approche SPL est plus légère : elle évite de créer un logstore intermédiaire (requis par la solution de transformation des données) et une table temporaire dans Flink (requise par la solution Flink). En effectuant la transformation des données plus près de la source, vous pouvez vous concentrer sur la logique métier dans la plateforme de calcul, créant ainsi une séparation plus claire des responsabilités.
Utiliser SPL dans Flink
1. Préparer les données dans SLS
Assurez-vous d'avoir activé Simple Log Service et créé un projet et un logstore.
-
Écrivez l'extrait de journal précédent dans votre logstore cible à l'aide d'un SDK pour simuler des exemples de données.
Une fois les données écrites, accédez à l'onglet Raw Logs dans la console Simple Log Service pour afficher les données simulées. Le contenu du journal inclut des champs tels que TaskType (avec la valeur
ALERT), Payload (qui contientalertName,projectetaliuid), et des informations d'erreur (code d'erreurLogStoreNotExist, messagelogstore k8s-event does not existet statut HTTP 404). -
Dans le logstore, écrivez une instruction de pipeline SLS SPL et prévisualisez les résultats.
Après avoir exécuté l'instruction SPL, l'onglet Raw Logs affiche les enregistrements de journal analysés, qui incluent des champs structurés tels que
errorCode,errorMessage,fileName,fileNo,httpCode,project,requestID,scheduleTypeetserviceHost.L'instruction de requête SPL est la suivante. La syntaxe du pipeline SPL utilise le délimiteur pipe (|) pour séparer les commandes. Vous pouvez saisir une commande à la fois pour voir le résultat immédiat, puis ajouter d'autres pipes pour construire itérativement la requête finale. Pour plus d'informations, consultez Syntaxe de requête d'analyse.
* | project Payload, error, "__tag__:__path__", "__tag__:__hostname__", caller | parse-json Payload | project-away Payload | parse-regexp error, 'CouldNotExecuteQuery : ({[\w":\s,\-}]+)' as errorJson | parse-json errorJson | parse-regexp "__tag__:__path__", '\/var\/log\/([\w\_]+).LOG' as serviceName | parse-regexp caller, '\w+/([\w\.]+):(\d+)' as fileName, fileNo | project-rename serviceHost="__tag__:__hostname__" | extend scheduleType = json_extract_scalar(schedule, '$.type') | project httpCode, errorCode,errorMessage,requestID,fileName, fileNo, serviceHost,scheduleType, projectExplication de la syntaxe :
Ligne 1 : La commande project conserve les champs Payload, error, __tag__:__path__ et caller pour l'analyse et ignore tous les autres champs.
Ligne 2 : La commande parse-json développe la chaîne Payload en un objet JSON. Ses champs de niveau supérieur, tels que lastNotified, serviceUri et jobID, sont ajoutés au résultat.
Ligne 3 : La commande project-away supprime le champ Payload d'origine.
Ligne 4 : La commande parse-regexp utilise une expression régulière pour extraire la partie JSON du champ error et l'assigne à un nouveau champ nommé errorJson.
Ligne 5 : La commande parse-json développe le champ errorJson, en extrayant httpCode, errorCode et errorMessage.
Ligne 6 : La commande parse-regexp utilise une expression régulière pour extraire le nom de fichier de __tag__:__path__ et le nomme serviceName.
Ligne 7 : La commande parse-regexp utilise une expression régulière pour extraire le nom de fichier et le numéro de ligne du champ caller et les assigne aux champs fileName et fileNo.
Ligne 8 : La commande project-rename renomme le champ __tag__:__hostname__ en serviceHost.
Ligne 9 : La commande extend utilise la fonction
json_extract_scalarpour extraire le champtypede l'objet schedule et le nomme scheduleType.Ligne 10 : La commande project ne conserve que les champs finaux requis, y compris le champ project qui a été extrait de Payload.
2. Créer une tâche SQL
Connectez-vous à la console Realtime Compute for Apache Flink et cliquez sur l'espace de travail cible.
Dans le volet de navigation de gauche, choisissez .
Cliquez sur Create. Dans la boîte de dialogue New Draft, choisissez , puis cliquez sur Next.
-
Dans l'éditeur de brouillon, saisissez l'instruction suivante pour créer une table temporaire.
CREATE TEMPORARY TABLE sls_input_complex ( errorCode STRING, errorMessage STRING, fileName STRING, fileNo STRING, httpCode STRING, requestID STRING, scheduleType STRING, serviceHost STRING, project STRING, proctime as PROCTIME() ) WITH ( 'connector' = 'sls', 'endpoint' ='cn-beijing-intranet.log.aliyuncs.com', 'accessId' = '${yourAccessKeyID}', 'accessKey' = '${yourAccessKeySecret}', 'starttime' = '2024-02-01 10:30:00', 'project' ='${project}', 'logstore' ='${logtore}', 'query' = '* | project Payload, error, "__tag__:__path__", "__tag__:__hostname__", caller | parse-json Payload | project-away Payload | parse-regexp error, ''CouldNotExecuteQuery : ({[\w":\s,\-}]+)'' as errorJson | parse-json errorJson | parse-regexp "__tag__:__path__", ''\/var\/log\/([\w\_]+).LOG'' as serviceName | parse-regexp caller, ''\w+/([\w\.]+):(\d+)'' as fileName, fileNo | project-rename serviceHost="__tag__:__hostname__" | extend scheduleType = json_extract_scalar(schedule, ''$.type'') | project httpCode, errorCode,errorMessage,requestID,fileName, fileNo, serviceHost,scheduleType,project' );Le tableau suivant décrit les paramètres de la clause WITH. Remplacez les valeurs d'exemple par vos valeurs réelles.
Parameter
Description
Example
connector
Le type de connecteur. Connecteurs pris en charge.
sls
endpoint
L'endpoint interne SLS. Endpoints.
cn-hangzhou-intranet.log.aliyuncs.com
accessId
Votre AccessKey ID. Créer une AccessKey.
LTAI
accessKey
L'AccessKey Secret. Créer une AccessKey.
yourAccessKeySecret
starttime
Heure de début pour la consommation des journaux.
2025-02-19 00:00:00
project
Le nom du projet SLS.
test-project
logstore
Le nom du Logstore SLS.
clb-access-log
query
L'instruction SPL. Échappez les littéraux de chaîne avec des guillemets simples doublés (
'') dans Flink SQL.*
where slbid = ''slb-01''
-
Sélectionnez l'instruction SQL, faites un clic droit et sélectionnez Running pour vous connecter à Simple Log Service.
CREATE TEMPORARY TABLE sls_input_complex ( errorCode STRING, errorMessage STRING, fileName STRING, fileNo STRING, httpCode STRING, requestID STRING, scheduleType STRING, serviceHost STRING, project STRING, proctime as PROCTIME() ) WITH ( 'connector' = 'sls', 'endpoint' = 'cn-hxxx', 'accessId' = 'xxx', 'accessKey' = 'xxx', 'starttime' = 'xxx', 'project' = 'xxx', 'logstore' = 'clb7xxx', 'query' = '* | prxxx", "__tag__:__hosxxx' );
3. Exécuter la requête et afficher les résultats
-
Dans l'éditeur de tâches, saisissez l'instruction suivante pour interroger les données :
SELECT * FROM sls_input_complex; -
Cliquez sur Debug dans le coin supérieur droit. Dans la boîte de dialogue, sélectionnez Create new session cluster dans la liste déroulante Session Cluster et configurez comme suit.
Définissez Name sur
demo-test, Deployment Target surdefault-queue, Status sur RUNNING et Engine Version survvr-8.0.11-flink-1.17, puis cliquez sur Create Session Cluster. -
Dans la boîte de dialogue de débogage, sélectionnez le cluster de session que vous venez de créer, puis cliquez sur OK.
Remarque : Le débogage avec une table source SLS avance le décalage du groupe de consommateurs. Une tâche déployée reprend à partir de ce nouveau décalage.
-
Dans l'onglet Results, vous pouvez voir que chaque colonne de la table correspond à un champ traité par la requête SPL.
Après avoir Debug la requête, la table de résultats affiche les champs analysés. Par exemple, la colonne
errorCodeestLogStoreNotExist, la colonneerrorMessageestlogstore k8s-event does not existet la colonnehttpCodeest404.