Repository: open-telemetry/opentelemetry-python

Issue:
Add suppress_instrumentation flag in context for Metrics
Similar to [logic](https://github.com/open-telemetry/opentelemetry-python/blob/master/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py#L205) in SpanProcessors, this need to be done in Metrics to avoid duplicated telemetry when using Http ext or other packages relying on this


Discussion:
Curious as to how we are going to handle the case of when dependency calls being made at the same time `export` is being called. With this logic, calls during when `suppress_instrumentation`  flag is true (during export) will not be [tracked](https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-http-requests/src/opentelemetry/ext/http_requests/__init__.py#L56).
EDIT: It seems like context is local per thread so that use case won't be an issue.

Resolving patch:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/controller.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/controller.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/controller.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/controller.py
@@ -15,6 +15,8 @@
 import atexit
 import threading
 
+from opentelemetry.context import attach, detach, set_value
+
 
 class PushController(threading.Thread):
     """A push based controller, used for exporting.
@@ -50,7 +52,9 @@ def shutdown(self):
     def tick(self):
         # Collect all of the meter's metrics to be exported
         self.meter.collect()
+        token = attach(set_value("suppress_instrumentation", True))
         # Export the given metrics in the batcher
         self.exporter.export(self.meter.batcher.checkpoint_set())
+        detach(token)
         # Perform post-exporting logic based on batcher configuration
         self.meter.batcher.finished_collection()


Test patch:
diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py
--- a/opentelemetry-sdk/tests/metrics/export/test_export.py
+++ b/opentelemetry-sdk/tests/metrics/export/test_export.py
@@ -17,6 +17,7 @@
 import unittest
 from unittest import mock
 
+from opentelemetry.context import get_value
 from opentelemetry.sdk import metrics
 from opentelemetry.sdk.metrics.export import (
     ConsoleMetricsExporter,
@@ -511,3 +512,18 @@ def test_push_controller(self):
         controller.shutdown()
         self.assertTrue(controller.finished.isSet())
         exporter.shutdown.assert_any_call()
+
+    def test_push_controller_suppress_instrumentation(self):
+        meter = mock.Mock()
+        exporter = mock.Mock()
+        exporter.export = lambda x: self.assertIsNotNone(
+            get_value("suppress_instrumentation")
+        )
+        with mock.patch(
+            "opentelemetry.context._RUNTIME_CONTEXT"
+        ) as context_patch:
+            controller = PushController(meter, exporter, 30.0)
+            controller.tick()
+            self.assertEqual(context_patch.attach.called, True)
+            self.assertEqual(context_patch.detach.called, True)
+        self.assertEqual(get_value("suppress_instrumentation"), None)