You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by as...@apache.org on 2021/02/26 14:46:07 UTC

[airflow] branch master updated: Fix statsd metrics not sending when using daemon mode (#14454)

This is an automated email from the ASF dual-hosted git repository.

ash pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/master by this push:
     new 0aa597e  Fix statsd metrics not sending when using daemon mode (#14454)
0aa597e is described below

commit 0aa597e2ffd71d3587f629c0a1cb3d904e07b6e6
Author: Jun <Ju...@users.noreply.github.com>
AuthorDate: Fri Feb 26 22:45:56 2021 +0800

    Fix statsd metrics not sending when using daemon mode (#14454)
    
    It seems that the daemonContext will close the socket of statsd.
    
    ```
        return self.statsd.incr(stat, count, rate)
      File "/usr/local/lib/python3.8/site-packages/statsd/client/base.py", line 35, in incr
        self._send_stat(stat, '%s|c' % count, rate)
      File "/usr/local/lib/python3.8/site-packages/statsd/client/base.py", line 59, in _send_stat
        self._after(self._prepare(stat, value, rate))
      File "/usr/local/lib/python3.8/site-packages/statsd/client/base.py", line 74, in _after
        self._send(data)
      File "/opt/airflow/airflow/stats.py", line 40, in _send
        self._sock.sendto(data.encode('ascii'), self._addr)
    OSError: [Errno 9] Bad file descriptor
    ```
---
 airflow/stats.py         | 27 +++++++++++++++------------
 tests/core/test_stats.py |  1 +
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/airflow/stats.py b/airflow/stats.py
index 7472647..8207220 100644
--- a/airflow/stats.py
+++ b/airflow/stats.py
@@ -348,25 +348,28 @@ class SafeDogStatsdLogger:
 
 
 class _Stats(type):
+    factory = None
     instance: Optional[StatsLogger] = None
 
     def __getattr__(cls, name):
+        if not cls.instance:
+            try:
+                cls.instance = cls.factory()
+            except (socket.gaierror, ImportError) as e:
+                log.error("Could not configure StatsClient: %s, using DummyStatsLogger instead.", e)
+                cls.instance = DummyStatsLogger()
         return getattr(cls.instance, name)
 
     def __init__(cls, *args, **kwargs):
         super().__init__(cls)
-        if cls.__class__.instance is None:
-            try:
-                is_datadog_enabled_defined = conf.has_option('metrics', 'statsd_datadog_enabled')
-                if is_datadog_enabled_defined and conf.getboolean('metrics', 'statsd_datadog_enabled'):
-                    cls.__class__.instance = cls.get_dogstatsd_logger()
-                elif conf.getboolean('metrics', 'statsd_on'):
-                    cls.__class__.instance = cls.get_statsd_logger()
-                else:
-                    cls.__class__.instance = DummyStatsLogger()
-            except (socket.gaierror, ImportError) as e:
-                log.error("Could not configure StatsClient: %s, using DummyStatsLogger instead.", e)
-                cls.__class__.instance = DummyStatsLogger()
+        if cls.__class__.factory is None:
+            is_datadog_enabled_defined = conf.has_option('metrics', 'statsd_datadog_enabled')
+            if is_datadog_enabled_defined and conf.getboolean('metrics', 'statsd_datadog_enabled'):
+                cls.__class__.factory = cls.get_dogstatsd_logger
+            elif conf.getboolean('metrics', 'statsd_on'):
+                cls.__class__.factory = cls.get_statsd_logger
+            else:
+                cls.__class__.factory = DummyStatsLogger
 
     @classmethod
     def get_statsd_logger(cls):
diff --git a/tests/core/test_stats.py b/tests/core/test_stats.py
index 428192b..b635e62 100644
--- a/tests/core/test_stats.py
+++ b/tests/core/test_stats.py
@@ -136,6 +136,7 @@ class TestStats(unittest.TestCase):
             ),
         ):
             importlib.reload(airflow.stats)
+            airflow.stats.Stats.incr("dummy_key")
 
     def tearDown(self) -> None:
         # To avoid side-effect