Skip to content

Commit 80c31ea

Browse files
committed
Merge pull request #19 from brian-brazil/standard-exports
Standard exports
2 parents 13739f1 + efbabea commit 80c31ea

File tree

13 files changed

+183
-3
lines changed

13 files changed

+183
-3
lines changed

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ Maintainers of this repository:
55
The following individuals have contributed code to this repository
66
(listed in alphabetical order):
77

8+
* Andrea Fagan <[email protected]>
89
* Brian Brazil <[email protected]>

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ c.labels('get', '/').inc()
133133
c.labels('post', '/submit').inc()
134134
```
135135

136+
### Process Collector
137+
138+
The Python Client automatically exports metrics about process CPU usage, RAM,
139+
file descriptors and start time. These all have the prefix `process\_`, and
140+
are only currently available on Linux.
141+
136142
## Exporting
137143

138144
There are several options for exporting metrics.
@@ -180,4 +186,5 @@ g.set(1)
180186
write_to_textfile('/configured/textfile/path/raid.prom', registry)
181187
```
182188

183-
A separate registry is used, as the default registry may contain other metrics.
189+
A separate registry is used, as the default registry may contain other metrics
190+
such as those from the Process Collector.

prometheus_client/__init__.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import copy
66
import re
7+
import resource
78
import os
89
import time
910
import threading
@@ -419,7 +420,7 @@ def generate_latest(registry=REGISTRY):
419420
labelstr = '{{{0}}}'.format(','.join(
420421
['{0}="{1}"'.format(
421422
k, v.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"'))
422-
for k, v in labels.items()]))
423+
for k, v in sorted(labels.items())]))
423424
else:
424425
labelstr = ''
425426
output.append('{0}{1} {2}\n'.format(name, labelstr, _floatToGoString(value)))
@@ -446,6 +447,83 @@ def write_to_textfile(path, registry):
446447
os.rename(tmppath, path)
447448

448449

450+
class ProcessCollector(object):
451+
"""Collector for Standard Exports such as cpu and memory."""
452+
def __init__(self, namespace='', pid='self', proc='/proc', registry=REGISTRY):
453+
self._namespace = namespace
454+
self._pid = os.path.join(proc, str(pid))
455+
self._proc = proc
456+
self._pagesize = resource.getpagesize()
457+
if namespace:
458+
self._prefix = namespace + '_process_'
459+
else:
460+
self._prefix = 'process_'
461+
self._ticks = 100.0
462+
try:
463+
self._ticks = os.sysconf('SC_CLK_TCK')
464+
except (ValueError, TypeError):
465+
pass
466+
467+
# This is used to test if we can access /proc.
468+
self._btime = 0
469+
try:
470+
self._btime = self._boot_time()
471+
except IOError:
472+
pass
473+
if registry:
474+
registry.register(self)
475+
476+
def _boot_time(self):
477+
with open(os.path.join(self._proc, 'stat')) as stat:
478+
for line in stat:
479+
if line.startswith('btime '):
480+
return float(line.split()[1])
481+
482+
def collect(self):
483+
if not self._btime:
484+
return []
485+
486+
result = []
487+
try:
488+
with open(os.path.join(self._pid, 'stat')) as stat:
489+
parts = (stat.read().split(')')[-1].split())
490+
vmem = Metric(self._prefix + 'virtual_memory_bytes', 'Virtual memory size in bytes', 'gauge')
491+
vmem.add_sample(self._prefix + 'virtual_memory_bytes', {}, float(parts[20]))
492+
rss = Metric(self._prefix + 'resident_memory_bytes', 'Resident memory size in bytes', 'gauge')
493+
rss.add_sample(self._prefix + 'resident_memory_bytes', {}, float(parts[21]) * self._pagesize)
494+
start_time = Metric(self._prefix + 'start_time_seconds',
495+
'Start time of the process since unix epoch in seconds.', 'gauge')
496+
start_time_secs = float(parts[19]) / self._ticks
497+
start_time.add_sample(self._prefix + 'start_time_seconds',{} , start_time_secs + self._btime)
498+
utime = float(parts[11]) / self._ticks
499+
stime = float(parts[12]) / self._ticks
500+
cpu = Metric(self._prefix + 'cpu_seconds_total',
501+
'Total user and system CPU time spent in seconds.', 'counter')
502+
cpu.add_sample(self._prefix + 'cpu_seconds_total', {}, utime + stime)
503+
result.extend([vmem, rss, start_time, cpu])
504+
except IOError:
505+
pass
506+
507+
try:
508+
max_fds = Metric(self._prefix + 'max_fds', 'Maximum number of open file descriptors.', 'gauge')
509+
with open(os.path.join(self._pid, 'limits')) as limits:
510+
for line in limits:
511+
if line.startswith('Max open file'):
512+
max_fds.add_sample(self._prefix + 'max_fds', {}, float(line.split()[3]))
513+
break
514+
open_fds = Metric(self._prefix + 'open_fds', 'Number of open file descriptors.', 'gauge')
515+
open_fds.add_sample(self._prefix + 'open_fds', {}, len(os.listdir(os.path.join(self._pid, 'fd'))))
516+
result.extend([open_fds, max_fds])
517+
except IOError:
518+
pass
519+
520+
return result
521+
522+
523+
PROCESS_COLLECTOR = ProcessCollector()
524+
"""Default ProcessCollector in default Registry REGISTRY."""
525+
526+
449527
if __name__ == '__main__':
450528
c = Counter('cc', 'A counter')
451529
c.inc()

tests/proc/26231/fd/0

Whitespace-only changes.

tests/proc/26231/fd/1

Whitespace-only changes.

tests/proc/26231/fd/2

Whitespace-only changes.

tests/proc/26231/fd/3

Whitespace-only changes.

tests/proc/26231/fd/4

Whitespace-only changes.

tests/proc/26231/limits

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Limit Soft Limit Hard Limit Units
2+
Max cpu time unlimited unlimited seconds
3+
Max file size unlimited unlimited bytes
4+
Max data size unlimited unlimited bytes
5+
Max stack size 8388608 unlimited bytes
6+
Max core file size 0 unlimited bytes
7+
Max resident set unlimited unlimited bytes
8+
Max processes 62898 62898 processes
9+
Max open files 2048 4096 files
10+
Max locked memory 65536 65536 bytes
11+
Max address space unlimited unlimited bytes
12+
Max file locks unlimited unlimited locks
13+
Max pending signals 62898 62898 signals
14+
Max msgqueue size 819200 819200 bytes
15+
Max nice priority 0 0

tests/proc/26231/stat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
26231 (vim) R 5392 7446 5392 34835 7446 4218880 32533 309516 26 82 1677 44 158 99 20 0 1 0 82375 56274944 1981 18446744073709551615 4194304 6294284 140736914091744 140736914087944 139965136429984 0 0 12288 1870679807 0 0 0 17 0 0 0 31 0 0 8391624 8481048 16420864 140736914093252 140736914093279 140736914093279 140736914096107 0

0 commit comments

Comments
 (0)