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