Completed
Push — master ( 55c3a8...4ee339 )
by Kenny
01:07
created

FcgiNet.rx_nbytes()   B

Complexity

Conditions 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 4
c 1
b 0
f 1
dl 0
loc 25
rs 8.5806
1
# -*- coding: utf-8 -*-
2
3
__author__ = 'Kenny Freeman'
4
__email__ = '[email protected]'
5
__license__ = "ISCL"
6
__docformat__ = 'reStructuredText'
7
8
import sys
9
10
PY3 = sys.version_info > (3,)
11
12
import json
13
import struct
14
import socket
15
from collections import deque
16
17
import plumd
18
import plumd.plugins
19
20
21
class FcgiParameterProblem(Exception):
22
    """Generic problem with FastCGI request data."""
23
    pass
24
25
class FcgiRequestFailed(Exception):
26
    """Generic FastCGI request failed exception."""
27
    pass
28
29
# Number of bytes in a FCGI_Header
30
FCGI_HEADER_LEN         =  8
31
32
# Version of the FastCGI Protocol
33
FCGI_VERSION_1          =  1
34
35
# Possible values for the type in FCGI_Header
36
FCGI_BEGIN_REQUEST      =  1
37
#FCGI_ABORT_REQUEST      =  2
38
FCGI_END_REQUEST        =  3
39
FCGI_PARAMS             =  4
40
FCGI_STDIN              =  5
41
FCGI_STDOUT             =  6
42
FCGI_STDERR             =  7
43
FCGI_DATA               =  8
44
#FCGI_GET_VALUES         =  9
45
#FCGI_GET_VALUES_RESULT  = 10
46
FCGI_UNKNOWN_TYPE       = 11
47
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
48
49
# Values for role in FCGI_BeginRequestBody
50
FCGI_RESPONDER  = 1
51
52
# Values for protocolStatus in FCGI_EndRequestBody
53
FCGI_REQUEST_COMPLETE = 0
54
FCGI_CANT_MPX_CONN    = 1
55
FCGI_OVERLOADED       = 2
56
FCGI_UNKNOWN_ROLE     = 3
57
58
# Numbe rof bytes in a FCGI_BeginRequestBody, etc
59
FCGI_BEGIN_REQUEST_LEN = 8
60
61
62
def fcgi_header(dat):
63
    """Return a dict with values unpacked from dat:
64
65
        dat = {
66
            'version': FCGI_VERSION_1,
67
            'type': FCGI_PARAMS,
68
            'id': 1,
69
            'data': 512, # data length
70
            'padding': 0 # padding length
71
        }
72
73
    :param dat: A packed FastCGI header string
74
    :type dat: str
75
    :rtype: dict
76
    """
77
    keys = ['version', 'type', 'id', 'data_len', 'pad_len']
78
    vals = struct.unpack("!BBHHBx", dat)
79
    return dict(zip(keys, vals))
80
81
def fcgi_end_request(dat):
82
    """Return a tuple of (appstatus, protocolstatus) from the end request
83
    body dat.
84
85
    :param dat: A packed string representing the FCGI_EndRequestBody
86
    :type dat: str
87
    :rtype: tuple(int, int)
88
    """
89
    astat, pstat = struct.unpack('!LB3x', dat)
90
    return (astat, pstat)
91
92
def fcgi_unpack(buff):
93
    """Return a dict containing all key, value pairs in the buffer.
94
95
    :param buff: str to unpack pairs from
96
    :type buff: str
97
    :rtype: dict
98
    """
99
    ret = {}
100
    maxi = len(buff)
101
    i = 0
102
    while i < maxi:
103
        # extract klen, vlen then key, val
104
        klen = ord(buff[i][0])
105
        if klen < 128:
106
            i += 1
107
        else:
108
            klen = struct.unpack("!L", struct_bytes)[0] & 0x7fffffff
109
            i += 4
110
        # now get the val length
111
        vlen = ord(buff[i][0])
112
        if vlen < 128:
113
            i += 1
114
        else:
115
            vlen = struct.unpack("!L", struct_bytes)[0] & 0x7fffffff
