À propos du langage MQL (Monitoring Query Language)

Cette page fournit des informations générales sur le langage MQL (Monitoring Query Language), y compris les sujets suivants :

Ces informations s'appliquent si vous utilisez MQL depuis Google Cloud Console ou l'API Cloud Monitoring. Pour en savoir plus sur la structure des requêtes MQL, consultez la page Exemples.

Raccourcis des opérations et fonctions de table

Les requêtes sont généralement constituées de chaînes connectées d'opérations de table reliées par des barres (|), chacune commençant par le nom de l'opération de table suivie d'une liste d'expressions. Les expressions peuvent contenir des appels de fonction qui répertorient explicitement tous leurs arguments. Cependant, le langage MQL permet d'exprimer les requêtes avec un certain nombre de raccourcis.

Cette section décrit les raccourcis des opérations de table, explique comment utiliser les fonctions comme opérations de table, et présente le raccourci permettant d'utiliser les colonnes de valeurs comme arguments des fonctions.

Pour obtenir la liste complète, consultez la section Raccourcis des opérations de table.

Raccourcis des opérations de table

Lorsque vous utilisez les opérations fetch, group_by et filter, vous pouvez omettre l'opération de table explicite lorsque les arguments sont suffisants pour déterminer l'opération prévue. Par exemple, la requête suivante :

gce_instance::compute.googleapis.com/instance/cpu/utilization

équivaut à :

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization

Les opérations group_by suivantes sont équivalentes :

         [zone], mean(val())
group_by [zone], mean(val())

Vous pouvez omettre le mot filter si vous mettez le test du filtre entre parenthèses. Par exemple, les deux opérations filter suivantes sont équivalentes :

       (instance_name =~ 'apache.*')
filter instance_name =~ 'apache.*'

Vous pouvez combiner ces formats de raccourci dans vos requêtes. Par exemple, la requête suivante :

gce_instance::compute.googleapis.com/instance/cpu/utilization
| (instance_name =~ 'apache.*')
|  [zone], mean(val())

équivaut à ce format plus explicite :

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization
| filter instance_name =~ 'apache.*'
| group_by [zone], mean(val())

Pour plus d'informations sur les raccourcis des opérations de table, consultez la section Raccourcis des opérations de table dans la documentation de référence sur le langage MQL.

Utiliser une fonction comme opération de table

Une opération de table commence généralement par le nom d'une opération de table. Cependant, MQL permet à une opération de table de commencer par un nom de fonction.

Vous pouvez utiliser un nom de fonction lorsque la fonction nommée consiste à effectuer une transformation des colonnes de valeurs de la table d'entrée. Ce remplacement est un raccourci pour les opérations de table group_by, align ou value, en fonction du type de fonction nommé.

Le format général ressemble à ceci :

|  FUNCTION_NAME ARG, ARG ... 

Dans l'opération de table, la fonction utilise les colonnes de valeurs de la table d'entrée comme arguments (suivis des arguments de la fonction elle-même). Lorsque vous utilisez une fonction comme opération de table, vous séparez les arguments par une virgule dans l'opération de table, plutôt que d'utiliser les parenthèses (()) généralement utilisés avec les fonctions.

L'opération de table complète générée par le développement du raccourci dépend du type de fonction :

  • group_by : si vous utilisez une fonction d'agrégation avec une opération group_by qui regroupe l'ensemble des colonnes d'identifiants de séries temporelles (c'est-à-dire qu'elle utilise [] pour effectuer le regroupement), vous pouvez utiliser la fonction en tant que raccourci. Exemple :

    | distribution powers_of(1.1)

    est un raccourci de

    | group_by [], distribution(val(0), powers_of(1.1))
  • align : si vous utilisez une fonction d'alignement comme argument de l'opération align, vous pouvez utiliser la fonction en tant que raccourci. Exemple :

    | delta

    est un raccourci de

    | align delta()

    De même,

    | rate 10m

    est un raccourci de

    | align rate(10m)

    Notez que les fonctions d'alignement utilisent la série temporelle d'entrée comme argument implicite. Les colonnes de valeurs ne sont donc pas explicitement indiquées ici.

  • value : toutes les autres fonctions peuvent être utilisées comme raccourcis pour l'opération de table value. Exemple :

    | mul 3.3

    est un raccourci de

    | value mul(val(0), 3.3)

    De même,

    | div

    est un raccourci de

    | value div(val(0), val(1))

    Notez que le raccourci div utilise une opération de table d'entrée avec deux colonnes de valeurs et génère une opération de table avec une colonne de valeurs correspondant au ratio.

