Repository: prometheus/client_python
Pull request #22 (2015-04-06 11:59:30)

Add support to allow for using pids from pidfiles.


Patch:
diff --git a/README.md b/README.md
index 9c016c1..bbc8b89 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,12 @@ The Python Client automatically exports metrics about process CPU usage, RAM,
 file descriptors and start time. These all have the prefix `process_`, and
 are only currently available on Linux.
 
+The namespace and pid constructor arguments allows for exporting metrics about
+other processes, for example:
+```
+ProcessCollector(namespace='mydaemon', pid=lambda: open('/var/run/daemon.pid').read())
+```
+
 ## Exporting
 
 There are several options for exporting metrics.
diff --git a/prometheus_client/__init__.py b/prometheus_client/__init__.py
index c57c8c7..6f668ac 100644
--- a/prometheus_client/__init__.py
+++ b/prometheus_client/__init__.py
@@ -452,9 +452,9 @@ def write_to_textfile(path, registry):
 
 class ProcessCollector(object):
     """Collector for Standard Exports such as cpu and memory."""
-    def __init__(self, namespace='', pid='self', proc='/proc', registry=REGISTRY):
+    def __init__(self, namespace='', pid=lambda: 'self', proc='/proc', registry=REGISTRY):
         self._namespace = namespace
-        self._pid = os.path.join(proc, str(pid))
+        self._pid = pid
         self._proc = proc
         self._pagesize = resource.getpagesize()
         if namespace:
@@ -486,9 +486,16 @@ class ProcessCollector(object):
         if not self._btime:
             return []
 
+        try:
+          pid = os.path.join(self._proc, str(self._pid()).strip())
+        except:
+          # File likely didn't exist, fail silently.
+          raise
+          return []
+
         result = []
         try:
-            with open(os.path.join(self._pid, 'stat')) as stat:
+            with open(os.path.join(pid, 'stat')) as stat:
                 parts = (stat.read().split(')')[-1].split())
             vmem = Metric(self._prefix + 'virtual_memory_bytes', 'Virtual memory size in bytes', 'gauge')
             vmem.add_sample(self._prefix + 'virtual_memory_bytes', {}, float(parts[20]))
@@ -509,13 +516,13 @@ class ProcessCollector(object):
 
         try:
             max_fds = Metric(self._prefix + 'max_fds', 'Maximum number of open file descriptors.', 'gauge')
-            with open(os.path.join(self._pid, 'limits')) as limits:
+            with open(os.path.join(pid, 'limits')) as limits:
                 for line in limits:
                     if line.startswith('Max open file'):
                         max_fds.add_sample(self._prefix + 'max_fds', {}, float(line.split()[3]))
                         break
             open_fds = Metric(self._prefix + 'open_fds', 'Number of open file descriptors.', 'gauge')
-            open_fds.add_sample(self._prefix + 'open_fds', {}, len(os.listdir(os.path.join(self._pid, 'fd'))))
+            open_fds.add_sample(self._prefix + 'open_fds', {}, len(os.listdir(os.path.join(pid, 'fd'))))
             result.extend([open_fds, max_fds])
         except IOError:
             pass


Test patch:
diff --git a/tests/test_client.py b/tests/test_client.py
index a7d353f..3b113cb 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -286,7 +286,7 @@ class TestProcessCollector(unittest.TestCase):
         self.test_proc = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'proc')
 
     def test_working(self):
-        collector = ProcessCollector(proc=self.test_proc, pid=26231, registry=self.registry)
+        collector = ProcessCollector(proc=self.test_proc, pid=lambda: 26231, registry=self.registry)
         collector._pagesize = 4096
         collector._ticks = 100
 
@@ -299,7 +299,7 @@ class TestProcessCollector(unittest.TestCase):
         self.assertEqual(None, self.registry.get_sample_value('process_fake_namespace'))
 
     def test_namespace(self):
-        collector = ProcessCollector(proc=self.test_proc, pid=26231, registry=self.registry, namespace='n')
+        collector = ProcessCollector(proc=self.test_proc, pid=lambda: 26231, registry=self.registry, namespace='n')
         collector._pagesize = 4096
         collector._ticks = 100
 
@@ -312,7 +312,7 @@ class TestProcessCollector(unittest.TestCase):
         self.assertEqual(None, self.registry.get_sample_value('process_cpu_seconds_total'))
 
     def test_working_584(self):
-        collector = ProcessCollector(proc=self.test_proc, pid=584, registry=self.registry)
+        collector = ProcessCollector(proc=self.test_proc, pid=lambda: "584\n", registry=self.registry)
         collector._pagesize = 4096
         collector._ticks = 100
 
@@ -324,7 +324,7 @@ class TestProcessCollector(unittest.TestCase):
         self.assertEqual(None, self.registry.get_sample_value('process_open_fds'))
 
     def test_working_fake_pid(self):
-        collector = ProcessCollector(proc=self.test_proc, pid=123, registry=self.registry)
+        collector = ProcessCollector(proc=self.test_proc, pid=lambda: 123, registry=self.registry)
         collector._pagesize = 4096
         collector._ticks = 100