116
            i += 4
117
        # get the values
118
        key = buff[i:i+klen]
119
        i += klen
120
        val = buff[i:i+vlen]
121
        i += vlen
122
        ret[key] = val
123
    return ret
124
125
def fcgi_pack(vals):
126
    """Pack a dict of key=value pairs into a buffer.
127
128
    :param vals: A dict of key=value pairs
129
    :type vals: dict
130
    :rtype: str
131
    """
132
    ret = deque()
133
    # pack: key_len+val_len+key+val
134
    for key, val in vals.items():
135
        val = str(val) if val is not None else ""
136
        klen = len(key)
137
        vlen = len(val)
138
139
        # does key_len fit in a char?
140
        if klen < 128:
141
            ret.append(chr(klen))
142
        else:
143
            # should check if len(key) > 0x7fffffff..
144
            #long value:      0x80000000L = 2147483648
145
            ret.append(struct.pack('!L', klen | 0x80000000))
146
147
        # same, does it fit in a single char?
148
        if vlen < 128:
149
            ret.append(chr(vlen))
150
        else:
151
            # should check if len(val) > 0x7fffffff..
152
            #long value:      0x80000000L = 2147483648
153
            ret.append(struct.pack('!L', vlen | 0x80000000))
154
        ret.append(str(key))
155
        ret.append(str(val))
156
    return "".join(ret)
157
158
def fcgi_type_begin():
159
    """Return a FCGI_BeginRequestRecord Responder request.
160
161
    :rtype: str
162
    """
163
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_BEGIN_REQUEST,
164
                         1, FCGI_BEGIN_REQUEST_LEN, 0)
165
    body = struct.pack('!HB5x', FCGI_RESPONDER, 0)
166
    return "".join([header, body])
167
168
def fcgi_type_params(params):
169
    """Return a FCGI_PARAMS record.
170
171
    :param params: A str returned from fcgi_pack.
172
    :type params: str
173
    :rtype: str
174
    """
175
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_PARAMS,
176
                         1, len(params), 0)
177
    return "".join([header, params])
178
179
def fcgi_type_data(data):
180
    """Return a FCGI_DATA record.
181
182
    :param data: A payload string
183
    :type data: str
184
    :rtype: str
185
    """
186
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_DATA,
187
                         1, len(data), 0)
188
    return "".join([header, data])
189
190
191
def fcgi_type_stdin(data):
192
    """Return a FCGI_STDIN record.
193
194
    :param data: The data to send to stdin
195
    :type data: str
196
    :rtype: str
197
    """
198
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_STDIN,
199
                         1, len(data), 0)
200
    return "".join([header, data])
201
202
203
class FcgiNet(object):
204
    """A socket reader/writer that reads/writes FastCGI records."""
205
206
    def __init__(self, addr, timeout):
207
        """A helper class to read/write FcgiHeaders to/from a server.
208
209
        :param addr: The FastCGI host:port to connect to.
210
        :type addr: str
211
        :param to: An optional timeout
212
        :type to: int
213
        :rtype: FcgiNet
214
        """
215
        self.addr = addr
216
        self.timeout = timeout
217
        self.socket = None
218
219
    def connect(self):
220
        if self.socket:
221
            self.socket.close()
222
        try:
223
            # connect
224
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
225
            # set timeout for socket operations
226
            self.socket.settimeout(self.timeout)
227
            self.socket.connect(self.addr)
228
        except Exception as e:
229
            msg = "could not connect: {0}"
230
            raise FcgiRequestFailed(msg.format(e))
231
232
    def rx_nbytes(self, nbytes):
233
        """Read nbytes from our socket, return a tuple of the number of bytes
234
        read and the data read.
235
236
        :param nbytes: The number of bytes to attempt to read
237
        :type nbytes: int
238
239
        :rtype: tuple(int, str)
240
        :raises: FcgiRequestFailed
241
        """
242
        dat = deque()
243
        nbytes_rx = 0
244
        while nbytes_rx < nbytes:
245
            buff = None
246
            try:
247
                buff = self.socket.recv(nbytes)
