Acerca del lenguaje de consultas de Monitoring (MQL)

En esta página, se proporciona información general sobre el Lenguaje de consulta de Monitoring (MQL), incluidos los siguientes temas:

Esta información se aplica ya sea que uses MQ desde Google Cloud Console o desde la API de Cloud Monitoring. Si deseas obtener información sobre la estructura de las consultas MQTT, consulta Ejemplos.

Combinaciones de teclas para operaciones y funciones de tablas

Las consultas, por lo general, consisten en cadenas conectadas de operaciones de tabla conectadas por inicios (|), cada una de las cuales comienza con el nombre de la operación de tabla seguida de una lista de expresiones. Las expresiones pueden contener llamadas a funciones que enumeren todos sus argumentos de forma explícita. Sin embargo, el lenguaje de consulta de Monitoring permite que las consultas se expresen con varios accesos directos.

En esta sección, se describen las combinaciones de teclas para operaciones de tabla, el uso de funciones como operaciones de tabla y un acceso directo para las columnas de valores como argumentos de funciones.

Para obtener una lista completa, consulta Combinaciones de teclas de operaciones de tabla.

Combinaciones de teclas para operaciones de tabla

Cuando usas las operaciones fetch, group_by y filter, puedes omitir la operación de tabla explícita cuando los argumentos son suficientes para determinar la operación deseada. Por ejemplo, la siguiente consulta:

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

es equivalente a lo siguiente:

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

Las siguientes operaciones de group_by son equivalentes:

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

Puedes omitir la palabra filter si aplicas una clasificación a prueba a prueba de filtro. Por ejemplo, las dos operaciones filter siguientes son equivalentes:

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

Puedes combinar estos formularios de accesos directos en tus consultas. Por ejemplo, la siguiente consulta:

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

es equivalente a este formulario más explícito:

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

A fin de obtener más información sobre los accesos directos para las operaciones de tablas, consulta Combinaciones de teclas de tablas en la referencia de MQL.

Usa una función como una operación de tabla

Por lo general, una operación de tabla comienza con el nombre de una operación de tabla. Pero MQTT permite que una operación de tabla comience con un nombre de función.

Puedes usar un nombre de función cuando la función con nombre es realizar alguna transformación de las columnas de valores de la tabla de entrada. Este reemplazo es un acceso directo para las operaciones de tabla group_by, align o value, según el tipo de función con el nombre proporcionado.

El formulario general es el siguiente:

|  FUNCTION_NAME ARG, ARG ... 

En la operación de tabla, la función toma las columnas de valores de la tabla de entrada como argumentos, argumentos seguidos de cualquier argumento para la función. Cuando usas una función como una operación de tabla, especificas los argumentos en el formulario de operación de tabla, como una lista separada por comas, en lugar de con los paréntesis (()) que se utilizan de forma ordenada con funciones.

La operación de tabla completa que genera el acceso directo depende de la clase de función:

  • group_by: Si estás usando unfunción de agregación con ungroup_by operación que agrega todas las columnas de identificador de serie temporal (es decir, usa[] para agrupar), puedes usar la función como acceso directo. Por ejemplo:

    | distribution powers_of(1.1)

    es un acceso directo para

    | group_by [], distribution(val(0), powers_of(1.1))
  • align: si usas una función de alineación como argumento de la operación align, puedes usar la función como acceso directo. Por ejemplo:

    | delta

    es un acceso directo para

    | align delta()

    Del mismo modo,

    | rate 10m

    es un acceso directo para

    | align rate(10m)

    Ten en cuenta que las funciones de alineador toman la serie temporal de entrada como un argumento implícito, por lo que las columnas de valores no se proporcionan de forma explícita aquí.

  • value: Todas las demás funciones pueden actuar como accesos directos en la operación de tabla value. Por ejemplo:

    | mul 3.3

    es un acceso directo para

    | value mul(val(0), 3.3)

    Del mismo modo,

    | div

    es un acceso directo para

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

    Ten en cuenta que el acceso directo div es una operación de tabla de entrada con dos columnas de valores y produce una operación de tabla con una columna de valores que es la proporción.

Acceso directo a las funciones de columna y valor

Puedes usar .function como acceso directo para function(val()) si hay una sola columna de valor en la entrada, o como acceso directo para function(val(0), val(1)), si hay dos valores. .

El punto al principio significa "Llamar a la siguiente función, proporcionando la columna de valor de punto de entrada (o columnas) como argumentos para la función".

Por ejemplo, .mean es un acceso directo para mean(val()). Los siguientes son equivalentes:

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