Raccourci des fonctions de colonnes de valeurs

Vous pouvez utiliser .function comme raccourci de function(val()) si l'entrée ne comporte qu'une seule colonne de valeur, ou comme raccourci de function(val(0), val(1)) s'il existe deux colonnes de valeurs ou plus.

Le point initial signifie "Appeler la fonction suivante, en fournissant la ou les colonnes de valeurs du point d'entrée comme argument(s) de la fonction".

Par exemple,   .mean est un raccourci de mean(val()). Les expressions suivantes sont équivalentes :

group_by [zone], .mean
group_by [zone], mean(val())

Si la table d'entrée comporte plusieurs colonnes de valeurs, chaque colonne devient un argument de la fonction dans ce raccourci. Par exemple, si la table d'entrée comporte deux colonnes de valeurs,

.div

est un raccourci de

div(val(0), val(1))

Ce raccourci vous permet de fournir des arguments qui ne font pas référence à des colonnes de valeurs. Les arguments supplémentaires sont fournis après les arguments de colonnes de valeurs. Par exemple, si la table d'entrée comporte une colonne de valeurs,

.div(3)

est équivalent à :

div(val(0), 3)

Variantes sur une opération fetch

L'opération fetch renvoie généralement une table de séries temporelles nommée par une paire de valeurs composée du type de ressource surveillée et du type de métrique. Exemple :

fetch gce_instance :: compute.googleapis.com/instance/cpu/utilization

Si la métrique ne s'applique qu'à un seul type de ressource surveillée, vous pouvez omettre la ressource surveillée de la requête. La requête suivante est équivalente à la requête précédente, car la métrique d'utilisation du processeur ne s'applique qu'aux ressources surveillées gce_instance :

fetch compute.googleapis.com/instance/cpu/utilization

L'opération fetch ne peut spécifier qu'un type de ressource surveillée, avec la métrique spécifiée dans les opérations metric ultérieures. L'exemple suivant est équivalent aux exemples fetch précédents :

fetch gce_instance
| metric metric compute.googleapis.com/instance/cpu/utilization

Diviser l'opération fetch de cette manière peut être utile lorsque vous souhaitez récupérer deux métriques différentes pour la même ressource surveillée. Par exemple, la requête suivante calcule le nombre de paquets par seconde de processeur consommée :

fetch gce_instance
| {
    metric compute.googleapis.com/instance/network/received_packets_count ;
    metric compute.googleapis.com/instance/cpu/usage_time
  }
| ratio

La division de fetch vous permet également de n'appliquer un filtrage qu'aux libellés de la ressource surveillée :

fetch gce_instance
| filter resource.zone =~ "asia.*"
| {
    metric compute.googleapis.com/instance/network/received_packets_count ;
    metric compute.googleapis.com/instance/cpu/usage_time
  }
| ratio

Une opération fetch qui ne nomme qu'un type de ressource surveillée doit être suivie d'une opération metric, éventuellement avec des opérations filter intermédiaires.

Requêtes de format strict

Une requête de format strict est une requête sans raccourcis ni valeurs implicites :

  • Tous les raccourcis sont remplacés.
  • Tous les arguments implicites sont rendus explicites.
  • Les colonnes sont désignées par des noms complets.
  • Les nouvelles colonnes portent des noms explicites.
  • Toutes les opérations d'alignement implicitement fournies sont explicitement données.

L'utilisation du format strict rend la requête plus résiliente aux modifications de la structure des tables d'entrée. Cela peut ainsi permettre de clarifier la fonction de la requête. L'exécution d'une requête de format strict ne rend pas la requête plus efficace.

Lorsque vous enregistrez une requête pour un graphique ou une règle d'alerte, celle-ci est convertie dans un format strict. La boîte de dialogue de confirmation pour l'opération d'enregistrement affiche le format strict.

Lorsque des raccourcis et des formats stricts sont disponibles, vous pouvez rencontrer des requêtes MQL équivalentes qui ne se ressemblent pas. Par exemple, la requête suivante, qui calcule le nombre de paquets reçus par seconde de processeur consommée, utilise de nombreux raccourcis :

gce_instance
| (zone =~ ".*-a")
| {
    compute.googleapis.com/instance/network/received_packets_count ;
    compute.googleapis.com/instance/cpu/usage_time
  }
