-
Notifications
You must be signed in to change notification settings - Fork 711
Add cloud monitoring exporter #739
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
toumorokoshi
merged 23 commits into
open-telemetry:master
from
AndrewAXue:cloud_monitoring
Jun 9, 2020
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
7c38720
update changelog
ceb0517
f
6aeea7f
initial commit
3b62a24
lint
d024eff
add first draft example
1e46194
move examples
d48f369
finish exporter
c58718f
f
758bcf2
f
b2d2e3b
address comments
8b5e343
add tests
f98f31a
f
2821882
revert
6ebd1e5
address comments
00524af
Update docs/examples/cloud_monitoring/README.rst
AndrewAXue 9e3c596
Update docs/examples/cloud_monitoring/README.rst
AndrewAXue 320e6db
Update docs/examples/cloud_monitoring/basic_metrics.py
AndrewAXue cd64112
Update docs/examples/cloud_monitoring/basic_metrics.py
AndrewAXue 27f35ee
Update ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/…
AndrewAXue 053790e
Update ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/…
AndrewAXue 86c5157
Update ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_m…
AndrewAXue c75a907
Merge branch 'master' into cloud_monitoring
AndrewAXue 11b6bb5
Merge branch 'master' into cloud_monitoring
toumorokoshi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,3 +22,4 @@ wrapt>=1.0.0,<2.0.0 | |
psutil~=5.7.0 | ||
boto~=2.0 | ||
google-cloud-trace >=0.23.0 | ||
google-cloud-monitoring>=0.36.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
Cloud Monitoring Exporter Example | ||
================================= | ||
|
||
These examples show how to use OpenTelemetry to send metrics data to Cloud Monitoring. | ||
|
||
|
||
Basic Example | ||
------------- | ||
|
||
To use this exporter you first need to: | ||
* `Create a Google Cloud project <https://console.cloud.google.com/projectcreate>`_. | ||
* Enable the Cloud Monitoring API (aka Stackdriver Monitoring API) in the project `here <https://console.cloud.google.com/apis/library?q=cloud_monitoring>`_. | ||
* Enable `Default Application Credentials <https://developers.google.com/identity/protocols/application-default-credentials>`_. | ||
|
||
* Installation | ||
|
||
.. code-block:: sh | ||
|
||
pip install opentelemetry-api | ||
pip install opentelemetry-sdk | ||
pip install opentelemetry-exporter-cloud-monitoring | ||
|
||
* Run example | ||
|
||
.. code-block:: sh | ||
|
||
python basic_metrics.py | ||
|
||
Viewing Output | ||
-------------------------- | ||
|
||
After running the example: | ||
* Go to the `Cloud Monitoring Metrics Explorer page <https://console.cloud.google.com/monitoring/metrics-explorer>`_. | ||
* In "Find resource type and metric" enter "OpenTelemetry/request_counter". | ||
* You can filter by labels and change the graphical output here as well. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright The OpenTelemetry Authors | ||
# | ||
# 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 time | ||
|
||
from opentelemetry import metrics | ||
from opentelemetry.exporter.cloud_monitoring import ( | ||
CloudMonitoringMetricsExporter, | ||
) | ||
from opentelemetry.sdk.metrics import Counter, MeterProvider | ||
from opentelemetry.sdk.metrics.export.controller import PushController | ||
|
||
meter = metrics.get_meter(__name__, True) | ||
|
||
# Gather and export metrics every 5 seconds | ||
controller = PushController( | ||
meter=meter, exporter=CloudMonitoringMetricsExporter(), interval=5 | ||
) | ||
|
||
requests_counter = meter.create_metric( | ||
name="request_counter", | ||
description="number of requests", | ||
unit="1", | ||
value_type=int, | ||
metric_type=Counter, | ||
label_keys=("environment"), | ||
) | ||
|
||
staging_labels = {"environment": "staging"} | ||
|
||
for i in range(20): | ||
requests_counter.add(25, staging_labels) | ||
time.sleep(10) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
OpenTelemetry Cloud Monitoring Exporter | ||
======================================= | ||
|
||
.. automodule:: opentelemetry.exporter.cloud_monitoring | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
OpenTelemetry Cloud Monitoring Exporters | ||
======================================== | ||
|
||
This library provides classes for exporting metrics data to Google Cloud Monitoring. | ||
|
||
Installation | ||
------------ | ||
|
||
:: | ||
|
||
pip install opentelemetry-exporter-cloud-monitoring | ||
|
||
References | ||
---------- | ||
|
||
* `OpenTelemetry Cloud Monitoring Exporter <https://opentelemetry-python.readthedocs.io/en/latest/ext/cloud_monitoring/cloud_monitoring.html>`_ | ||
* `Cloud Monitoring <https://cloud.google.com/monitoring/>`_ | ||
* `OpenTelemetry Project <https://opentelemetry.io/>`_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,48 @@ | ||||||
|
||||||
# Copyright OpenTelemetry Authors | ||||||
# | ||||||
# 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. | ||||||
# | ||||||
[metadata] | ||||||
name = opentelemetry-exporter-cloud-monitoring | ||||||
description = Cloud Monitoring integration for OpenTelemetry | ||||||
long_description = file: README.rst | ||||||
long_description_content_type = text/x-rst | ||||||
author = OpenTelemetry Authors | ||||||
author_email = [email protected] | ||||||
url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-exporter-cloud-monitoring | ||||||
platforms = any | ||||||
license = Apache-2.0 | ||||||
classifiers = | ||||||
Development Status :: 4 - Beta | ||||||
Intended Audience :: Developers | ||||||
License :: OSI Approved :: Apache Software License | ||||||
Programming Language :: Python | ||||||
Programming Language :: Python :: 3 | ||||||
Programming Language :: Python :: 3.4 | ||||||
Programming Language :: Python :: 3.5 | ||||||
Programming Language :: Python :: 3.6 | ||||||
Programming Language :: Python :: 3.7 | ||||||
|
||||||
[options] | ||||||
python_requires = >=3.4 | ||||||
package_dir= | ||||||
=src | ||||||
packages=find_namespace: | ||||||
install_requires = | ||||||
opentelemetry-api | ||||||
opentelemetry-sdk | ||||||
google-cloud-monitoring | ||||||
|
||||||
[options.packages.find] | ||||||
where = src | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your commit suggestion has no change, can you clarify? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Copyright OpenTelemetry Authors | ||
# | ||
# 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 os | ||
|
||
import setuptools | ||
|
||
BASE_DIR = os.path.dirname(__file__) | ||
VERSION_FILENAME = os.path.join( | ||
BASE_DIR, | ||
"src", | ||
"opentelemetry", | ||
"exporter", | ||
"cloud_monitoring", | ||
"version.py", | ||
) | ||
PACKAGE_INFO = {} | ||
with open(VERSION_FILENAME) as f: | ||
exec(f.read(), PACKAGE_INFO) | ||
|
||
setuptools.setup(version=PACKAGE_INFO["__version__"]) |
169 changes: 169 additions & 0 deletions
169
...lemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import logging | ||
from typing import Optional, Sequence | ||
|
||
import google.auth | ||
from google.api.label_pb2 import LabelDescriptor | ||
from google.api.metric_pb2 import MetricDescriptor | ||
from google.cloud.monitoring_v3 import MetricServiceClient | ||
from google.cloud.monitoring_v3.proto.metric_pb2 import TimeSeries | ||
|
||
from opentelemetry.sdk.metrics.export import ( | ||
MetricRecord, | ||
MetricsExporter, | ||
MetricsExportResult, | ||
) | ||
from opentelemetry.sdk.metrics.export.aggregate import CounterAggregator | ||
|
||
logger = logging.getLogger(__name__) | ||
MAX_BATCH_WRITE = 200 | ||
WRITE_INTERVAL = 10 | ||
|
||
|
||
# pylint is unable to resolve members of protobuf objects | ||
# pylint: disable=no-member | ||
class CloudMonitoringMetricsExporter(MetricsExporter): | ||
""" Implementation of Metrics Exporter to Google Cloud Monitoring""" | ||
|
||
def __init__(self, project_id=None, client=None): | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.client = client or MetricServiceClient() | ||
if not project_id: | ||
_, self.project_id = google.auth.default() | ||
else: | ||
self.project_id = project_id | ||
self.project_name = self.client.project_path(self.project_id) | ||
self._metric_descriptors = {} | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self._last_updated = {} | ||
|
||
def _add_resource_info(self, series: TimeSeries) -> None: | ||
"""Add Google resource specific information (e.g. instance id, region). | ||
|
||
Args: | ||
series: ProtoBuf TimeSeries | ||
""" | ||
# TODO: Leverage this better | ||
|
||
def _batch_write(self, series: TimeSeries) -> None: | ||
""" Cloud Monitoring allows writing up to 200 time series at once | ||
|
||
:param series: ProtoBuf TimeSeries | ||
:return: | ||
""" | ||
write_ind = 0 | ||
while write_ind < len(series): | ||
self.client.create_time_series( | ||
self.project_name, | ||
series[write_ind : write_ind + MAX_BATCH_WRITE], | ||
) | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
write_ind += MAX_BATCH_WRITE | ||
|
||
def _get_metric_descriptor( | ||
self, record: MetricRecord | ||
) -> Optional[MetricDescriptor]: | ||
""" We can map Metric to MetricDescriptor using Metric.name or | ||
MetricDescriptor.type. We create the MetricDescriptor if it doesn't | ||
exist already and cache it. Note that recreating MetricDescriptors is | ||
a no-op if it already exists. | ||
|
||
:param record: | ||
:return: | ||
""" | ||
descriptor_type = "custom.googleapis.com/OpenTelemetry/{}".format( | ||
record.metric.name | ||
) | ||
if descriptor_type in self._metric_descriptors: | ||
return self._metric_descriptors[descriptor_type] | ||
descriptor = { | ||
"name": None, | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"type": descriptor_type, | ||
"display_name": record.metric.name, | ||
"description": record.metric.description, | ||
"labels": [], | ||
} | ||
for key, value in record.labels: | ||
if isinstance(value, str): | ||
descriptor["labels"].append( | ||
LabelDescriptor(key=key, value_type="STRING") | ||
) | ||
elif isinstance(value, bool): | ||
descriptor["labels"].append( | ||
LabelDescriptor(key=key, value_type="BOOL") | ||
) | ||
elif isinstance(value, int): | ||
descriptor["labels"].append( | ||
LabelDescriptor(key=key, value_type="INT64") | ||
) | ||
else: | ||
logger.warning( | ||
"Label value %s is not a string, bool or integer", value | ||
) | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if isinstance(record.aggregator, CounterAggregator): | ||
descriptor["metric_kind"] = MetricDescriptor.MetricKind.GAUGE | ||
else: | ||
logger.warning( | ||
"Unsupported aggregation type %s, ignoring it", | ||
type(record.aggregator).__name__, | ||
) | ||
return None | ||
if record.metric.value_type == int: | ||
descriptor["value_type"] = MetricDescriptor.ValueType.INT64 | ||
elif record.metric.value_type == float: | ||
descriptor["value_type"] = MetricDescriptor.ValueType.DOUBLE | ||
proto_descriptor = MetricDescriptor(**descriptor) | ||
try: | ||
descriptor = self.client.create_metric_descriptor( | ||
self.project_name, proto_descriptor | ||
) | ||
# pylint: disable=broad-except | ||
except Exception as ex: | ||
logger.error( | ||
"Failed to create metric descriptor %s", | ||
proto_descriptor, | ||
exc_info=ex, | ||
) | ||
return None | ||
self._metric_descriptors[descriptor_type] = descriptor | ||
return descriptor | ||
|
||
def export( | ||
self, metric_records: Sequence[MetricRecord] | ||
) -> "MetricsExportResult": | ||
all_series = [] | ||
for record in metric_records: | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
metric_descriptor = self._get_metric_descriptor(record) | ||
if not metric_descriptor: | ||
continue | ||
|
||
series = TimeSeries() | ||
self._add_resource_info(series) | ||
series.metric.type = metric_descriptor.type | ||
for key, value in record.labels: | ||
series.metric.labels[key] = str(value) | ||
|
||
point = series.points.add() | ||
if record.metric.value_type == int: | ||
point.value.int64_value = record.aggregator.checkpoint | ||
elif record.metric.value_type == float: | ||
point.value.double_value = record.aggregator.checkpoint | ||
seconds, nanos = divmod( | ||
record.aggregator.last_update_timestamp, 1e9 | ||
) | ||
|
||
# Cloud Monitoring API allows, for any combination of labels and | ||
# metric name, one update per WRITE_INTERVAL seconds | ||
updated_key = (metric_descriptor.type, record.labels) | ||
last_updated_seconds = self._last_updated.get(updated_key, 0) | ||
if seconds <= last_updated_seconds + WRITE_INTERVAL: | ||
continue | ||
self._last_updated[updated_key] = seconds | ||
point.interval.end_time.seconds = int(seconds) | ||
point.interval.end_time.nanos = int(nanos) | ||
all_series.append(series) | ||
try: | ||
self._batch_write(all_series) | ||
# pylint: disable=broad-except | ||
except Exception as ex: | ||
logger.error( | ||
"Error while writing to Cloud Monitoring", exc_info=ex | ||
) | ||
return MetricsExportResult.FAILURE | ||
return MetricsExportResult.SUCCESS | ||
AndrewAXue marked this conversation as resolved.
Show resolved
Hide resolved
|
15 changes: 15 additions & 0 deletions
15
...elemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/version.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright OpenTelemetry Authors | ||
# | ||
# 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. | ||
|
||
__version__ = "0.9.dev0" |
Empty file.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.