google-cloud-platformgoogle-cloud-stackdriver

Why do I set the resource type when writing the metric but not when creating a descriptor?


This is very confusing because the docs make it sound like I choose a resource type when I'm manually creating a descriptor: https://cloud.google.com/monitoring/custom-metrics/creating-metrics#create-metric-desc

But there is no field to set the resource type:

client = monitoring_v3.MetricServiceClient()
project_name = f"projects/{project_id}"
descriptor = ga_metric.MetricDescriptor()
descriptor.type = "custom.googleapis.com/my_metric" + str(uuid.uuid4())
descriptor.metric_kind = ga_metric.MetricDescriptor.MetricKind.GAUGE
descriptor.value_type = ga_metric.MetricDescriptor.ValueType.DOUBLE
descriptor.description = "This is a simple example of a custom metric."

labels = ga_label.LabelDescriptor()
labels.key = "TestLabel"
labels.value_type = ga_label.LabelDescriptor.ValueType.STRING
labels.description = "This is a test label"
descriptor.labels.append(labels)

descriptor = client.create_metric_descriptor(
    name=project_name, metric_descriptor=descriptor
)
print("Created {}.".format(descriptor.name))

I set the type when I'm writing to the custom metric:

series = monitoring_v3.TimeSeries()
series.metric.type = "custom.googleapis.com/my_metric" + str(uuid.uuid4())

###### SET TYPE HERE ######
series.resource.type = "gce_instance"

series.resource.labels["instance_id"] = "1234567890123456789"
series.resource.labels["zone"] = "us-central1-c"
series.metric.labels["TestLabel"] = "My Label Data"
now = time.time()
seconds = int(now)
nanos = int((now - seconds) * 10**9)
interval = monitoring_v3.TimeInterval(
    {"end_time": {"seconds": seconds, "nanos": nanos}}
)
point = monitoring_v3.Point({"interval": interval, "value": {"double_value": 3.14}})
series.points = [point]
client.create_time_series(name=project_name, time_series=[series])

I tried writing to the metric as a gce_instance then as a generic_task and then as global. It created three different metrics- one for each type.

It seems strange this is how it works. I was expecting to be able to bind a custom metric to a specific resource type so it would throw errors if a user tried to write to it as the wrong type.

Is there a way to enforce the resource type for a custom metric? I feel like that information should be part of the "descriptor".


Solution

  • The list of monitored resources on that page is ambiguously described.

    You don't "choose from the following list" when you create the descriptor but, as you've demonstrated, when you write to time-series.

    Curiously, the MetricDescriptor that you create includes a monitoredResourceTypes field that is read-only and is defined to be the list of resource types shown in the documentation.

    BEARER=$(gcloud auth print-access-token)
    
    ENDPOINT="https://monitoring.googleapis.com/v3"
    PROJECT="..."
    METRIC="custom.googleapis.com/..."
    
    curl \
    --silent \
    --get \
    --header "Authorization: Bearer ${BEARER}" \
    --header "Accept: application/json" \ 
    ${ENDPOINT}/projects/${PROJECT}/metricDescriptors/${METRIC} \
    | jq -r .monitoredResourceTypes
    
    [
      "aws_ec2_instance",
      "aws_lambda_function",
      "aws_sqs_queue",
      "baremetalsolution.googleapis.com/Instance",
      "cloud_composer_environment",
      "cloud_composer_workflow",
      "cloud_dataproc_batch",
      "dataflow_job",
      "gae_instance",
      "gce_instance",
      "generic_node",
      "generic_task",
      "gke_container",
      "global",
      "k8s_cluster",
      "k8s_container",
      "k8s_node",
      "k8s_pod",
      "k8s_service",
      "prometheus_target"
    ]
    

    But there's no way to edit this to your preferred subset.