| join
| div

Lorsque vous enregistrez cette requête dans un graphique ou dans le cadre d'une règle d'alerte, la requête de format strict obtenue fait exactement la même chose. Cependant, le format strict peut sembler légèrement différent, comme illustré dans l'exemple suivant :

fetch gce_instance
| filter (resource.zone =~ '.*-a')
| { t_0:
      metric 'compute.googleapis.com/instance/network/received_packets_count'
      | align delta() ;
    t_1:
      metric 'compute.googleapis.com/instance/cpu/usage_time'
      | align delta() }
| join
| value [v_0: div(t_0.value.received_packets_count, t_1.value.usage_time)]

Lorsque vous modifiez la définition enregistrée du graphique ou de la règle d'alerte, l'éditeur de requête affiche le format strict.

Travailler avec la colonne resource.project_id

Le nom à afficher des projets Google Cloud apparaît dans les menus, mais ne permet pas de les identifier de manière unique. Le nom à afficher d'un projet peut être "Démonstration Monitoring".

Les projets comportent également deux champs qui servent d'identifiants :

  • ID du projet : identifiant de chaîne unique. Il est souvent basé sur le nom à afficher. Il est défini lors de la création du projet, généralement en concaténant les éléments du nom du projet et en ajoutant éventuellement des chiffres à la fin, si cela est nécessaire pour pouvoir identifier le projet de manière unique. Un ID de projet peut se présenter au format "monitoring-demo" ou "monitoring-demo-2349". L'ID du projet est parfois appelé nom du projet.
  • Numéro de projet : identifiant numérique unique.

Chaque type de ressource surveillée inclut un libellé project_id. Le numéro du projet propriétaire de la ressource et des données de cette ressource est représenté sous forme de chaîne.

Dans les requêtes MQL, ce libellé est référence comme resource.project_id. La valeur du libellé resource.project_id est en général le numéro de projet au format textuel, mais MQL peut dans certains cas convertir cette valeur afin de renvoyer l'ID du projet.

Dans les cas suivants, MDL traite la valeur du libellé resource.project_id comme l'ID de projet plutôt que le numéro de projet :

  • La légende d'un graphique affiche l'ID du projet plutôt que son numéro pour la valeur du libellé resource.project_id.

  • Les comparaisons d'égalité entre la valeur du libellé resource.project_id et une chaîne littérale reconnaissent à la fois le numéro du projet et l'ID du projet. Par exemple, les comparaisons suivantes renvoient vrai toutes les deux pour des ressources appartenant au projet :

    • resource.project_id == "monitoring-demo"
    • resource.project_id == "530310927541"

    Ce cas s'applique aux opérateurs == et !=, ainsi qu'à leurs formats de fonctions, eq() et ne().

  • Une correspondance d'expression régulière sur le libellé resource.project_id fonctionne correctement avec le numéro du projet ou l'ID du projet. Par exemple, les deux expressions suivantes renvoient true pour les ressources appartenant à ce projet :

    • resource.project_id =~ "monitoring-.*"
    • resource.project_id =~ ".*27541"

    Ce cas s'applique aux opérateurs =~ et !~, ainsi qu'au format de fonction re_full_match.

Dans tous les autres cas, la valeur réelle du libellé resource.project_id est utilisée. Par exemple, la fonction concatenate("project-", resource.project_id) renvoie project-530310927541 et non project-monitoring-demo.

Ratios et "effet de bord"

En général, il est préférable de calculer les ratios en fonction des séries temporelles collectées pour un type de métrique unique, à l'aide de valeurs de libellés. Un ratio calculé sur deux types de métriques peut faire l'objet d'un effet de bord.

Par exemple, supposons que vous disposiez de deux types de métriques différents (nombre de RPC total et nombre de RPC en erreur), et que vous souhaitiez calculer le ratio du nombre de RPC en erreur par rapport au nombre de RPC total. Les RPC ayant échoué sont comptabilisés dans les séries temporelles des deux types de métriques. Il est donc possible que, lorsque vous alignez la série temporelle, un RPC en échec peut apparaître dans un intervalle d'alignement dans le nombre total, mais dans un autre intervalle d'alignement pour le nombre d'erreurs. Cette différence peut se produire pour plusieurs raisons :

  • Étant donné que deux séries temporelles enregistrent le même événement, deux valeurs de compteur sous-jacentes mettent en œuvre la collection, qui ne seront pas mises à jour de manière atomique.
  • Les taux d'échantillonnage peuvent varier. Lorsque les séries temporelles sont alignées sur une période commune, les décomptes d'un seul événement peuvent apparaître dans des intervalles d'alignement adjacents dans la série temporelle des différentes métriques.

