Skip to content

Commit

Permalink
Resources prototype (#853)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewAXue authored and Alex Boten committed Jul 7, 2020
1 parent 69f9114 commit aefdfd9
Show file tree
Hide file tree
Showing 17 changed files with 617 additions and 16 deletions.
37 changes: 37 additions & 0 deletions docs/examples/basic_tracer/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Basic Trace
===========

These examples show how to use OpenTelemetry to create and export Spans.

There are two different examples:

* basic_trace: Shows how to configure a SpanProcessor and Exporter, and how to create a tracer and span.

* resources: Shows how to add resource information to a Provider. Note that this must be run on a Google Compute Engine instance.

The source files of these examples are available :scm_web:`here <docs/examples/basic_tracer/>`.

Installation
------------

.. code-block:: sh
pip install opentelemetry-api
pip install opentelemetry-sdk
Run the Example
---------------

.. code-block:: sh
python <example_name>.py
The output will be shown in the console.

Useful links
------------

- OpenTelemetry_
- :doc:`../../api/trace`

.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/
14 changes: 14 additions & 0 deletions docs/examples/basic_tracer/basic_trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleExportSpanProcessor,
)

trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
SimpleExportSpanProcessor(ConsoleSpanExporter())
)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("foo"):
print("Hello world!")
19 changes: 19 additions & 0 deletions docs/examples/basic_tracer/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from opentelemetry import trace
from opentelemetry.sdk.resources import get_aggregated_resources
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleExportSpanProcessor,
)
from opentelemetry.tools.resource_detector import GoogleCloudResourceDetector

resources = get_aggregated_resources([GoogleCloudResourceDetector()])

trace.set_tracer_provider(TracerProvider(resource=resources))

