NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

cpstats.py (13488B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\lib\cpstats.py
      2 import logging
      3 if not hasattr(logging, 'statistics'):
      4     logging.statistics = {}
      5 
      6 def extrapolate_statistics(scope):
      7     c = {}
      8     for k, v in list(scope.items()):
      9         if isinstance(v, dict):
     10             v = extrapolate_statistics(v)
     11         elif isinstance(v, (list, tuple)):
     12             v = [ extrapolate_statistics(record) for record in v ]
     13         elif hasattr(v, '__call__'):
     14             v = v(scope)
     15         c[k] = v
     16 
     17     return c
     18 
     19 
     20 import threading
     21 import time
     22 import cherrypy
     23 appstats = logging.statistics.setdefault('CherryPy Applications', {})
     24 appstats.update({'Enabled': True,
     25  'Bytes Read/Request': lambda s: s['Total Requests'] and s['Total Bytes Read'] / float(s['Total Requests']) or 0.0,
     26  'Bytes Read/Second': lambda s: s['Total Bytes Read'] / s['Uptime'](s),
     27  'Bytes Written/Request': lambda s: s['Total Requests'] and s['Total Bytes Written'] / float(s['Total Requests']) or 0.0,
     28  'Bytes Written/Second': lambda s: s['Total Bytes Written'] / s['Uptime'](s),
     29  'Current Time': lambda s: time.time(),
     30  'Current Requests': 0,
     31  'Requests/Second': lambda s: float(s['Total Requests']) / s['Uptime'](s),
     32  'Server Version': cherrypy.__version__,
     33  'Start Time': time.time(),
     34  'Total Bytes Read': 0,
     35  'Total Bytes Written': 0,
     36  'Total Requests': 0,
     37  'Total Time': 0,
     38  'Uptime': lambda s: time.time() - s['Start Time'],
     39  'Requests': {}})
     40 proc_time = lambda s: time.time() - s['Start Time']
     41 
     42 class ByteCountWrapper(object):
     43 
     44     def __init__(self, rfile):
     45         self.rfile = rfile
     46         self.bytes_read = 0
     47 
     48     def read(self, size = -1):
     49         data = self.rfile.read(size)
     50         self.bytes_read += len(data)
     51         return data
     52 
     53     def readline(self, size = -1):
     54         data = self.rfile.readline(size)
     55         self.bytes_read += len(data)
     56         return data
     57 
     58     def readlines(self, sizehint = 0):
     59         total = 0
     60         lines = []
     61         line = self.readline()
     62         while line:
     63             lines.append(line)
     64             total += len(line)
     65             if 0 < sizehint <= total:
     66                 break
     67             line = self.readline()
     68 
     69         return lines
     70 
     71     def close(self):
     72         self.rfile.close()
     73 
     74     def __iter__(self):
     75         return self
     76 
     77     def next(self):
     78         data = self.rfile.next()
     79         self.bytes_read += len(data)
     80         return data
     81 
     82 
     83 average_uriset_time = lambda s: s['Count'] and s['Sum'] / s['Count'] or 0
     84 
     85 class StatsTool(cherrypy.Tool):
     86 
     87     def __init__(self):
     88         cherrypy.Tool.__init__(self, 'on_end_request', self.record_stop)
     89 
     90     def _setup(self):
     91         if appstats.get('Enabled', False):
     92             cherrypy.Tool._setup(self)
     93             self.record_start()
     94 
     95     def record_start(self):
     96         request = cherrypy.serving.request
     97         if not hasattr(request.rfile, 'bytes_read'):
     98             request.rfile = ByteCountWrapper(request.rfile)
     99             request.body.fp = request.rfile
    100         r = request.remote
    101         appstats['Current Requests'] += 1
    102         appstats['Total Requests'] += 1
    103         appstats['Requests'][threading._get_ident()] = {'Bytes Read': None,
    104          'Bytes Written': None,
    105          'Client': lambda s: '%s:%s' % (r.ip, r.port),
    106          'End Time': None,
    107          'Processing Time': proc_time,
    108          'Request-Line': request.request_line,
    109          'Response Status': None,
    110          'Start Time': time.time()}
    111 
    112     def record_stop(self, uriset = None, slow_queries = 1.0, slow_queries_count = 100, debug = False, **kwargs):
    113         w = appstats['Requests'][threading._get_ident()]
    114         r = cherrypy.request.rfile.bytes_read
    115         w['Bytes Read'] = r
    116         appstats['Total Bytes Read'] += r
    117         if cherrypy.response.stream:
    118             w['Bytes Written'] = 'chunked'
    119         else:
    120             cl = int(cherrypy.response.headers.get('Content-Length', 0))
    121             w['Bytes Written'] = cl
    122             appstats['Total Bytes Written'] += cl
    123         w['Response Status'] = cherrypy.response.status
    124         w['End Time'] = time.time()
    125         p = w['End Time'] - w['Start Time']
    126         w['Processing Time'] = p
    127         appstats['Total Time'] += p
    128         appstats['Current Requests'] -= 1
    129         if debug:
    130             cherrypy.log('Stats recorded: %s' % repr(w), 'TOOLS.CPSTATS')
    131         if uriset:
    132             rs = appstats.setdefault('URI Set Tracking', {})
    133             r = rs.setdefault(uriset, {'Min': None,
    134              'Max': None,
    135              'Count': 0,
    136              'Sum': 0,
    137              'Avg': average_uriset_time})
    138             if r['Min'] is None or p < r['Min']:
    139                 r['Min'] = p
    140             if r['Max'] is None or p > r['Max']:
    141                 r['Max'] = p
    142             r['Count'] += 1
    143             r['Sum'] += p
    144         if slow_queries and p > slow_queries:
    145             sq = appstats.setdefault('Slow Queries', [])
    146             sq.append(w.copy())
    147             if len(sq) > slow_queries_count:
    148                 sq.pop(0)
    149 
    150 
    151 import cherrypy
    152 cherrypy.tools.cpstats = StatsTool()
    153 import os
    154 thisdir = os.path.abspath(os.path.dirname(__file__))
    155 try:
    156     import json
    157 except ImportError:
    158     try:
    159         import simplejson as json
    160     except ImportError:
    161         json = None
    162 
    163 missing = object()
    164 locale_date = lambda v: time.strftime('%c', time.gmtime(v))
    165 iso_format = lambda v: time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(v))
    166 
    167 def pause_resume(ns):
    168 
    169     def _pause_resume(enabled):
    170         pause_disabled = ''
    171         resume_disabled = ''
    172         if enabled:
    173             resume_disabled = 'disabled="disabled" '
    174         else:
    175             pause_disabled = 'disabled="disabled" '
    176         return '\n            <form action="pause" method="POST" style="display:inline">\n            <input type="hidden" name="namespace" value="%s" />\n            <input type="submit" value="Pause" %s/>\n            </form>\n            <form action="resume" method="POST" style="display:inline">\n            <input type="hidden" name="namespace" value="%s" />\n            <input type="submit" value="Resume" %s/>\n            </form>\n            ' % (ns,
    177          pause_disabled,
    178          ns,
    179          resume_disabled)
    180 
    181     return _pause_resume
    182 
    183 
    184 class StatsPage(object):
    185     formatting = {'CherryPy Applications': {'Enabled': pause_resume('CherryPy Applications'),
    186                                'Bytes Read/Request': '%.3f',
    187                                'Bytes Read/Second': '%.3f',
    188                                'Bytes Written/Request': '%.3f',
    189                                'Bytes Written/Second': '%.3f',
    190                                'Current Time': iso_format,
    191                                'Requests/Second': '%.3f',
    192                                'Start Time': iso_format,
    193                                'Total Time': '%.3f',
    194                                'Uptime': '%.3f',
    195                                'Slow Queries': {'End Time': None,
    196                                                 'Processing Time': '%.3f',
    197                                                 'Start Time': iso_format},
    198                                'URI Set Tracking': {'Avg': '%.3f',
    199                                                     'Max': '%.3f',
    200                                                     'Min': '%.3f',
    201                                                     'Sum': '%.3f'},
    202                                'Requests': {'Bytes Read': '%s',
    203                                             'Bytes Written': '%s',
    204                                             'End Time': None,
    205                                             'Processing Time': '%.3f',
    206                                             'Start Time': None}},
    207      'CherryPy WSGIServer': {'Enabled': pause_resume('CherryPy WSGIServer'),
    208                              'Connections/second': '%.3f',
    209                              'Start time': iso_format}}
    210 
    211     def index(self):
    212         yield '\n<html>\n<head>\n    <title>Statistics</title>\n<style>\n\nth, td {\n    padding: 0.25em 0.5em;\n    border: 1px solid #666699;\n}\n\ntable {\n    border-collapse: collapse;\n}\n\ntable.stats1 {\n    width: 100%;\n}\n\ntable.stats1 th {\n    font-weight: bold;\n    text-align: right;\n    background-color: #CCD5DD;\n}\n\ntable.stats2, h2 {\n    margin-left: 50px;\n}\n\ntable.stats2 th {\n    font-weight: bold;\n    text-align: center;\n    background-color: #CCD5DD;\n}\n\n</style>\n</head>\n<body>\n'
    213         for title, scalars, collections in self.get_namespaces():
    214             yield "\n<h1>%s</h1>\n\n<table class='stats1'>\n    <tbody>\n" % title
    215             for i, (key, value) in enumerate(scalars):
    216                 colnum = i % 3
    217                 if colnum == 0:
    218                     yield '\n        <tr>'
    219                 yield "\n            <th>%(key)s</th><td id='%(title)s-%(key)s'>%(value)s</td>" % vars()
    220                 if colnum == 2:
    221                     yield '\n        </tr>'
    222 
    223             if colnum == 0:
    224                 yield '\n            <th></th><td></td>\n            <th></th><td></td>\n        </tr>'
    225             elif colnum == 1:
    226                 yield '\n            <th></th><td></td>\n        </tr>'
    227             yield '\n    </tbody>\n</table>'
    228             for subtitle, headers, subrows in collections:
    229                 yield "\n<h2>%s</h2>\n<table class='stats2'>\n    <thead>\n        <tr>" % subtitle
    230                 for key in headers:
    231                     yield '\n            <th>%s</th>' % key
    232 
    233                 yield '\n        </tr>\n    </thead>\n    <tbody>'
    234                 for subrow in subrows:
    235                     yield '\n        <tr>'
    236                     for value in subrow:
    237                         yield '\n            <td>%s</td>' % value
    238 
    239                     yield '\n        </tr>'
    240 
    241                 yield '\n    </tbody>\n</table>'
    242 
    243         yield '\n</body>\n</html>\n'
    244 
    245     index.exposed = True
    246 
    247     def get_namespaces(self):
    248         s = extrapolate_statistics(logging.statistics)
    249         for title, ns in sorted(s.items()):
    250             scalars = []
    251             collections = []
    252             ns_fmt = self.formatting.get(title, {})
    253             for k, v in sorted(ns.items()):
    254                 fmt = ns_fmt.get(k, {})
    255                 if isinstance(v, dict):
    256                     headers, subrows = self.get_dict_collection(v, fmt)
    257                     collections.append((k, ['ID'] + headers, subrows))
    258                 elif isinstance(v, (list, tuple)):
    259                     headers, subrows = self.get_list_collection(v, fmt)
    260                     collections.append((k, headers, subrows))
    261                 else:
    262                     format = ns_fmt.get(k, missing)
    263                     if format is None:
    264                         continue
    265                     if hasattr(format, '__call__'):
    266                         v = format(v)
    267                     elif format is not missing:
    268                         v = format % v
    269                     scalars.append((k, v))
    270 
    271             yield (title, scalars, collections)
    272 
    273     def get_dict_collection(self, v, formatting):
    274         headers = []
    275         for record in v.itervalues():
    276             for k3 in record:
    277                 format = formatting.get(k3, missing)
    278                 if format is None:
    279                     continue
    280                 if k3 not in headers:
    281                     headers.append(k3)
    282 
    283         headers.sort()
    284         subrows = []
    285         for k2, record in sorted(v.items()):
    286             subrow = [k2]
    287             for k3 in headers:
    288                 v3 = record.get(k3, '')
    289                 format = formatting.get(k3, missing)
    290                 if format is None:
    291                     continue
    292                 if hasattr(format, '__call__'):
    293                     v3 = format(v3)
    294                 elif format is not missing:
    295                     v3 = format % v3
    296                 subrow.append(v3)
    297 
    298             subrows.append(subrow)
    299 
    300         return (headers, subrows)
    301 
    302     def get_list_collection(self, v, formatting):
    303         headers = []
    304         for record in v:
    305             for k3 in record:
    306                 format = formatting.get(k3, missing)
    307                 if format is None:
    308                     continue
    309                 if k3 not in headers:
    310                     headers.append(k3)
    311 
    312         headers.sort()
    313         subrows = []
    314         for record in v:
    315             subrow = []
    316             for k3 in headers:
    317                 v3 = record.get(k3, '')
    318                 format = formatting.get(k3, missing)
    319                 if format is None:
    320                     continue
    321                 if hasattr(format, '__call__'):
    322                     v3 = format(v3)
    323                 elif format is not missing:
    324                     v3 = format % v3
    325                 subrow.append(v3)
    326 
    327             subrows.append(subrow)
    328 
    329         return (headers, subrows)
    330 
    331     if json is not None:
    332 
    333         def data(self):
    334             s = extrapolate_statistics(logging.statistics)
    335             cherrypy.response.headers['Content-Type'] = 'application/json'
    336             return json.dumps(s, sort_keys=True, indent=4)
    337 
    338         data.exposed = True
    339 
    340     def pause(self, namespace):
    341         logging.statistics.get(namespace, {})['Enabled'] = False
    342         raise cherrypy.HTTPRedirect('./')
    343 
    344     pause.exposed = True
    345     pause.cp_config = {'tools.allow.on': True,
    346      'tools.allow.methods': ['POST']}
    347 
    348     def resume(self, namespace):
    349         logging.statistics.get(namespace, {})['Enabled'] = True
    350         raise cherrypy.HTTPRedirect('./')
    351 
    352     resume.exposed = True
    353     resume.cp_config = {'tools.allow.on': True,
    354      'tools.allow.methods': ['POST']}