Si la tabla de entrada tiene varias columnas de valores, cada columna se convierte en un argumento para la función de este acceso directo. Por ejemplo, si la tabla de entrada tiene dos columnas de valores, entonces

.div

es un acceso directo para

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

Con esta combinación de teclas, puedes proporcionar argumentos que no hagan referencia a las columnas de valores. Los argumentos adicionales se suministran después de los argumentos de columna de valor. Por ejemplo, si la tabla de entrada tiene una columna de valores, entonces

.div(3)

es equivalente a

div(val(0), 3)

Variaciones en fetch

La operación fetch generalmente muestra una tabla de series temporales denominadas por un par de recursos supervisados y de recursos. Por ejemplo:

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

Si la métrica se aplica solo a un tipo de recurso supervisado, puedes omitir el recurso supervisado de la consulta. La siguiente consulta es equivalente a la consulta anterior, porque la métrica de uso de CPU se aplica solo a los recursos supervisados gce_instance:

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

La operación fetch puede especificar solo un tipo de recurso supervisado, con la métrica especificada en una operación posterior metric. Por ejemplo, este ejemplo es equivalente a los ejemplos fetch anteriores:

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

Dividir el objeto fetch de esta manera puede ser útil cuando quieres recuperar dos métricas diferentes para el mismo recurso supervisado. Por ejemplo, la siguiente consulta calcula la cantidad de paquetes por segundo de CPU consumido:

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

Dividir fetch también te permite aplicar filtros solo a las etiquetas del recurso supervisado:

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

Un fetch que nombra solo un tipo de recurso supervisado debe estar seguido por una operación metric, tal vez con operaciones filter intermedias.

Consultas de formas estrictas

Una consulta estricta es aquella en la que no hay accesos directos ni valores implícitos:

  • Se reemplazan todas las combinaciones de teclas.
  • Todos los argumentos implícitos son explícitos.
  • Las columnas se denominan nombres completos.
  • Las columnas nuevas reciben nombres de forma explícita.
  • Las operaciones de alineación suministradas de forma implícita se proporcionan de forma explícita.

El uso del formato estricto hace que la consulta sea más resistente a los cambios en la estructura de las tablas de entrada, y puede aclarar con mayor facilidad lo que hace la consulta. Agregar una consulta de forma estricta no hace que la consulta sea más eficiente.

Cuando guardas una consulta para un gráfico o una política de alertas, se convierte en estricta forma. En el cuadro de diálogo de confirmación de la operación de guardado, se muestra el formulario estricto.

Con las combinaciones de teclas y las formas estrictas disponibles, es posible que encuentres consultas de MQTT equivalentes que se ven muy diferentes entre sí. Por ejemplo, la siguiente consulta, que calcula la cantidad de paquetes recibidos por segundo de CPU consumido, usa muchas combinaciones de teclas:

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

Cuando guardas esta consulta a un gráfico o como parte de una política de alertas, la consulta de forma estricta resultante hace exactamente lo mismo. Sin embargo, su forma estricta podría verse bastante diferente, como se muestra en el siguiente ejemplo:

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)]

Cuando editas la definición guardada del gráfico o la política de alertas, el Editor de consultas muestra la forma estricta.

Coincidencia con la columna resource.project_id

Los proyectos de Google Cloud tienen un nombre visible, que aparece en los menús, pero no identifica el proyecto de forma única. Un nombre visible del proyecto podría ser “Demostración de Monitoring”.

Los proyectos también tienen dos campos que actúan como identificadores:

  • ID del proyecto: Un identificador único de string. En general, se basa en el nombre comercial. Los ID del proyecto se establecen cuando se crea el proyecto, por lo general, concatenando los elementos del nombre del proyecto y, si es necesario, agrega dígitos al final. Un ID del proyecto puede tener el formato “monitoring-demo” o “monitoring-demo-2349”. El ID del proyecto a veces se denomina nombre de proyecto informal.
  • Número de proyecto: un identificador numérico único.

Cada tipo de recurso supervisado incluye una etiqueta project_id, con una representación de string del número de proyecto que posee el recurso y los datos sobre ese recurso.

En las consultas de MQL, debes referirte a esta etiqueta como resource.project_id. La etiqueta resource.project_id tiene el número de proyecto en forma de texto como su valor, pero MQL convierte ese valor en el ID del proyecto en ciertas situaciones.