248
            except socket.error as e:
249
                msg = "Exception reading from remote host {0}:{1}: {2}"
250
                msg = msg.format(self.addr[0], self.addr[1], e)
251
                raise FcgiRequestFailed(msg)
252
            if not buff:
253
                break
254
            dat.append(buff)
255
            nbytes_rx += len(buff)
256
        return (nbytes_rx, "".join(dat))
257
258
    def rx(self):
259
        """Read a FastCGI header and payload from our socket and return it as a
260
        a tuple of (header, buffer).
261
262
        :raises:
263
            FcgiRequestFailed
264
265
        :rtype: tuple(FcgiHeader, dict)
266
        """
267
        # hdr.data will raise ValueError if data length != FCGI_HEADER_LEN
268
        hlen, dat  = self.rx_nbytes(FCGI_HEADER_LEN)
269
        if hlen != FCGI_HEADER_LEN:
270
            msg = "Excpecting header of {0} bytes, received {1}"
271
            raise FcgiRequestFailed(msg.format(FCGI_HEADER_LEN, hlen))
272
273
        hdr = fcgi_header(dat)
274
        dat = ""
275
        dat_len = hdr['data_len']
276
        pad_len = hdr['pad_len']
277
278
        if dat_len:
279
            dlen, dat = self.rx_nbytes(dat_len)
280
            if dlen != dat_len:
281
                msg = "Excpecting data of {0} bytes, received {1}"
282
                raise FcgiRequestFailed(msg.format(dat_len, dlen))
283
284
        if pad_len:
285
            plen, pad = self.rx_nbytes(pad_len)
286
            if plen != pad_len:
287
                msg = "Excpecting padding of {0} bytes, received {1}"
288
                raise FcgiRequestFailed(msg.format(pad_len, plen))
289
290
        return (hdr, dat)
291
292
    def tx(self, dat):
293
        """Send a FastCGI Record to our socket.
294
295
        :param record: The FcgiRecord to send
296
        :type record: FcgiRecord
297
        """
298
        if not self.socket:
299
            self.connect()
300
        self.socket.sendall(dat)
301
302
    def end(self):
303
        """Close the socket."""
304
        if self.socket:
305
            self.socket.close()
306
307
308
def fcgi_call(addr, timeout, script, root, query):
309
    """Call <script> on local/remote FastCGI server <addr> and return results.
310
311
    The script is called with no arguments and minimal FastCGI parameters
312
    are set.
313
314
    :param addr: The host:port to connect to
315
    :type addr: str
316
    :param timeout: The tcp timeout to set on the socket
317
    :type timeout: int
318
    :param script: The name of the script to call
319
    :type script: str
320
    :param root: The root directory the script lives in
321
    :type root: str
322
    :param query: The query string for the request (eg. json&full)
323
    :type query: str
324
    :rval: tuple()
325
    :raises: FcgiRequestFailed
326
    """
327
    net = FcgiNet(addr, timeout)
328
    sout = deque()
329
    serr = deque()
330
    r_begin = fcgi_type_begin()
331
    params = {
332
        "QUERY_STRING": "json",
333
        "REQUEST_METHOD": "GET",
334
        "CONTENT_TYPE": "",
335
        "CONTENT_LENGTH": "",
336
        "SCRIPT_NAME": script,
337
        "REQUEST_URI": script,
338
        "DOCUMENT_ROOT": root,
339
        "SERVER_PROTOCOL": "HTTP/1.1",
340
        "GATEWAY_INTERFACE": "CGI/1.1",
341
        "SERVER_SOFTWARE": "fcgi/1.0",
342
        "REMOTE_ADDR": "127.0.0.1",
343
        "REMOTE_PORT": "0",
344
        "SERVER_ADDR": "127.0.0.1",
345
        "SERVER_PORT": "80",
346
        "SERVER_NAME": "localhost",
347
        "PATH_INFO": script,
348
        "SCRIPT_FILENAME": "{0}/{1}".format(root, script)
349
    }
350
    param_dat = fcgi_pack(params)
351
    r_params = fcgi_type_params(param_dat)
