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,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+
449527if __name__ == '__main__' :
450528 c = Counter ('cc' , 'A counter' )
451529 c .inc ()
0 commit comments