La différence du nombre de valeurs dans les intervalles d'alignement correspondants peut générer des valeurs de ratio error/total insensées, telles que 1/0 ou 2/1.

L'effet de bord est généralement inférieur pour les ratios entre des nombres supérieurs. Vous pouvez obtenir des nombres plus élevés par agrégation, soit en utilisant une fenêtre d'alignement plus longue que la période d'échantillonnage, soit en regroupant les données pour certains libellés. Ces techniques réduisent l'effet de différences mineures dans le nombre de points sur un intervalle donné. Une disparité à deux points est plus importante si trois points sont attendus dans un intervalle plutôt que 300.

Si vous utilisez des types de métriques intégrés, vous n'aurez peut-être pas d'autre choix que de calculer les ratios sur l'ensemble des types de métriques pour obtenir la valeur souhaitée.

Si vous concevez des métriques personnalisées pouvant comptabiliser la même chose (par exemple, des RPC qui renvoient des états d'erreur) dans deux métriques différentes, envisagez plutôt d'utiliser une seule métrique, qui inclut un décompte à la fois. Par exemple, si vous comptabilisez les RPC et que vous souhaitez suivre ratio des RPC en échec par rapport à tous les RPC, créez un seul type de métrique pour comptabiliser les RPC, puis utilisez un libellé pour enregistrer l'état de l'appel, avec l'état "OK". Ensuite, chaque valeur d'état, erreur ou état "OK", est enregistré en mettant à jour un seul compteur pour ce cas.

Formats de date MQL

MQL n'accepte actuellement qu'un nombre limité de formats de date. Dans les requêtes MQL, les dates sont exprimées dans l'un des formats suivants :

  • d'BASE_STRING'
  • D'BASE_STRING'

BASE_STRING est une chaîne au format 2010/06/23-19:32:15-07:00. Le premier tiret (-) séparant la date et l'heure peut être remplacé par une espace. Dans le composant temporel, certaines parties de l'horloge (19:32:15) ou du spécificateur de fuseau horaire (-07:00) peuvent être supprimées.

Les exemples suivants sont des dates valides dans les requêtes MQL :

  • d'2010/06/23-19:32:15-07:00'
  • d'2010/06/23 19:32:15-07:00'
  • d'2010/06/23 19:32:15'
  • D'2010/06/23 19:32'
  • d'2010/06/23-19'
  • D'2010/06/23 -07:00'

Le tableau suivant répertorie la grammaire utilisée pour BASE_STRING :

Structure Signification
%Y/%m/%d Date
%Y/%m/%d %H
%Y/%m/%d-%H
Date et heure
%Y/%m/%d %H:%M
%Y/%m/%d-%H:%M
Date, heure et minute
%Y/%m/%d %H:%M:%S
%Y/%m/%d-%H:%M:%S
Date, heure, minute et seconde
%Y/%m/%d %H:%M:%E*S
%Y/%m/%d-%H:%M:%E*S
Date, heure, minute et seconde fractionnaire
%Y/%m/%d %Ez Date avec fuseau horaire
%Y/%m/%d %H%Ez
%Y/%m/%d-%H%Ez
Date et heure avec fuseau horaire
%Y/%m/%d %H:%M%Ez
%Y/%m/%d-%H:%M%Ez
Date, heure et minute avec fuseau horaire
%Y/%m/%d %H:%M:%S%Ez
%Y/%m/%d-%H:%M:%S%Ez
Date, heure, minute et seconde avec fuseau horaire
%Y/%m/%d %H:%M:%E*S%Ez
%Y/%m/%d-%H:%M:%E*S%Ez
Date, heure, minute et seconde fractionnaire avec fuseau horaire

Longueur et complexité des requêtes

Les requêtes MQL peuvent être longues et complexes, mais ne sont pas illimitées.

  • Un texte de requête encodé au format UTF-8 est limité à 10 000 octets.
  • Une requête est limitée à 2 000 constructions de langage, c'est-à-dire que la complexité de l'arbre syntaxique abstrait (ASA) est limitée à 2 000 nœuds.

 Un arbre syntaxique abstrait, ou ASA, est une représentation du code source (dans ce cas, une chaîne de requête MQL), où les nœuds de l'arborescence correspondent à des structures syntaxiques dans le code.

Macros MQL

MQL inclut un utilitaire de définition de macros appelé def. Celui-ci vous permet de remplacer les opérations répétées, de rendre les requêtes complexes plus lisibles et de simplifier le développement des requêtes. Vous pouvez définir des macros pour des opérations de table et des fonctions.

Macros pour les opérations de table

Vous pouvez écrire des macros pour effectuer de nouvelles opérations de table. La syntaxe générale se présente comme suit :

def MACRO_NAME [MACRO_PARAMETER[, MACRO_PARAMETER]] = MACRO_BODY ;

Pour appeler la macro, utilisez la syntaxe suivante :

@MACRO_NAME [MACRO_ARG [, MACRO_ARG]]

Par exemple, supposons que vous exécutiez la requête suivante pour extraire les données d'utilisation du processeur :

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization
| every 1m
| group_by [zone], mean(val())

La première ligne peut être remplacée par la macro suivante :

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;

Pour appeler la macro dans la requête, remplacez la valeur fetch d'origine comme suit :

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;

@my_fetch
| every 1m
| group_by [zone], mean(val())

Vous pouvez remplacer la deuxième et la troisième lignes par des macros acceptant des arguments. La définition de la macro répertorie les paramètres de la macro et y fait référence au format $MACRO_PARAMETER dans le corps de la macro. Par exemple, vous pouvez définir les macros suivantes :

def my_every time_arg = every $time_arg ;

def my_group label, aggr = group_by [$label], $aggr ;

Pour appeler ces macros et fournir les arguments, spécifiez les arguments dans une liste délimitée par des virgules dans les appels de macros. Voici la requête avec toutes les macros définies et leurs appels :

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;
def my_every time_arg = every $time_arg ;
def my_group label, aggr = group_by [$label], $aggr ;

{@my_fetch}
| @my_every 1m
| @my_group zone, mean(val())

Macros pour les fonctions

Pour les fonctions MQL, vous spécifiez les paramètres entre parenthèses dans une liste délimitée par des virgules. Les parenthèses distinguent la macro pour une fonction d'une macro pour une opération de table. Les parenthèses doivent apparaître dans l'appel, même si aucun argument n'existe.

def MACRO_NAME([MACRO_PARAMETER [, MACRO_PARAMETER]]) = MACRO_BODY ;

Par exemple, la requête suivante récupère les tables de deux métriques, combine les deux tables en une seule avec deux colonnes de valeurs, puis calcule le ratio entre le nombre d'octets reçus et le nombre total d'octets dans une colonne appelée received_percent :

{
  fetch k8s_pod :: kubernetes.io/pod/network/received_bytes_count ;
  fetch k8s_pod :: kubernetes.io/pod/network/sent_bytes_count
}
| join
| value [received_percent: val(0) * 100 / (val(0) + val(1))]

Vous pouvez remplacer le calcul received_percent par une macro semblable à la suivante :

def recd_percent(recd, sent) = $recd * 100 / ($recd + $sent) ;

Pour appeler une macro pour une fonction, utilisez la syntaxe suivante :

@MACRO_NAME([MACRO_ARG[, MACRO_ARG]])

Lorsque vous appelez une macro pour une fonction sans argument, vous devez spécifier les parenthèses vides pour distinguer l'appel de l'appel d'une macro pour une opération de table.

L'exemple suivant montre la requête précédente avec une macro permettant de calculer le ratio :

def recd_percent(recd, sent) = $recd * 100 / ($recd + $sent) ;

{
  fetch k8s_pod :: kubernetes.io/pod/network/received_bytes_count ;
  fetch k8s_pod :: kubernetes.io/pod/network/sent_bytes_count
}
| join
| value [received_percent: @recd_percent(val(0), val(1))]

Limites

La fonctionnalité de macro MQL n'est pas compatible avec les éléments suivants :

  • Imbrication de macros
  • Macros définies de manière récursive. Aucune macro ne peut faire référence dans son corps à une macro qui n'est pas encore entièrement définie, y compris elle-même.
  • Utilisation de fonctions définies par des macros en tant qu'opérations de table
  • Utilisation d'arguments de macros en tant que noms de fonctions ou d'opérations de table