diff --git a/monitoring/api/auth.py b/monitoring/api/auth.py index d62f03c03be9..fb349d74ec96 100644 --- a/monitoring/api/auth.py +++ b/monitoring/api/auth.py @@ -16,6 +16,10 @@ """Sample command-line program for retrieving Google Cloud Monitoring API data. +Prerequisites: To run locally, download a Service Account JSON file from +your project and point GOOGLE_APPLICATION_CREDENTIALS to the file. + + This sample is used on this page: https://cloud.google.com/monitoring/api/authentication @@ -30,7 +34,6 @@ from googleapiclient.discovery import build from oauth2client.client import GoogleCredentials - METRIC = 'compute.googleapis.com/instance/disk/read_ops_count' YOUNGEST = '2015-01-01T00:00:00Z' diff --git a/monitoring/api/labeled_custom_metric.py b/monitoring/api/labeled_custom_metric.py new file mode 100644 index 000000000000..9fd6c4d77ce6 --- /dev/null +++ b/monitoring/api/labeled_custom_metric.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Creates, writes, and reads a labeled custom metric. + +This is an example of how to use the Google Cloud Monitoring API to create, +write, and read a labeled custom metric. +The metric has two labels: color and size, and the data points represent +the number of shirts of the given color and size in inventory. + +Prerequisites: To run locally, download a Service Account JSON file from +your project and point GOOGLE_APPLICATION_CREDENTIALS to the file. + +From App Engine or a GCE instance with the correct scope, the Service +Account step is not required. + +Typical usage: Run the following shell commands on the instance: + python labeled_custom_metric.py --project_id / + --color yellow --size large --count 10 +""" + +import argparse +import datetime +import time + +from googleapiclient import discovery +from oauth2client.client import GoogleCredentials + +CUSTOM_METRIC_DOMAIN = "custom.cloudmonitoring.googleapis.com" +CUSTOM_METRIC_NAME = "{}/shirt_inventory".format(CUSTOM_METRIC_DOMAIN) + + +def format_rfc3339(datetime_instance=None): + """Formats a datetime per RFC 3339. + :param datetime_instance: Datetime instance to format, defaults to utcnow + """ + return datetime_instance.isoformat("T") + "Z" + + +def get_now_rfc3339(): + return format_rfc3339(datetime.datetime.utcnow()) + + +def create_custom_metric(client, project_id): + """Create metric descriptor for the custom metric and send it to the + API.""" + # You need to execute this operation only once. The operation is + # idempotent, so, for simplicity, this sample code calls it each time + + # Create a label descriptor for each of the metric labels. The + # "description" field should be more meaningful for your metrics. + label_descriptors = [] + for label in ["color", "size", ]: + label_descriptors.append({"key": "/{}".format(label), + "description": "The {}.".format(label)}) + + # Create the metric descriptor for the custom metric. + metric_descriptor = { + "name": CUSTOM_METRIC_NAME, + "project": project_id, + "typeDescriptor": { + "metricType": "gauge", + "valueType": "int64", + }, + "labels": label_descriptors, + "description": "The size of my shirt inventory.", + } + # Submit the custom metric creation request. + try: + request = client.metricDescriptors().create( + project=project_id, body=metric_descriptor) + request.execute() # ignore the response + except Exception as e: + print("Failed to create custom metric: exception={})".format(e)) + raise + + +def write_custom_metric(client, project_id, now_rfc3339, color, size, count): + """Write a data point to a single time series of the custom metric.""" + # Identify the particular time series to which to write the data by + # specifying the metric and values for each label. + timeseries_descriptor = { + "project": project_id, + "metric": CUSTOM_METRIC_NAME, + "labels": { + "{}/color".format(CUSTOM_METRIC_DOMAIN): color, + "{}/size".format(CUSTOM_METRIC_DOMAIN): size, + } + } + + # Specify a new data point for the time series. + timeseries_data = { + "timeseriesDesc": timeseries_descriptor, + "point": { + "start": now_rfc3339, + "end": now_rfc3339, + "int64Value": count, + } + } + + # Submit the write request. + request = client.timeseries().write( + project=project_id, body={"timeseries": [timeseries_data, ]}) + try: + request.execute() # ignore the response + except Exception as e: + print("Failed to write data to custom metric: exception={}".format(e)) + raise + + +def read_custom_metric(client, project_id, now_rfc3339, color, size): + """Read all the timeseries data points for a given set of label values.""" + # To identify a time series, specify values for in label as a list. + labels = ["{}/color=={}".format(CUSTOM_METRIC_DOMAIN, color), + "{}/size=={}".format(CUSTOM_METRIC_DOMAIN, size), ] + + # Submit the read request. + request = client.timeseries().list( + project=project_id, + metric=CUSTOM_METRIC_NAME, + youngest=now_rfc3339, + labels=labels) + + # When a custom metric is created, it may take a few seconds + # to propagate throughout the system. Retry a few times. + start = time.time() + while True: + try: + response = request.execute() + for point in response["timeseries"][0]["points"]: + print("{}: {}".format(point["end"], point["int64Value"])) + break + except Exception as e: + if time.time() < start + 20: + print("Failed to read custom metric data, retrying...") + time.sleep(3) + else: + print("Failed to read custom metric data, aborting: " + "exception={}".format(e)) + raise + + +def get_client(): + """Builds an http client authenticated with the application default + credentials.""" + credentials = GoogleCredentials.get_application_default() + client = discovery.build( + 'cloudmonitoring', 'v2beta2', + credentials=credentials) + return client + + +def main(project_id, color, size, count): + now_rfc3339 = get_now_rfc3339() + + client = get_client() + + print ("Labels: color: {}, size: {}.".format(color, size)) + print ("Creating custom metric...") + create_custom_metric(client, project_id) + time.sleep(2) + print ("Writing new data to custom metric timeseries...") + write_custom_metric(client, project_id, now_rfc3339, + color, size, count) + print ("Reading data from custom metric timeseries...") + write_custom_metric(client, project_id, now_rfc3339, color, + size, count) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + + parser.add_argument( + '--project_id', help='Project ID you want to access.', required=True) + parser.add_argument("--color", required=True) + parser.add_argument("--size", required=True) + parser.add_argument("--count", required=True) + + args = parser.parse_args() + main(args.project_id, args.color, args.size, args.count) diff --git a/monitoring/api/labeled_custom_metric_test.py b/monitoring/api/labeled_custom_metric_test.py new file mode 100644 index 000000000000..f564eef0fd5d --- /dev/null +++ b/monitoring/api/labeled_custom_metric_test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re + +import labeled_custom_metric + + +def test_main(cloud_config, capsys): + labeled_custom_metric.main(cloud_config.project, "yellow", "large", "10") + output, _ = capsys.readouterr() + + assert re.search( + re.compile( + r'.*Creating.*' + r'Writing.*' + r'Reading.*', flags=re.DOTALL), + output) diff --git a/monitoring/api/lightweight_custom_metric.py b/monitoring/api/lightweight_custom_metric.py new file mode 100644 index 000000000000..b2fc550f781f --- /dev/null +++ b/monitoring/api/lightweight_custom_metric.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Writes and reads a lightweight custom metric. + +This is an example of how to use the Google Cloud Monitoring API to write +and read a lightweight custom metric. Lightweight custom metrics have no +labels and you do not need to create a metric descriptor for them. + +Prerequisites: To run locally, download a Service Account JSON file from +your project and point GOOGLE_APPLICATION_CREDENTIALS to the file. + +From App Engine or a GCE instance with the correct scope, the Service +Account step is not required. + +Typical usage: Run the following shell commands on the instance: + + python lightweight_custom_metric.py --project_id= +""" + +import argparse +import datetime +import os +import time + +from googleapiclient import discovery +from oauth2client.client import GoogleCredentials + +CUSTOM_METRIC_NAME = "custom.cloudmonitoring.googleapis.com/pid" + + +def format_rfc3339(datetime_instance=None): + """Formats a datetime per RFC 3339. + :param datetime_instance: Datetime instanec to format, defaults to utcnow + """ + return datetime_instance.isoformat("T") + "Z" + + +def get_now_rfc3339(): + return format_rfc3339(datetime.datetime.utcnow()) + + +def get_client(): + """Builds an http client authenticated with the service account + credentials.""" + credentials = GoogleCredentials.get_application_default() + client = discovery.build( + 'cloudmonitoring', 'v2beta2', + credentials=credentials) + return client + + +def main(project_id): + # Set up the write request. + client = get_client() + now = get_now_rfc3339() + desc = {"project": project_id, + "metric": CUSTOM_METRIC_NAME} + point = {"start": now, + "end": now, + "doubleValue": os.getpid()} + print("Writing {} at {}".format(point["doubleValue"], now)) + + # Write a new data point. + try: + write_request = client.timeseries().write( + project=project_id, + body={"timeseries": [{"timeseriesDesc": desc, "point": point}]}) + write_request.execute() # Ignore the response. + except Exception as e: + print("Failed to write custom metric data: exception={}".format(e)) + raise + + # Read all data points from the time series. + # When a custom metric is created, it may take a few seconds + # to propagate throughout the system. Retry a few times. + print("Reading data from custom metric timeseries...") + read_request = client.timeseries().list( + project=project_id, + metric=CUSTOM_METRIC_NAME, + youngest=now) + start = time.time() + while True: + try: + read_response = read_request.execute() + for point in read_response["timeseries"][0]["points"]: + print("Point: {}: {}".format( + point["end"], point["doubleValue"])) + break + except Exception as e: + if time.time() < start + 20: + print("Failed to read custom metric data, retrying...") + time.sleep(3) + else: + print("Failed to read custom metric data, aborting: " + "exception={}".format(e)) + raise + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + parser.add_argument( + '--project_id', help='Project ID you want to access.', required=True) + + args = parser.parse_args() + main(args.project_id) diff --git a/monitoring/api/lightweight_custom_metric_test.py b/monitoring/api/lightweight_custom_metric_test.py new file mode 100644 index 000000000000..f8c0598a61b1 --- /dev/null +++ b/monitoring/api/lightweight_custom_metric_test.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re + +import lightweight_custom_metric + + +def test_main(cloud_config, capsys): + lightweight_custom_metric.main(cloud_config.project) + output, _ = capsys.readouterr() + + assert re.search( + re.compile( + r'Point:\s*'), + output)