En los siguientes casos, MQL trata el valor de la etiqueta resource.project_id como el ID del proyecto en lugar del número del proyecto:

  • La leyenda de un gráfico muestra el ID del proyecto en lugar del número del proyecto para el valor de la etiqueta resource.project_id.

  • Las comparaciones de igualdad del valor de resource.project_id para un literal de string reconocen tanto el número del proyecto como el ID del proyecto. Por ejemplo, ambos muestran un valor verdadero para los recursos de este proyecto:

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

    Este caso se aplica a los operadores == y !=, y por sus formularios de funciones, eq() y ne().

  • Una coincidencia de expresión regular en la etiqueta resource.project_id funciona correctamente con el número de proyecto o el ID del proyecto. Por ejemplo, las dos expresiones siguientes muestran true para los recursos de este proyecto:

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

    Este caso se aplica a los operadores =~ y !~, y para el formulario de función, re_full_match.

En todos los demás casos, se usa el valor real de la etiqueta resource.project_id. Por ejemplo, concatenate("project-", resource.project_id) da como resultado el valor project-530310927541 y no project-monitoring-demo.

Relaciones y el "efecto perimetral"

En general, es mejor calcular las proporciones según la serie temporal recopilada para un solo tipo de métrica, mediante el uso de valores de etiqueta. Una proporción calculada en dos tipos de métricas diferentes está sujeta a un efecto perimetral.

Por ejemplo, supongamos que tienes dos tipos de métricas diferentes, un recuento total de RPC y un recuento de errores de RPC, y deseas calcular la proporción de RPC del recuento de errores en comparación con el total de RPC. Las RPC fallidas se cuentan en series temporales de ambos tipos de métricas, por lo que existe la posibilidad de que, cuando alinees las series temporales, una RPC incorrecta pueda aparecer en un intervalo de alineación en el recuento total, pero en un intervalo de alineación diferente para el recuento de errores. Esta diferencia puede deberse a varios motivos, incluidos los siguientes:

  • Como hay dos series temporales diferentes que registran el mismo evento, hay dos valores de contador subyacentes que implementan la colección y no se actualizarán de forma atómica.
  • Las tasas de muestreo pueden variar. Cuando las series temporales se alinean con un período común, los recuentos de un solo evento pueden aparecer en intervalos de alineación adyacentes en la serie temporal para las diferentes métricas.

La diferencia en la cantidad de valores en los intervalos de alineación correspondientes puede llevar a valores error/total de relación sin sentido, como 1/0 o 2/1.

El efecto perimetral suele ser menor en las relaciones entre números más grandes. Puedes obtener números más grandes mediante la agregación, ya sea con una ventana de alineación que sea más larga que el período de muestreo o mediante la agrupación de datos para determinadas etiquetas. Estas técnicas minimizan el efecto de las pequeñas diferencias en la cantidad de puntos de un intervalo determinado. una diferencia de dos puntos es más significativa si la cantidad esperada de puntos en un intervalo es tres a la que el número esperado es 300.

Si usas tipos de métricas integradas, es posible que no tengas opción, pero calcular cálculos en tipos de métricas para obtener el valor que necesitas.

Si diseñas métricas personalizadas que podrían contar lo mismo, como las RPC que muestran el estado de error, en dos métricas diferentes, considera una sola métrica, que incluya cada recuento solo una vez. Por ejemplo, si cuentas RPC y quieres realizar un seguimiento de la proporción entre RPC fallidas y todas las RPC, crea un solo tipo de métrica a fin de contar las RPC y usa una etiqueta para registrar el estado de las invocación, incluido el estado "OK". Luego, cada valor de estado, o "OK", se registra mediante la actualización de un contador único para ese caso.

Formatos de fecha de MQL

Actualmente, MQL admite una cantidad limitada de formatos de fecha. En las consultas de MQL, las fechas se expresan como una de las siguientes opciones:

  • d'BASE_STRING'
  • D'BASE_STRING'

El BASE_STRING es una string con el formato 2010/06/23-19:32:15-07:00. El primer guion (-), que separa la fecha y hora, se puede reemplazar por un espacio. En el componente de tiempo, se pueden descartar partes de la hora del reloj (19:32:15) o el especificador de zona horaria (-07:00).

Los siguientes ejemplos son fechas válidas en consultas de 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'

En la siguiente tabla, se muestra la gramática de BASE_STRING:

Estructura Significado
%Y/%m/%d Date
%Y/%m/%d %H
%Y/%m/%d-%H
DATE, HOUR
%Y/%m/%d %H:%M
%Y/%m/%d-%H:%M
Fecha, hora, minuto
%Y/%m/%d %H:%M:%S
%Y/%m/%d-%H:%M:%S
Fecha, hora, minuto, segundo
%Y/%m/%d %H:%M:%E*S
%Y/%m/%d-%H:%M:%E*S
Fecha, hora, minuto, segundo decimal
%Y/%m/%d %Ez Fecha con zona horaria
%Y/%m/%d %H%Ez
%Y/%m/%d-%H%Ez
Fecha, hora, con zona horaria
%Y/%m/%d %H:%M%Ez
%Y/%m/%d-%H:%M%Ez
Fecha, hora, minuto, con zona horaria
%Y/%m/%d %H:%M:%S%Ez
%Y/%m/%d-%H:%M:%S%Ez
Fecha, hora, minuto, segundo, con zona horaria
%Y/%m/%d %H:%M:%E*S%Ez
%Y/%m/%d-%H:%M:%E*S%Ez
Fecha, hora, minuto, segundo decimal, con zona horaria