352
    r_stdin = fcgi_type_stdin("")
353
    net.tx("".join([r_begin, r_params, r_stdin]))
354
    while True:
355
        hdr, buff = net.rx()
356
        if hdr['type'] == FCGI_STDOUT:
357
            sout.append(buff)
358
            continue
359
        elif hdr['type'] == FCGI_STDERR:
360
            serr.append(buff)
361
            continue
362
        elif hdr['type'] == FCGI_END_REQUEST:
363
            net.end()
364
            astatus, pstatus = fcgi_end_request(buff)
365
            if pstatus != FCGI_REQUEST_COMPLETE or astatus != 0:
366
                msg = "bad proto/app status: {0}, {1}".format(pstatus, astatus)
367
                raise FcgiRequestFailed(msg)
368
            break
369
        net.end()
370
        msg = "unexpected response: {0} : {1}"
371
        raise FcgiRequestFailed(msg.format(hdr, buff))
372
    net.end()
373
    return ("".join(sout), "".join(serr))
374
375
376
377
class Php(plumd.plugins.Reader):
378
    """Plugin to record php-fpm, opcache and apc metrics."""
379
380
    # default config values
381
    defaults = {
382
        'poll.interval': 10,
383
        'fpm_status': "/fpmstatus",         # match pm.status_path from config
384
        'fpm_status_args': "json",          # query params (json required)
385
        'fpm_pools': {
386
            'www': {                        # add an entry per pool to monitor
387
                'host': '127.0.0.1',
388
                'port': 9000,
389
                'script': 'cacheinfo.php',
390
                'root': '/var/www'
391
            }
392
        },
393
        'record': {
394
            'apc': [ "expunges","mem_size", "num_entries",  "num_hits",
395
                     "num_inserts", "num_misses", "num_slots", "ttl" ],
396
            'apc_sma': [ "avail_mem", "num_seg", "seg_size" ]
397
        },
398
        'record_op': {
399
            "interned_strings_usage": [
400
                "buffer_size", "free_memory", "number_of_strings",
401
                "used_memory"
402
                ],
403
            "memory_usage": [
404
                "current_wasted_percentage", "free_memory", "used_memory", "wasted_memory"
405
                ],
406
            "opcache_statistics": [
407
                "blacklist_miss_ratio", "blacklist_misses", "hash_restarts",
408
                "hits", "manual_restarts", "max_cached_keys", "misses",
409
                "num_cached_keys","num_cached_scripts", "oom_restarts",
410
                "opcache_hit_rate"
411
                ]
412
        },
413
        'record_status': [
414
            "accepted conn", "active processes", "idle processes",
415
            "listen queue", "listen queue len", "max active processes",
416
            "max children reached", "max listen queue", "slow requests",
417
            "total processes"
418
        ],
419
        'rename': {
420
            'interned_strings_usage': 'strings',
421
            'memory_usage': 'mem',
422
            'opcache_statistics': 'cache',
423
            'buffer_size': 'buf_size',
424
            'free_memory': 'mem_free',
425
            'number_of_strings': 'num_str',
426
            'used_memory': 'mem_used',
427
            'current_wasted_percentage': 'waste_perc',
428
            'wasted_memory': 'mem_wasted',
429
            'blacklist_miss_ratio': 'bl_miss_ratio',
430
            'blacklist_misses': 'bl_miss',
431
            'hash_restarts': 'h_restarts',
432
            'manual_restarts': 'm_restarts',
433
            'max_cached_keys': 'max_keys',
434
            'num_cached_keys': 'num_keys',
435
            'num_cached_scripts': 'num_scripts',
436
            'opcache_hit_rate': 'hit_rate',
437
            'accepted conn': 'con_accepted',
438
            'active processes': 'proc_active',
439
            'idle processes': 'proc_idle',
440
            'listen queue': 'listen_q',
441
            'listen queue len': 'listen_q_len',
442
            'max active processes': 'proc_max_active',
443
            'max children reached': 'proc_max_child',
444
            'max listen queue': 'listen_q_max',
445
            'slow requests': 'req_slow',
446
            'total processes': 'proc_total'
447
        },
448
        'timeout': 10
449
    }