trace.get_tracer_provider().add_span_processor(
SimpleExportSpanProcessor(ConsoleSpanExporter())
)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("foo"):
print("Hello world!")
3 changes: 3 additions & 0 deletions ext/opentelemetry-exporter-cloud-monitoring/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Add support for resources
([#853](https://github.com/open-telemetry/opentelemetry-python/pull/853))

## Version 0.10b0

Released 2020-06-23
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import google.auth
from google.api.label_pb2 import LabelDescriptor
from google.api.metric_pb2 import MetricDescriptor
from google.api.monitored_resource_pb2 import MonitoredResource
from google.cloud.monitoring_v3 import MetricServiceClient
from google.cloud.monitoring_v3.proto.metric_pb2 import TimeSeries

Expand All @@ -14,12 +15,21 @@
MetricsExportResult,
)
from opentelemetry.sdk.metrics.export.aggregate import SumAggregator
from opentelemetry.sdk.resources import Resource

logger = logging.getLogger(__name__)
MAX_BATCH_WRITE = 200
WRITE_INTERVAL = 10
UNIQUE_IDENTIFIER_KEY = "opentelemetry_id"

OT_RESOURCE_LABEL_TO_GCP = {
"gce_instance": {
"cloud.account.id": "project_id",
"host.id": "instance_id",
"cloud.zone": "zone",
}
}


# pylint is unable to resolve members of protobuf objects
# pylint: disable=no-member
Expand Down Expand Up @@ -56,13 +66,33 @@ def __init__(
random.randint(0, 16 ** 8)
)

def _add_resource_info(self, series: TimeSeries) -> None:
@staticmethod
def _get_monitored_resource(
resource: Resource,
) -> Optional[MonitoredResource]:
"""Add Google resource specific information (e.g. instance id, region).
See
https://cloud.google.com/monitoring/custom-metrics/creating-metrics#custom-metric-resources
for supported types
Args:
series: ProtoBuf TimeSeries
"""
# TODO: Leverage this better

if resource.labels.get("cloud.provider") != "gcp":
return None
resource_type = resource.labels["gcp.resource_type"]
if resource_type not in OT_RESOURCE_LABEL_TO_GCP:
return None
return MonitoredResource(
type=resource_type,
labels={
gcp_label: str(resource.labels[ot_label])
for ot_label, gcp_label in OT_RESOURCE_LABEL_TO_GCP[
resource_type
].items()
},
)

def _batch_write(self, series: TimeSeries) -> None:
""" Cloud Monitoring allows writing up to 200 time series at once
Expand Down Expand Up @@ -162,9 +192,11 @@ def export(
metric_descriptor = self._get_metric_descriptor(record)
if not metric_descriptor:
continue

series = TimeSeries()
self._add_resource_info(series)
series = TimeSeries(
resource=self._get_monitored_resource(
record.instrument.meter.resource
)
)
series.metric.type = metric_descriptor.type
for key, value in record.labels:
series.metric.labels[key] = str(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from google.api.label_pb2 import LabelDescriptor
from google.api.metric_pb2 import MetricDescriptor
from google.api.monitored_resource_pb2 import MonitoredResource
from google.cloud.monitoring_v3.proto.metric_pb2 import TimeSeries

from opentelemetry.exporter.cloud_monitoring import (
Expand All @@ -27,17 +28,30 @@
)
from opentelemetry.sdk.metrics.export import MetricRecord
from opentelemetry.sdk.metrics.export.aggregate import SumAggregator
from opentelemetry.sdk.resources import Resource


class UnsupportedAggregator:
pass


class MockMeter:
def __init__(self, resource=Resource.create_empty()):
self.resource = resource


class MockMetric:
def __init__(self, name="name", description="description", value_type=int):
def __init__(
self,
name="name",
description="description",
value_type=int,
meter=None,
):
self.name = name
self.description = description
self.value_type = value_type
self.meter = meter or MockMeter()


# pylint: disable=protected-access
Expand Down Expand Up @@ -205,24 +219,41 @@ def test_export(self):
}
)

resource = Resource(
labels={
"cloud.account.id": 123,
"host.id": "host",
"cloud.zone": "US",
"cloud.provider": "gcp",
"extra_info": "extra",
"gcp.resource_type": "gce_instance",
"not_gcp_resource": "value",
}
)

sum_agg_one = SumAggregator()
sum_agg_one.checkpoint = 1
sum_agg_one.last_update_timestamp = (WRITE_INTERVAL + 1) * 1e9
exporter.export(
[
MetricRecord(
MockMetric(),
MockMetric(meter=MockMeter(resource=resource)),
(("label1", "value1"), ("label2", 1),),
sum_agg_one,
),
MetricRecord(
MockMetric(),
MockMetric(meter=MockMeter(resource=resource)),
(("label1", "value2"), ("label2", 2),),
sum_agg_one,
),
]
)
series1 = TimeSeries()
expected_resource = MonitoredResource(
type="gce_instance",
labels={"project_id": "123", "instance_id": "host", "zone": "US"},
)

series1 = TimeSeries(resource=expected_resource)
series1.metric.type = "custom.googleapis.com/OpenTelemetry/name"
series1.metric.labels["label1"] = "value1"
series1.metric.labels["label2"] = "1"
Expand All @@ -231,7 +262,7 @@ def test_export(self):
point.interval.end_time.seconds = WRITE_INTERVAL + 1
point.interval.end_time.nanos = 0

series2 = TimeSeries()
series2 = TimeSeries(resource=expected_resource)
series2.metric.type = "custom.googleapis.com/OpenTelemetry/name"
series2.metric.labels["label1"] = "value2"
series2.metric.labels["label2"] = "2"
Expand Down Expand Up @@ -342,3 +373,64 @@ def test_unique_identifier(self):
first_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY],
second_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY],
)

def test_extract_resources(self):
exporter = CloudMonitoringMetricsExporter(project_id=self.project_id)

self.assertIsNone(
exporter._get_monitored_resource(Resource.create_empty())
)
resource = Resource(
labels={
"cloud.account.id": 123,
"host.id": "host",
"cloud.zone": "US",
"cloud.provider": "gcp",
"extra_info": "extra",
"gcp.resource_type": "gce_instance",
"not_gcp_resource": "value",
}
)
expected_extract = MonitoredResource(
type="gce_instance",
labels={"project_id": "123", "instance_id": "host", "zone": "US"},
)
self.assertEqual(
exporter._get_monitored_resource(resource), expected_extract
)

resource = Resource(
labels={
"cloud.account.id": "123",
"host.id": "host",
"extra_info": "extra",
"not_gcp_resource": "value",
"gcp.resource_type": "gce_instance",
"cloud.provider": "gcp",
}
)
# Should throw when passed a malformed GCP resource dict
self.assertRaises(KeyError, exporter._get_monitored_resource, resource)

resource = Resource(
labels={
"cloud.account.id": "123",
"host.id": "host",
"extra_info": "extra",
"not_gcp_resource": "value",
"gcp.resource_type": "unsupported_gcp_resource",
"cloud.provider": "gcp",
}
)
self.assertIsNone(exporter._get_monitored_resource(resource))

resource = Resource(
labels={
"cloud.account.id": "123",
"host.id": "host",
"extra_info": "extra",
"not_gcp_resource": "value",
"cloud.provider": "aws",
}
)
self.assertIsNone(exporter._get_monitored_resource(resource))
3 changes: 3 additions & 0 deletions ext/opentelemetry-exporter-cloud-trace/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Add support for resources and resource detector
([#853](https://github.com/open-telemetry/opentelemetry-python/pull/853))

## Version 0.10b0

Released 2020-06-23
Expand Down
1 change: 1 addition & 0 deletions ext/opentelemetry-exporter-cloud-trace/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ package_dir=
=src
packages=find_namespace:
install_requires =
requests
opentelemetry-api
opentelemetry-sdk
google-cloud-trace
Expand Down
Loading

0 comments on commit aefdfd9

Please sign in to comment.