Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3efe854

Browse files
authored
OC Exporter - send start_timestamp, resource labels, and convert labels to strings (open-telemetry#937)
1 parent cffc016 commit 3efe854

File tree

3 files changed

+155
-11
lines changed

3 files changed

+155
-11
lines changed

exporter/opentelemetry-exporter-opencensus/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- Change package name to opentelemetry-exporter-opencensus
66
([#953](https://github.com/open-telemetry/opentelemetry-python/pull/953))
7+
- Send start_timestamp and convert labels to strings
8+
([#937](https://github.com/open-telemetry/opentelemetry-python/pull/937))
79

810
## 0.8b0
911

exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/metrics_exporter/__init__.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515
"""OpenCensus Collector Metrics Exporter."""
1616

1717
import logging
18-
from typing import Sequence
18+
from typing import Dict, Sequence
1919

2020
import grpc
21+
from google.protobuf.timestamp_pb2 import Timestamp
2122
from opencensus.proto.agent.metrics.v1 import (
2223
metrics_service_pb2,
2324
metrics_service_pb2_grpc,
2425
)
2526
from opencensus.proto.metrics.v1 import metrics_pb2
27+
from opencensus.proto.resource.v1 import resource_pb2
2628

2729
import opentelemetry.exporter.opencensus.util as utils
2830
from opentelemetry.sdk.metrics import Counter, Metric
@@ -34,6 +36,14 @@
3436

3537
DEFAULT_ENDPOINT = "localhost:55678"
3638

39+
# In priority order. See collector impl https://bit.ly/2DvJW6y
40+
_OT_LABEL_PRESENCE_TO_RESOURCE_TYPE = (
41+
("container.name", "container"),
42+
("k8s.pod.name", "k8s"),
43+
("host.name", "host"),
44+
("cloud.provider", "cloud"),
45+
)
46+
3747
logger = logging.getLogger(__name__)
3848

3949

@@ -65,6 +75,8 @@ def __init__(
6575
self.client = client
6676

6777
self.node = utils.get_node(service_name, host_name)
78+
self.exporter_start_timestamp = Timestamp()
79+
self.exporter_start_timestamp.GetCurrentTime()
6880

6981
def export(
7082
self, metric_records: Sequence[MetricRecord]
@@ -89,7 +101,9 @@ def shutdown(self) -> None:
89101
def generate_metrics_requests(
90102
self, metrics: Sequence[MetricRecord]
91103
) -> metrics_service_pb2.ExportMetricsServiceRequest:
92-
collector_metrics = translate_to_collector(metrics)
104+
collector_metrics = translate_to_collector(
105+
metrics, self.exporter_start_timestamp
106+
)
93107
service_request = metrics_service_pb2.ExportMetricsServiceRequest(
94108
node=self.node, metrics=collector_metrics
95109
)
@@ -99,6 +113,7 @@ def generate_metrics_requests(
99113
# pylint: disable=too-many-branches
100114
def translate_to_collector(
101115
metric_records: Sequence[MetricRecord],
116+
exporter_start_timestamp: Timestamp,
102117
) -> Sequence[metrics_pb2.Metric]:
103118
collector_metrics = []
104119
for metric_record in metric_records:
@@ -109,7 +124,8 @@ def translate_to_collector(
109124
label_keys.append(metrics_pb2.LabelKey(key=label_tuple[0]))
110125
label_values.append(
111126
metrics_pb2.LabelValue(
112-
has_value=label_tuple[1] is not None, value=label_tuple[1]
127+
has_value=label_tuple[1] is not None,
128+
value=str(label_tuple[1]),
113129
)
114130
)
115131

@@ -121,13 +137,23 @@ def translate_to_collector(
121137
label_keys=label_keys,
122138
)
123139

140+
# If cumulative and stateful, explicitly set the start_timestamp to
141+
# exporter start time.
142+
if metric_record.instrument.meter.batcher.stateful:
143+
start_timestamp = exporter_start_timestamp
144+
else:
145+
start_timestamp = None
146+
124147
timeseries = metrics_pb2.TimeSeries(
125148
label_values=label_values,
126149
points=[get_collector_point(metric_record)],
150+
start_timestamp=start_timestamp,
127151
)
128152
collector_metrics.append(
129153
metrics_pb2.Metric(
130-
metric_descriptor=metric_descriptor, timeseries=[timeseries]
154+
metric_descriptor=metric_descriptor,
155+
timeseries=[timeseries],
156+
resource=get_resource(metric_record),
131157
)
132158
)
133159
return collector_metrics
@@ -162,3 +188,22 @@ def get_collector_point(metric_record: MetricRecord) -> metrics_pb2.Point:
162188
)
163189
)
164190
return point
191+
192+
193+
def get_resource(metric_record: MetricRecord) -> resource_pb2.Resource:
194+
resource_labels = metric_record.instrument.meter.resource.labels
195+
return resource_pb2.Resource(
196+
type=infer_oc_resource_type(resource_labels),
197+
labels={k: str(v) for k, v in resource_labels.items()},
198+
)
199+
200+
201+
def infer_oc_resource_type(resource_labels: Dict[str, str]) -> str:
202+
"""Convert from OT resource labels to OC resource type"""
203+
for (
204+
ot_resource_key,
205+
oc_resource_type,
206+
) in _OT_LABEL_PRESENCE_TO_RESOURCE_TYPE:
207+
if ot_resource_key in resource_labels:
208+
return oc_resource_type
209+
return ""

exporter/opentelemetry-exporter-opencensus/tests/test_otcollector_metrics_exporter.py

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,24 @@
3232
MetricsExportResult,
3333
aggregate,
3434
)
35+
from opentelemetry.sdk.resources import Resource
3536

3637

3738
# pylint: disable=no-member
3839
class TestCollectorMetricsExporter(unittest.TestCase):
3940
@classmethod
4041
def setUpClass(cls):
4142
# pylint: disable=protected-access
42-
metrics.set_meter_provider(MeterProvider())
43+
cls._resource_labels = {
44+
"key_with_str_value": "some string",
45+
"key_with_int_val": 321,
46+
"key_with_true": True,
47+
}
48+
metrics.set_meter_provider(
49+
MeterProvider(resource=Resource(cls._resource_labels))
50+
)
4351
cls._meter = metrics.get_meter(__name__)
44-
cls._labels = {"environment": "staging"}
52+
cls._labels = {"environment": "staging", "number": 321}
4553
cls._key_labels = get_dict_as_key(cls._labels)
4654

4755
def test_constructor(self):
@@ -119,7 +127,7 @@ def test_export(self):
119127
client=mock_client, host_name=host_name
120128
)
121129
test_metric = self._meter.create_metric(
122-
"testname", "testdesc", "unit", int, Counter,
130+
"testname", "testdesc", "unit", int, Counter, self._labels.keys(),
123131
)
124132
record = MetricRecord(
125133
test_metric, self._key_labels, aggregate.SumAggregator(),
@@ -142,13 +150,16 @@ def test_export(self):
142150

143151
def test_translate_to_collector(self):
144152
test_metric = self._meter.create_metric(
145-
"testname", "testdesc", "unit", int, Counter,
153+
"testname", "testdesc", "unit", int, Counter, self._labels.keys()
146154
)
147155
aggregator = aggregate.SumAggregator()
148156
aggregator.update(123)
149157
aggregator.take_checkpoint()
150158
record = MetricRecord(test_metric, self._key_labels, aggregator,)
151-
output_metrics = metrics_exporter.translate_to_collector([record])
159+
start_timestamp = Timestamp()
160+
output_metrics = metrics_exporter.translate_to_collector(
161+
[record], start_timestamp,
162+
)
152163
self.assertEqual(len(output_metrics), 1)
153164
self.assertIsInstance(output_metrics[0], metrics_pb2.Metric)
154165
self.assertEqual(output_metrics[0].metric_descriptor.name, "testname")
@@ -161,14 +172,44 @@ def test_translate_to_collector(self):
161172
metrics_pb2.MetricDescriptor.CUMULATIVE_INT64,
162173
)
163174
self.assertEqual(
164-
len(output_metrics[0].metric_descriptor.label_keys), 1
175+
len(output_metrics[0].metric_descriptor.label_keys), 2
165176
)
166177
self.assertEqual(
167178
output_metrics[0].metric_descriptor.label_keys[0].key,
168179
"environment",
169180
)
181+
self.assertEqual(
182+
output_metrics[0].metric_descriptor.label_keys[1].key, "number",
183+
)
184+
185+
self.assertIsNotNone(output_metrics[0].resource)
186+
self.assertEqual(
187+
output_metrics[0].resource.type, "",
188+
)
189+
self.assertEqual(
190+
output_metrics[0].resource.labels["key_with_str_value"],
191+
self._resource_labels["key_with_str_value"],
192+
)
193+
self.assertIsInstance(
194+
output_metrics[0].resource.labels["key_with_int_val"], str,
195+
)
196+
self.assertEqual(
197+
output_metrics[0].resource.labels["key_with_int_val"],
198+
str(self._resource_labels["key_with_int_val"]),
199+
)
200+
self.assertIsInstance(
201+
output_metrics[0].resource.labels["key_with_true"], str,
202+
)
203+
self.assertEqual(
204+
output_metrics[0].resource.labels["key_with_true"],
205+
str(self._resource_labels["key_with_true"]),
206+
)
207+
170208
self.assertEqual(len(output_metrics[0].timeseries), 1)
171-
self.assertEqual(len(output_metrics[0].timeseries[0].label_values), 1)
209+
self.assertEqual(len(output_metrics[0].timeseries[0].label_values), 2)
210+
self.assertEqual(
211+
output_metrics[0].timeseries[0].start_timestamp, start_timestamp
212+
)
172213
self.assertEqual(
173214
output_metrics[0].timeseries[0].label_values[0].has_value, True
174215
)
@@ -187,3 +228,59 @@ def test_translate_to_collector(self):
187228
self.assertEqual(
188229
output_metrics[0].timeseries[0].points[0].int64_value, 123
189230
)
231+
232+
def test_infer_ot_resource_type(self):
233+
# empty resource
234+
self.assertEqual(metrics_exporter.infer_oc_resource_type({}), "")
235+
236+
# container
237+
self.assertEqual(
238+
metrics_exporter.infer_oc_resource_type(
239+
{
240+
"k8s.cluster.name": "cluster1",
241+
"k8s.pod.name": "pod1",
242+
"k8s.namespace.name": "namespace1",
243+
"container.name": "container-name1",
244+
"cloud.account.id": "proj1",
245+
"cloud.zone": "zone1",
246+
}
247+
),
248+
"container",
249+
)
250+
251+
# k8s pod
252+
self.assertEqual(
253+
metrics_exporter.infer_oc_resource_type(
254+
{
255+
"k8s.cluster.name": "cluster1",
256+
"k8s.pod.name": "pod1",
257+
"k8s.namespace.name": "namespace1",
258+
"cloud.zone": "zone1",
259+
}
260+
),
261+
"k8s",
262+
)
263+
264+
# host
265+
self.assertEqual(
266+
metrics_exporter.infer_oc_resource_type(
267+
{
268+
"k8s.cluster.name": "cluster1",
269+
"cloud.zone": "zone1",
270+
"host.name": "node1",
271+
}
272+
),
273+
"host",
274+
)
275+
276+
# cloud
277+
self.assertEqual(
278+
metrics_exporter.infer_oc_resource_type(
279+
{
280+
"cloud.provider": "gcp",
281+
"host.id": "inst1",
282+
"cloud.zone": "zone1",
283+
}
284+
),
285+
"cloud",
286+
)

0 commit comments

Comments
 (0)