450
451
    def __init__(self, log, config):
452
        """Plugin to record nginx stub_status metrics.
453
454
        :param log: A logger
455
        :type log: logging.RootLogger
456
        :param config: a plumd.config.Conf configuration helper instance.
457
        :type config: plumd.config.Conf
458
        """
459
        super(Php, self).__init__(log, config)
460
        self.config.defaults(Php.defaults)
461
462
        self.fpm_status = self.config.get('fpm_status')
463
        self.fpm_status_args = self.config.get('fpm_status_args')
464
        self.record_status = self.config.get('record_status')
465
        self.pools = self.config.get('pools')
466
        self.timeout = self.config.get('timeout')
467
        self.record = self.config.get("record")
468
        self.record_op = self.config.get("record_op")
469
        self.rename = self.config.get("rename")
470
471
472
473
    def poll(self):
474
        """Query PHP-FPM for metrics over a FastCGI connection.
475
476
        Records php-fpm pool stats from pm.status_path and also
477
        output from a php script that returns both opcache and apc metrics.
478
479
        The php-fpm pool stats output is expected to be in json by setting a
480
        QUERY_STRING FastCGI parameter to json (currently does not support
481
        json&full).
482
483
        The opcache and apc metrics are parsed from a custom php script - see
484
        misc in the git repo for the script source.
485
486
        :rtype: ResultSet
487
        """
488
        result = plumd.Result("php")
489
        record = self.config.get('record')
490
        record_op = self.config.get('record_op')
491
        rename = self.config.get('rename')
492
        timeout = self.config.get('timeout')
493
        record_status = self.config.get('record_status')
494
        fpm_status = self.config.get('fpm_status')
495
        fpm_status_args = self.config.get('fpm_status_args')
496
        fpm_pools = self.config.get('fpm_pools')
497
498
        # check each configured pool
499
        for pname, pconf in fpm_pools.items():
500
            try:
501
                addr = (pconf['host'], pconf['port'])
502
                script = pconf['script']
503
                root = pconf['root']
504
            except KeyError as e:
505
                msg = "Php: pool {0} config invalid: {1}"
506
                self.log.error(msg.format(pname, e))
507
                continue
508
            # call the status script and get its output
509
            cinfo = {}
510
            try:
511
                sout, serr = fcgi_call(addr, timeout, fpm_status, root,
512
                                       fpm_status_args)
513
                lines = [ line for line in sout.split("\n")
514
                          if line.startswith("{\"") ]
515
                cinfo = json.loads("".join(lines))
516
            except Exception as e:
517
                msg = "Php: failed to call {0} for pool {1}: {2}"
518
                self.log.error(msg.format(fpm_status, pname, e))
519
                continue
520
521
            # record fpm status metrics
522
            for metric in record_status:
523
                if metric not in cinfo:
524
                    continue
525
                mname = metric if metric not in rename else rename[metric]
526
                mstr = "{0}.fpm.{1}".format(pname, mname)
527
                result.add(plumd.Float(mstr, cinfo[metric]))
528
529
            # call the cache info script and get its output
530
            cinfo = {}
531
            try:
532
                sout, serr = fcgi_call(addr, timeout, script, root, "")
533
                lines = [ line for line in sout.split("\n")
534
                          if line.startswith("{\"") ]
535
                cinfo = json.loads("".join(lines))
536
            except Exception as e:
537
                msg = "Php: failed to call {0} for pool {1}: {2}"
538
                self.log.error(msg.format(script, pname, e))
539
                continue
540
541
            # apc
542
            for key, metrics in record.items():
543
                if key not in cinfo:
544
                    continue
545
                mkey = key if key not in rename else rename[key]
546
                for metric in metrics:
547
                    if metric not in cinfo[key]:
548
                        continue
549
                    mname = metric if metric not in rename else rename[metric]
550
                    mstr = "{0}.{1}.{2}".format(pname, mkey, mname)
551
                    result.add(plumd.Float(mstr, cinfo[key][metric]))
