googleapis / google-cloud-python

Google Cloud Client Library for Python
https://googleapis.github.io/google-cloud-python/
Apache License 2.0
4.73k stars 1.51k forks source link

Add support for `monitoring.regex.full_match` in the `google-cloud-monitoring` #12746

Open racinmat opened 1 month ago

racinmat commented 1 month ago

The google-cloud-monitoring does not support monitoring.regex.full_match. See https://github.com/googleapis/google-cloud-python/blob/main/packages/google-cloud-monitoring/google/cloud/monitoring_v3/query.py#L622-L637 I need to have a way to pass key=value without escaping to fully use the functionality, e.g. the monitoring.regex.full_match. Now, when I call e.g.

    client = monitoring_v3.MetricServiceClient()
   project_id = '...'
    end_time = datetime.fromisoformat("...")

    query = (Query(client, project_id,
                   metric_type='cloudfunctions.googleapis.com/function/execution_count', end_time=end_time,
                   days=30,
                   ).select_resources(
        function_name="""monitoring.regex.full_match(".*-something-.*")""",
        region="europe-west1")
    query.to_dataframe()

I get an error, because the filter built from the params is like this:

'metric.type = "cloudfunctions.googleapis.com/function/execution_count" AND resource.label.function_name = "monitoring.regex.full_match(".*-something-.*")" AND resource.label.region = "europe-west1"'

but I need

'metric.type = "cloudfunctions.googleapis.com/function/execution_count" AND resource.label.function_name = monitoring.regex.full_match(".*-something-.*") AND resource.label.region = "europe-west1"'
racinmat commented 2 weeks ago

Here is my fix for that, but I failed to make the tests run on Windows:

from google.cloud import monitoring_v3
from datetime import datetime, timedelta
from google.cloud.monitoring_v3.query import Query
from google.cloud.monitoring_v3.types import Aggregation
from google.cloud.monitoring_v3 import query
import pandas as pd

# I could make a PR, but running the tests there is pain
def fixed_build_label_filter(category, *args, **kwargs):
    """Construct a filter string to filter on metric or resource labels."""
    terms = list(args)
    for key, value in kwargs.items():
        if value is None:
            continue

        suffix = None
        if key.endswith(
            (
                "_prefix",
                "_suffix",
                "_greater",
                "_greaterequal",
                "_less",
                "_lessequal",
                "_notequal",
                "_regex",
            )
        ):
            key, suffix = key.rsplit("_", 1)

        if category == "resource" and key == "resource_type":
            key = "resource.type"
        else:
            key = ".".join((category, "label", key))

        if suffix == "prefix":
            term = '{key} = starts_with("{value}")'
        elif suffix == "suffix":
            term = '{key} = ends_with("{value}")'
        elif suffix == "greater":
            term = "{key} > {value}"
        elif suffix == "greaterequal":
            term = "{key} >= {value}"
        elif suffix == "less":
            term = "{key} < {value}"
        elif suffix == "lessequal":
            term = "{key} <= {value}"
        elif suffix == "notequal":
            term = "{key} != {value}"
        elif suffix == "regex":
            term = '{key} = monitoring.regex.full_match("{value}")'
        else:
            term = '{key} = "{value}"'

        terms.append(term.format(key=key, value=value))

    return " AND ".join(sorted(terms))

query._build_label_filter = fixed_build_label_filter