prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  tests.internal.runtime.test_runtime_metrics.TestRuntimeWorker.test_worker_metrics fails randomly
  ```
      def test_worker_metrics(self):
          self.tracer.configure(collect_metrics=True)
      
          with self.override_global_tracer(self.tracer):
              self.tracer._dogstatsd_client = DogStatsd()
              self.tracer._dogstatsd_client.socket = FakeSocket()
      
              root = self.start_span('parent', service='parent')
              context = root.context
              self.start_span('child', service='child', child_of=context)
      
              self.worker = RuntimeWorker(self.tracer._dogstatsd_client)
              self.worker.start()
              self.worker.stop()
      
              # get all received metrics
              received = []
              while True:
                  new = self.tracer._dogstatsd_client.socket.recv()
                  if not new:
                      break
      
                  received.append(new)
                  # DEV: sleep since metrics will still be getting collected and written
                  time.sleep(.5)
      
              # expect received all default metrics
  >           self.assertEqual(len(received), len(DEFAULT_RUNTIME_METRICS))
  E           AssertionError: 0 != 10
  
  tests/internal/runtime/test_runtime_metrics.py:75: AssertionError
  ```
  
  https://circleci.com/gh/DataDog/dd-trace-py/114364
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `ddtrace/internal/runtime/runtime_metrics.py`
  Content:
  ```
  1 import threading
  2 import time
  3 import itertools
  4 
  5 from ..logger import get_logger
  6 from .constants import (
  7     DEFAULT_RUNTIME_METRICS,
  8     DEFAULT_RUNTIME_TAGS,
  9 )
  10 from .metric_collectors import (
  11     GCRuntimeMetricCollector,
  12     PSUtilRuntimeMetricCollector,
  13 )
  14 from .tag_collectors import (
  15     TracerTagCollector,
  16 )
  17 
  18 log = get_logger(__name__)
  19 
  20 
  21 class RuntimeCollectorsIterable(object):
  22     def __init__(self, enabled=None):
  23         self._enabled = enabled or self.ENABLED
  24         # Initialize the collectors.
  25         self._collectors = [c() for c in self.COLLECTORS]
  26 
  27     def __iter__(self):
  28         collected = (
  29             collector.collect(self._enabled)
  30             for collector in self._collectors
  31         )
  32         return itertools.chain.from_iterable(collected)
  33 
  34     def __repr__(self):
  35         return '{}(enabled={})'.format(
  36             self.__class__.__name__,
  37             self._enabled,
  38         )
  39 
  40 
  41 class RuntimeTags(RuntimeCollectorsIterable):
  42     ENABLED = DEFAULT_RUNTIME_TAGS
  43     COLLECTORS = [
  44         TracerTagCollector,
  45     ]
  46 
  47 
  48 class RuntimeMetrics(RuntimeCollectorsIterable):
  49     ENABLED = DEFAULT_RUNTIME_METRICS
  50     COLLECTORS = [
  51         GCRuntimeMetricCollector,
  52         PSUtilRuntimeMetricCollector,
  53     ]
  54 
  55 
  56 class RuntimeWorker(object):
  57     """ Worker thread for collecting and writing runtime metrics to a DogStatsd
  58         client.
  59     """
  60 
  61     FLUSH_INTERVAL = 10
  62 
  63     def __init__(self, statsd_client, flush_interval=None):
  64         self._stay_alive = None
  65         self._thread = None
  66         self._flush_interval = flush_interval or self.FLUSH_INTERVAL
  67         self._statsd_client = statsd_client
  68         self._runtime_metrics = RuntimeMetrics()
  69 
  70     def _target(self):
  71         while self._stay_alive:
  72             self.flush()
  73             time.sleep(self._flush_interval)
  74 
  75     def start(self):
  76         if not self._thread:
  77             log.debug('Starting {}'.format(self))
  78             self._stay_alive = True
  79             self._thread = threading.Thread(target=self._target)
  80             self._thread.setDaemon(True)
  81             self._thread.start()
  82 
  83     def stop(self):
  84         if self._thread and self._stay_alive:
  85             log.debug('Stopping {}'.format(self))
  86             self._stay_alive = False
  87 
  88     def _write_metric(self, key, value):
  89         log.debug('Writing metric {}:{}'.format(key, value))
  90         self._statsd_client.gauge(key, value)
  91 
  92     def flush(self):
  93         if not self._statsd_client:
  94             log.warn('Attempted flush with uninitialized or failed statsd client')
  95             return
  96 
  97         for key, value in self._runtime_metrics:
  98             self._write_metric(key, value)
  99 
  100     def reset(self):
  101         self._runtime_metrics = RuntimeMetrics()
  102 
  103     def __repr__(self):
  104         return '{}(runtime_metrics={})'.format(
  105             self.__class__.__name__,
  106             self._runtime_metrics,
  107         )
  108
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/ddtrace/internal/runtime/runtime_metrics.py b/ddtrace/internal/runtime/runtime_metrics.py
  --- a/ddtrace/internal/runtime/runtime_metrics.py
  +++ b/ddtrace/internal/runtime/runtime_metrics.py
  @@ -60,10 +60,10 @@
   
       FLUSH_INTERVAL = 10
   
  -    def __init__(self, statsd_client, flush_interval=None):
  +    def __init__(self, statsd_client, flush_interval=FLUSH_INTERVAL):
           self._stay_alive = None
           self._thread = None
  -        self._flush_interval = flush_interval or self.FLUSH_INTERVAL
  +        self._flush_interval = flush_interval
           self._statsd_client = statsd_client
           self._runtime_metrics = RuntimeMetrics()
   
  @@ -85,6 +85,10 @@
               log.debug('Stopping {}'.format(self))
               self._stay_alive = False
   
  +    def join(self, timeout=None):
  +        if self._thread:
  +            return self._thread.join(timeout)
  +
       def _write_metric(self, key, value):
           log.debug('Writing metric {}:{}'.format(key, value))
           self._statsd_client.gauge(key, value)
  
verification_info: {"golden_diff": "diff --git a/ddtrace/internal/runtime/runtime_metrics.py b/ddtrace/internal/runtime/runtime_metrics.py\n--- a/ddtrace/internal/runtime/runtime_metrics.py\n+++ b/ddtrace/internal/runtime/runtime_metrics.py\n@@ -60,10 +60,10 @@\n \n     FLUSH_INTERVAL = 10\n \n-    def __init__(self, statsd_client, flush_interval=None):\n+    def __init__(self, statsd_client, flush_interval=FLUSH_INTERVAL):\n         self._stay_alive = None\n         self._thread = None\n-        self._flush_interval = flush_interval or self.FLUSH_INTERVAL\n+        self._flush_interval = flush_interval\n         self._statsd_client = statsd_client\n         self._runtime_metrics = RuntimeMetrics()\n \n@@ -85,6 +85,10 @@\n             log.debug('Stopping {}'.format(self))\n             self._stay_alive = False\n \n+    def join(self, timeout=None):\n+        if self._thread:\n+            return self._thread.join(timeout)\n+\n     def _write_metric(self, key, value):\n         log.debug('Writing metric {}:{}'.format(key, value))\n         self._statsd_client.gauge(key, value)\n", "issue": "tests.internal.runtime.test_runtime_metrics.TestRuntimeWorker.test_worker_metrics fails randomly\n```\r\n    def test_worker_metrics(self):\r\n        self.tracer.configure(collect_metrics=True)\r\n    \r\n        with self.override_global_tracer(self.tracer):\r\n            self.tracer._dogstatsd_client = DogStatsd()\r\n            self.tracer._dogstatsd_client.socket = FakeSocket()\r\n    \r\n            root = self.start_span('parent', service='parent')\r\n            context = root.context\r\n            self.start_span('child', service='child', child_of=context)\r\n    \r\n            self.worker = RuntimeWorker(self.tracer._dogstatsd_client)\r\n            self.worker.start()\r\n            self.worker.stop()\r\n    \r\n            # get all received metrics\r\n            received = []\r\n            while True:\r\n                new = self.tracer._dogstatsd_client.socket.recv()\r\n                if not new:\r\n                    break\r\n    \r\n                received.append(new)\r\n                # DEV: sleep since metrics will still be getting collected and written\r\n                time.sleep(.5)\r\n    \r\n            # expect received all default metrics\r\n>           self.assertEqual(len(received), len(DEFAULT_RUNTIME_METRICS))\r\nE           AssertionError: 0 != 10\r\n\r\ntests/internal/runtime/test_runtime_metrics.py:75: AssertionError\r\n```\r\n\r\nhttps://circleci.com/gh/DataDog/dd-trace-py/114364\n", "before_files": [{"content": "import threading\nimport time\nimport itertools\n\nfrom ..logger import get_logger\nfrom .constants import (\n    DEFAULT_RUNTIME_METRICS,\n    DEFAULT_RUNTIME_TAGS,\n)\nfrom .metric_collectors import (\n    GCRuntimeMetricCollector,\n    PSUtilRuntimeMetricCollector,\n)\nfrom .tag_collectors import (\n    TracerTagCollector,\n)\n\nlog = get_logger(__name__)\n\n\nclass RuntimeCollectorsIterable(object):\n    def __init__(self, enabled=None):\n        self._enabled = enabled or self.ENABLED\n        # Initialize the collectors.\n        self._collectors = [c() for c in self.COLLECTORS]\n\n    def __iter__(self):\n        collected = (\n            collector.collect(self._enabled)\n            for collector in self._collectors\n        )\n        return itertools.chain.from_iterable(collected)\n\n    def __repr__(self):\n        return '{}(enabled={})'.format(\n            self.__class__.__name__,\n            self._enabled,\n        )\n\n\nclass RuntimeTags(RuntimeCollectorsIterable):\n    ENABLED = DEFAULT_RUNTIME_TAGS\n    COLLECTORS = [\n        TracerTagCollector,\n    ]\n\n\nclass RuntimeMetrics(RuntimeCollectorsIterable):\n    ENABLED = DEFAULT_RUNTIME_METRICS\n    COLLECTORS = [\n        GCRuntimeMetricCollector,\n        PSUtilRuntimeMetricCollector,\n    ]\n\n\nclass RuntimeWorker(object):\n    \"\"\" Worker thread for collecting and writing runtime metrics to a DogStatsd\n        client.\n    \"\"\"\n\n    FLUSH_INTERVAL = 10\n\n    def __init__(self, statsd_client, flush_interval=None):\n        self._stay_alive = None\n        self._thread = None\n        self._flush_interval = flush_interval or self.FLUSH_INTERVAL\n        self._statsd_client = statsd_client\n        self._runtime_metrics = RuntimeMetrics()\n\n    def _target(self):\n        while self._stay_alive:\n            self.flush()\n            time.sleep(self._flush_interval)\n\n    def start(self):\n        if not self._thread:\n            log.debug('Starting {}'.format(self))\n            self._stay_alive = True\n            self._thread = threading.Thread(target=self._target)\n            self._thread.setDaemon(True)\n            self._thread.start()\n\n    def stop(self):\n        if self._thread and self._stay_alive:\n            log.debug('Stopping {}'.format(self))\n            self._stay_alive = False\n\n    def _write_metric(self, key, value):\n        log.debug('Writing metric {}:{}'.format(key, value))\n        self._statsd_client.gauge(key, value)\n\n    def flush(self):\n        if not self._statsd_client:\n            log.warn('Attempted flush with uninitialized or failed statsd client')\n            return\n\n        for key, value in self._runtime_metrics:\n            self._write_metric(key, value)\n\n    def reset(self):\n        self._runtime_metrics = RuntimeMetrics()\n\n    def __repr__(self):\n        return '{}(runtime_metrics={})'.format(\n            self.__class__.__name__,\n            self._runtime_metrics,\n        )\n", "path": "ddtrace/internal/runtime/runtime_metrics.py"}], "after_files": [{"content": "import threading\nimport time\nimport itertools\n\nfrom ..logger import get_logger\nfrom .constants import (\n    DEFAULT_RUNTIME_METRICS,\n    DEFAULT_RUNTIME_TAGS,\n)\nfrom .metric_collectors import (\n    GCRuntimeMetricCollector,\n    PSUtilRuntimeMetricCollector,\n)\nfrom .tag_collectors import (\n    TracerTagCollector,\n)\n\nlog = get_logger(__name__)\n\n\nclass RuntimeCollectorsIterable(object):\n    def __init__(self, enabled=None):\n        self._enabled = enabled or self.ENABLED\n        # Initialize the collectors.\n        self._collectors = [c() for c in self.COLLECTORS]\n\n    def __iter__(self):\n        collected = (\n            collector.collect(self._enabled)\n            for collector in self._collectors\n        )\n        return itertools.chain.from_iterable(collected)\n\n    def __repr__(self):\n        return '{}(enabled={})'.format(\n            self.__class__.__name__,\n            self._enabled,\n        )\n\n\nclass RuntimeTags(RuntimeCollectorsIterable):\n    ENABLED = DEFAULT_RUNTIME_TAGS\n    COLLECTORS = [\n        TracerTagCollector,\n    ]\n\n\nclass RuntimeMetrics(RuntimeCollectorsIterable):\n    ENABLED = DEFAULT_RUNTIME_METRICS\n    COLLECTORS = [\n        GCRuntimeMetricCollector,\n        PSUtilRuntimeMetricCollector,\n    ]\n\n\nclass RuntimeWorker(object):\n    \"\"\" Worker thread for collecting and writing runtime metrics to a DogStatsd\n        client.\n    \"\"\"\n\n    FLUSH_INTERVAL = 10\n\n    def __init__(self, statsd_client, flush_interval=FLUSH_INTERVAL):\n        self._stay_alive = None\n        self._thread = None\n        self._flush_interval = flush_interval\n        self._statsd_client = statsd_client\n        self._runtime_metrics = RuntimeMetrics()\n\n    def _target(self):\n        while self._stay_alive:\n            self.flush()\n            time.sleep(self._flush_interval)\n\n    def start(self):\n        if not self._thread:\n            log.debug('Starting {}'.format(self))\n            self._stay_alive = True\n            self._thread = threading.Thread(target=self._target)\n            self._thread.setDaemon(True)\n            self._thread.start()\n\n    def stop(self):\n        if self._thread and self._stay_alive:\n            log.debug('Stopping {}'.format(self))\n            self._stay_alive = False\n\n    def join(self, timeout=None):\n        if self._thread:\n            return self._thread.join(timeout)\n\n    def _write_metric(self, key, value):\n        log.debug('Writing metric {}:{}'.format(key, value))\n        self._statsd_client.gauge(key, value)\n\n    def flush(self):\n        if not self._statsd_client:\n            log.warn('Attempted flush with uninitialized or failed statsd client')\n            return\n\n        for key, value in self._runtime_metrics:\n            self._write_metric(key, value)\n\n    def reset(self):\n        self._runtime_metrics = RuntimeMetrics()\n\n    def __repr__(self):\n        return '{}(runtime_metrics={})'.format(\n            self.__class__.__name__,\n            self._runtime_metrics,\n        )\n", "path": "ddtrace/internal/runtime/runtime_metrics.py"}]}