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

Skip to content

Commit d35e626

Browse files
author
Andrew Xue
authored
Add unique identifier to Cloud Monitoring exporter (open-telemetry#841)
1 parent 3c9f99d commit d35e626

File tree

3 files changed

+102
-3
lines changed

3 files changed

+102
-3
lines changed

docs/examples/cloud_monitoring/README.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,16 @@ After running the example:
3333
* Go to the `Cloud Monitoring Metrics Explorer page <https://console.cloud.google.com/monitoring/metrics-explorer>`_.
3434
* In "Find resource type and metric" enter "OpenTelemetry/request_counter".
3535
* You can filter by labels and change the graphical output here as well.
36+
37+
Troubleshooting
38+
--------------------------
39+
40+
``One or more points were written more frequently than the maximum sampling period configured for the metric``
41+
##############################################################################################################
42+
43+
Currently, Cloud Monitoring allows one write every 10 seconds for any unique tuple (metric_name, metric_label_value_1, metric_label_value_2, ...). The exporter should rate limit on its own but issues arise if:
44+
45+
* You are restarting the server more than once every 10 seconds.
46+
* You have a multiple exporters (possibly on different threads) writing to the same tuple.
47+
48+
For both cases, you can pass ``add_unique_identifier=True`` to the CloudMonitoringMetricsExporter constructor. This adds a UUID label_value, making the tuple unique again. For the first case, you can also choose to just wait longer than 10 seconds between restarts.

ext/opentelemetry-exporter-cloud-monitoring/src/opentelemetry/exporter/cloud_monitoring/__init__.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import random
23
from typing import Optional, Sequence
34

45
import google.auth
@@ -17,14 +18,30 @@
1718
logger = logging.getLogger(__name__)
1819
MAX_BATCH_WRITE = 200
1920
WRITE_INTERVAL = 10
21+
UNIQUE_IDENTIFIER_KEY = "opentelemetry_id"
2022

2123

2224
# pylint is unable to resolve members of protobuf objects
2325
# pylint: disable=no-member
2426
class CloudMonitoringMetricsExporter(MetricsExporter):
25-
""" Implementation of Metrics Exporter to Google Cloud Monitoring"""
26-
27-
def __init__(self, project_id=None, client=None):
27+
""" Implementation of Metrics Exporter to Google Cloud Monitoring
28+
29+
You can manually pass in project_id and client, or else the
30+
Exporter will take that information from Application Default
31+
Credentials.
32+
33+
Args:
34+
project_id: project id of your Google Cloud project.
35+
client: Client to upload metrics to Google Cloud Monitoring.
36+
add_unique_identifier: Add an identifier to each exporter metric. This
37+
must be used when there exist two (or more) exporters that may
38+
export to the same metric name within WRITE_INTERVAL seconds of
39+
each other.
40+
"""
41+
42+
def __init__(
43+
self, project_id=None, client=None, add_unique_identifier=False
44+
):
2845
self.client = client or MetricServiceClient()
2946
if not project_id:
3047
_, self.project_id = google.auth.default()
@@ -33,6 +50,11 @@ def __init__(self, project_id=None, client=None):
3350
self.project_name = self.client.project_path(self.project_id)
3451
self._metric_descriptors = {}
3552
self._last_updated = {}
53+
self.unique_identifier = None
54+
if add_unique_identifier:
55+
self.unique_identifier = "{:08x}".format(
56+
random.randint(0, 16 ** 8)
57+
)
3658

3759
def _add_resource_info(self, series: TimeSeries) -> None:
3860
"""Add Google resource specific information (e.g. instance id, region).
@@ -97,6 +119,12 @@ def _get_metric_descriptor(
97119
logger.warning(
98120
"Label value %s is not a string, bool or integer", value
99121
)
122+
123+
if self.unique_identifier:
124+
descriptor["labels"].append(
125+
LabelDescriptor(key=UNIQUE_IDENTIFIER_KEY, value_type="STRING")
126+
)
127+
100128
if isinstance(record.aggregator, SumAggregator):
101129
descriptor["metric_kind"] = MetricDescriptor.MetricKind.GAUGE
102130
else:
@@ -141,6 +169,11 @@ def export(
141169
for key, value in record.labels:
142170
series.metric.labels[key] = str(value)
143171

172+
if self.unique_identifier:
173+
series.metric.labels[
174+
UNIQUE_IDENTIFIER_KEY
175+
] = self.unique_identifier
176+
144177
point = series.points.add()
145178
if instrument.value_type == int:
146179
point.value.int64_value = record.aggregator.checkpoint

ext/opentelemetry-exporter-cloud-monitoring/tests/test_cloud_monitoring.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from opentelemetry.exporter.cloud_monitoring import (
2323
MAX_BATCH_WRITE,
24+
UNIQUE_IDENTIFIER_KEY,
2425
WRITE_INTERVAL,
2526
CloudMonitoringMetricsExporter,
2627
)
@@ -289,3 +290,55 @@ def test_export(self):
289290
mock.call(self.project_name, [series3]),
290291
]
291292
)
293+
294+
def test_unique_identifier(self):
295+
client = mock.Mock()
296+
exporter1 = CloudMonitoringMetricsExporter(
297+
project_id=self.project_id,
298+
client=client,
299+
add_unique_identifier=True,
300+
)
301+
exporter2 = CloudMonitoringMetricsExporter(
302+
project_id=self.project_id,
303+
client=client,
304+
add_unique_identifier=True,
305+
)
306+
exporter1.project_name = self.project_name
307+
exporter2.project_name = self.project_name
308+
309+
client.create_metric_descriptor.return_value = MetricDescriptor(
310+
**{
311+
"name": None,
312+
"type": "custom.googleapis.com/OpenTelemetry/name",
313+
"display_name": "name",
314+
"description": "description",
315+
"labels": [
316+
LabelDescriptor(
317+
key=UNIQUE_IDENTIFIER_KEY, value_type="STRING"
318+
),
319+
],
320+
"metric_kind": "GAUGE",
321+
"value_type": "DOUBLE",
322+
}
323+
)
324+
325+
sum_agg_one = SumAggregator()
326+
sum_agg_one.update(1)
327+
metric_record = MetricRecord(MockMetric(), (), sum_agg_one,)
328+
exporter1.export([metric_record])
329+
exporter2.export([metric_record])
330+
331+
(
332+
first_call,
333+
second_call,
334+
) = client.create_metric_descriptor.call_args_list
335+
self.assertEqual(first_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY)
336+
self.assertEqual(
337+
second_call[0][1].labels[0].key, UNIQUE_IDENTIFIER_KEY
338+
)
339+
340+
first_call, second_call = client.create_time_series.call_args_list
341+
self.assertNotEqual(
342+
first_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY],
343+
second_call[0][1][0].metric.labels[UNIQUE_IDENTIFIER_KEY],
344+
)

0 commit comments

Comments
 (0)