552
553
            # opcache
554
            for key, metrics in record_op.items():
555
                if key not in cinfo['op']:
556
                    continue
557
                mkey = ke if key not in rename else rename[key]
558
                for metric in metrics:
559
                    if metric not in cinfo['op'][key]:
560
                        continue
561
                    mname = metric if metric not in rename else rename[metric]
562
                    mstr = "{0}.op.{1}.{2}".format(pname, mkey, mname)
563
                    result.add(plumd.Float(mstr, cinfo['op'][key][metric]))
564
565
        return plumd.ResultSet([result])
566
567
568
"""
569
Example php script and pm.status_path outputs:
570
571
Cache output:
572
{
573
    "apc": {
574
        "expunges": 0,
575
        "file_upload_progress": 1,
576
        "mem_size": 65600,
577
        "memory_type": "mmap",
578
        "num_entries": 100,
579
        "num_hits": 0,
580
        "num_inserts": 100,
581
        "num_misses": 0,
582
        "num_slots": 4099,
583
        "start_time": 1472356538,
584
        "ttl": 0
585
    },
586
    "op": {
587
        "cache_full": false,
588
        "interned_strings_usage": {
589
            "buffer_size": 8388608,
590
            "free_memory": 8050800,
591
            "number_of_strings": 3748,
592
            "used_memory": 337808
593
        },
594
        "memory_usage": {
595
            "current_wasted_percentage": 0.0081896781921387,
596
            "free_memory": 123263200,
597
            "used_memory": 10943536,
598
            "wasted_memory": 10992
599
        },
600
        "opcache_enabled": true,
601
        "opcache_statistics": {
602
            "blacklist_miss_ratio": 0,
603
            "blacklist_misses": 0,
604
            "hash_restarts": 0,
605
            "hits": 90,
606
            "last_restart_time": 0,
607
            "manual_restarts": 0,
608
            "max_cached_keys": 7963,
609
            "misses": 5,
610
            "num_cached_keys": 2,
611
            "num_cached_scripts": 2,
612
            "oom_restarts": 0,
613
            "opcache_hit_rate": 94.736842105263,
614
            "start_time": 1472356538
615
        },
616
        "restart_in_progress": false,
617
        "restart_pending": false
618
    }
619
}
620
621
622
623
Cache Output w/ SMA:
624
{
625
    "apc": {
626
        "expunges": 0,
627
        "file_upload_progress": 1,
628
        "mem_size": 65600,
629
        "memory_type": "mmap",
630
        "num_entries": 100,
631
        "num_hits": 0,
632
        "num_inserts": 100,
633
        "num_misses": 0,
634
        "num_slots": 4099,
635
        "start_time": 1472356538,
636
        "ttl": 0
637
    },
638
    "apc_sma": {
639
        "avail_mem": 33452504,
640
        "num_seg": 1,
641
        "seg_size": 33554296
642
    },
643
    "op": {
644
        "cache_full": false,
645
        "interned_strings_usage": {
646
            "buffer_size": 8388608,
647
            "free_memory": 8050800,
648
            "number_of_strings": 3748,
649
            "used_memory": 337808
650
        },
651
        "memory_usage": {
652
            "current_wasted_percentage": 0.0081896781921387,
653
            "free_memory": 123263200,
654
            "used_memory": 10943536,
655
            "wasted_memory": 10992
656
        },
657
        "opcache_enabled": true,
658
        "opcache_statistics": {
659
            "blacklist_miss_ratio": 0,
660
            "blacklist_misses": 0,
661
            "hash_restarts": 0,
662
            "hits": 90,
663
            "last_restart_time": 0,
664
            "manual_restarts": 0,
665
            "max_cached_keys": 7963,
666
            "misses": 5,
667
            "num_cached_keys": 2,
668
            "num_cached_scripts": 2,
669
            "oom_restarts": 0,
670
            "opcache_hit_rate": 94.736842105263,
671
            "start_time": 1472356538
672
        },
673
        "restart_in_progress": false,
674
        "restart_pending": false
675
    }
676
}
677
678
679
680
681
682
Normal output:
683
X-Powered-By: PHP/5.4.16
684
Expires: Thu, 01 Jan 1970 00:00:00 GMT
685
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
686
Content-Type: application/json
687
688
{
689
    "accepted conn": 164,
690
    "active processes": 1,
691
    "idle processes": 5,
692
    "listen queue": 0,
693
    "listen queue len": 128,
694
    "max active processes": 1,
695
    "max children reached": 0,
696
    "max listen queue": 0,
697
    "pool": "www",
698
    "process manager": "dynamic",
699
    "slow requests": 0,
700
    "start since": 16150,
701
    "start time": 1472356538,
702
    "total processes": 6
703
}
704
705
706
Full output:
707
X-Powered-By: PHP/5.4.16
708
Expires: Thu, 01 Jan 1970 00:00:00 GMT
709
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
710
Content-Type: application/json
711
712
{
713
    "accepted conn": 162,
714
    "active processes": 1,
715
    "idle processes": 5,
716
    "listen queue": 0,
717
    "listen queue len": 128,
718
    "max active processes": 1,
719
    "max children reached": 0,
720
    "max listen queue": 0,
721
    "pool": "www",
722
    "process manager": "dynamic",
723
    "processes": [
724
        {
725
            "content length": 0,
726
            "last request cpu": 0.0,
727
            "last request memory": 262144,
728
            "pid": 13170,
729
            "request duration": 790,
730
            "request method": "GET",
731
            "request uri": "/fpmstatus?json&full",
732
            "requests": 29,
733
            "script": "-",
734
            "start since": 15954,
735
            "start time": 1472356538,
736
            "state": "Idle",
737
            "user": "-"
738
        },
739
        {
740
            "content length": 0,
741
            "last request cpu": 0.0,
742
            "last request memory": 0,
743
            "pid": 13171,
744
            "request duration": 219,
745
            "request method": "GET",
746
            "request uri": "/fpmstatus?json&full",
747
            "requests": 29,
748
            "script": "-",
749
            "start since": 15954,
750
            "start time": 1472356538,
751
            "state": "Running",
752
            "user": "-"
753
        },
754
        {
755
            "content length": 0,
756
            "last request cpu": 3802.28,
757
            "last request memory": 262144,
758
            "pid": 13172,
759
            "request duration": 263,
760
            "request method": "GET",
761
            "request uri": "/fpmstatus?json",
762
            "requests": 28,
763
            "script": "-",
764
            "start since": 15954,
765
            "start time": 1472356538,
766
            "state": "Idle",
767
            "user": "-"
768
        },
769
        {
770
            "content length": 0,
771
            "last request cpu": 0.0,
772
            "last request memory": 262144,
773
            "pid": 13173,
774
            "request duration": 216,
775
            "request method": "GET",
776
            "request uri": "/fpmstatus?json",
777
            "requests": 28,
778
            "script": "-",
779
            "start since": 15954,
780
            "start time": 1472356538,
781
            "state": "Idle",
782
            "user": "-"
783
        },
784
        {
785
            "content length": 0,
786
            "last request cpu": 0.0,
787
            "last request memory": 262144,
788
            "pid": 13174,
789
            "request duration": 466,
790
            "request method": "GET",
791
            "request uri": "/fpmstatus?json&full",
792
            "requests": 28,
793
            "script": "-",
794
            "start since": 15954,
795
            "start time": 1472356538,
796
            "state": "Idle",
797
            "user": "-"
798
        },
799
        {
800
            "content length": 0,
801
            "last request cpu": 0.0,
802
            "last request memory": 262144,
803
            "pid": 15823,
804
            "request duration": 780,
805
            "request method": "GET",
806
            "request uri": "/fpmstatus?json&full",
807
            "requests": 20,
808
            "script": "-",
809
            "start since": 875,
810
            "start time": 1472371617,
811
            "state": "Idle",
812
            "user": "-"
813
        }
814
    ],
815
    "slow requests": 0,
816
    "start since": 15954,
817
    "start time": 1472356538,
818
    "total processes": 6
819
}
820
"""
821