Longitud y complejidad de las consultas

La supervisión de consultas de lenguaje de consulta puede ser larga y compleja, pero no tiene límites.

  • Un texto de consulta, codificado como UTF-8, está limitado a 10,000 bytes.
  • Las consultas tienen un límite de 2,000 compilaciones de lenguaje. Es decir, la complejidad de AST está limitada a 2,000 nodos.

Unárbol de sintaxis abstracta , o AST, es una representación del código fuente, en este caso, la string de consulta de MQL, en la que los nodos del árbol se asignan a estructuras sintácticas en el código.

Macros de MQL

MQL incluye una utilidad de definición de macros, def. Puedes usar la utilidad de definición de macros MQ para reemplazar operaciones repetidas, facilitar la lectura de consultas complejas y facilitar el desarrollo de consultas. Puedes definir macros para operaciones de tablas y funciones.

Cuando guardas una consulta con macros, no se conservan las macros. En la conversión a strict estricto, las invocaciones de la macro se reemplazan por su texto correspondiente y se quitan las definiciones de macros.

Macros para operaciones con tablas

Puedes escribir macros para realizar operaciones de tabla nuevas. La sintaxis general se ve de la siguiente manera:

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

Para invocar la macro, usa la siguiente sintaxis:

@MACRO_NAME [MACRO_ARG [, MACRO_ARG]]

Por ejemplo, supón que usas la siguiente consulta para obtener datos sobre el uso de CPU:

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

La primera línea se puede reemplazar por la siguiente macro:

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

Para invocar la macro en la consulta, reemplaza el fetch original de la siguiente manera:

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

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

Puedes reemplazar la segunda y la tercera línea con macros que toman argumentos. La definición de la macro enumera los parámetros para la macro y, en el cuerpo de la macro, consulta los parámetros de la macro como $MACRO_PARAMETER. Por ejemplo, puedes definir las siguientes macros:

def my_every time_arg = every $time_arg ;

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

Para invocar estas macros y proporcionar los argumentos, especifica los argumentos en una lista delimitada por comas en las invocaciones de macros. A continuación, se muestra la consulta con todas las macros definidas y sus invocaciones:

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())

Las macros no se conservan cuando la consulta se convierte a strict-form. Por ejemplo, la forma estricta de la consulta anterior se ve de la siguiente manera:

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization
| align mean_aligner()
| every 1m
| group_by [resource.zone],
           [value_utilization_mean: mean(value.utilization)]

Macros para funciones

Para las funciones de MQL, especificas cualquier parámetro en una lista delimitada por comas entre paréntesis. Los paréntesis distinguen una macro de función de una macro de operación de tabla. El superior debe aparecer en la invocación, incluso si no hay argumentos. Las macros no se conservan cuando la consulta se convierte a strict-form.

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

Por ejemplo, la siguiente consulta recupera tablas para dos métricas, combina las dos tablas en una con dos columnas de valores y calcula la proporción de bytes recibidos en bytes totales para una columna llamada 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))]

Puedes reemplazar el cálculo received_percent con una macro como el siguiente ejemplo:

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

Para invocar una macro de función, usa la siguiente sintaxis:

@MACRO_NAME([MACRO_ARG[, MACRO_ARG]])

Cuando invocas una macro de función sin argumentos, debes especificar las paréntesis vacías para distinguir la invocación de la invocación de una macro de operación de tabla.

En el siguiente ejemplo, se muestra la consulta anterior con una macro para la computación de proporción:

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))]

Limitaciones

La función de macro de MQL no admite lo siguiente:

  • Anidación de macros.
  • Macros definidas de forma recurrente Ningún cuerpo de la macro puede hacer referencia a cualquier macro, incluida la misma, que aún no está completamente definida.
  • Uso de funciones definidas por macros como operaciones de tabla
  • Uso de argumentos de macros como nombres de funciones o operaciones de tabla.
  • Preservación de macros cuando la consulta se convierte en formato estricto Las invocaciones de la macro se reemplazan con el texto correspondiente y se quitan las definiciones de macros.