FcgiNet.tx()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 2
1
# -*- coding: utf-8 -*-
2
"""PHP FastCGI metrics reader."""
3
import sys
4
import json
5
import struct
6
import socket
7
import os.path
8
from collections import deque
9
10
import plumd
11
12
PY3 = sys.version_info > (3,)
13
14
15
__author__ = 'Kenny Freeman'
16
__email__ = '[email protected]'
17
__license__ = "ISCL"
18
__docformat__ = 'reStructuredText'
19
20
21
class FcgiParameterProblem(Exception):
22
    """Generic problem with FastCGI request data."""
23
24
    pass
25
26
27
class FcgiRequestFailed(Exception):
28
    """Generic FastCGI request failed exception."""
29
30
    pass
31
32
# Number of bytes in a FCGI_Header
33
FCGI_HEADER_LEN = 8
34
35
# Version of the FastCGI Protocol
36
FCGI_VERSION_1 = 1
37
38
# Possible values for the type in FCGI_Header
39
FCGI_BEGIN_REQUEST = 1
40
# FCGI_ABORT_REQUEST      =  2
41
FCGI_END_REQUEST = 3
42
FCGI_PARAMS = 4
43
FCGI_STDIN = 5
44
FCGI_STDOUT = 6
45
FCGI_STDERR = 7
46
FCGI_DATA = 8
47
# FCGI_GET_VALUES         =  9
48
# FCGI_GET_VALUES_RESULT  = 10
49
FCGI_UNKNOWN_TYPE = 11
50
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
51
52
# Values for role in FCGI_BeginRequestBody
53
FCGI_RESPONDER = 1
54
55
# Values for protocolStatus in FCGI_EndRequestBody
56
FCGI_REQUEST_COMPLETE = 0
57
FCGI_CANT_MPX_CONN = 1
58
FCGI_OVERLOADED = 2
59
FCGI_UNKNOWN_ROLE = 3
60
61
# Numbe rof bytes in a FCGI_BeginRequestBody, etc
62
FCGI_BEGIN_REQUEST_LEN = 8
63
64
65
def fcgi_header(dat):
66
    """Return a dict with values unpacked from dat:
67
68
        dat = {
69
            'version': FCGI_VERSION_1,
70
            'type': FCGI_PARAMS,
71
            'id': 1,
72
            'data': 512, # data length
73
            'padding': 0 # padding length
74
        }
75
76
    :param dat: A packed FastCGI header string
77
    :type dat: str
78
    :rtype: dict
79
    """
80
    keys = ['version', 'type', 'id', 'data_len', 'pad_len']
81
    vals = struct.unpack("!BBHHBx", dat)
82
    return dict(zip(keys, vals))
83
84
85
def fcgi_end_request(dat):
86
    """Return a tuple of (appstatus, protocolstatus) from the end request
87
    body dat.
88
89
    :param dat: A packed string representing the FCGI_EndRequestBody
90
    :type dat: str
91
    :rtype: tuple(int, int)
92
    """
93
    astat, pstat = struct.unpack('!LB3x', dat)
94
    return (astat, pstat)
95
96
97
def fcgi_unpack(buff):
98
    """Return a dict containing all key, value pairs in the buffer.
99
100
    :param buff: str to unpack pairs from
101
    :type buff: str
102
    :rtype: dict
103
    """
104
    ret = {}
105
    maxi = len(buff)
106
    i = 0
107
    struct_bytes = None
108
    while i < maxi:
109
        # extract klen, vlen then key, val
110
        klen = ord(buff[i][0])
111
        if klen < 128:
112
            i += 1
113
        else:
114
            klen = struct.unpack("!L", struct_bytes)[0] & 0x7fffffff
115
            i += 4
116
        # now get the val length
117
        vlen = ord(buff[i][0])
118
        if vlen < 128:
119
            i += 1
120
        else:
121
            vlen = struct.unpack("!L", struct_bytes)[0] & 0x7fffffff
122
            i += 4
123
        # get the values
124
        key = buff[i:i + klen]
125
        i += klen
126
        val = buff[i:i + vlen]
127
        i += vlen
128
        ret[key] = val
129
    return ret
130
131
132
def fcgi_pack(vals):
133
    """Pack a dict of key=value pairs into a buffer.
134
135
    :param vals: A dict of key=value pairs
136
    :type vals: dict
137
    :rtype: str
138
    """
139
    ret = deque()
140
    # pack: key_len+val_len+key+val
141
    for key, val in vals.items():
142
        val = str(val) if val is not None else ""
143
        klen = len(key)
144
        vlen = len(val)
145
146
        # does key_len fit in a char?
147
        if klen < 128:
148
            ret.append(chr(klen))
149
        else:
150
            # should check if len(key) > 0x7fffffff..
151
            # long value:      0x80000000L = 2147483648
152
            ret.append(struct.pack('!L', klen | 0x80000000))
153
154
        # same, does it fit in a single char?
155
        if vlen < 128:
156
            ret.append(chr(vlen))
157
        else:
158
            # should check if len(val) > 0x7fffffff..
159
            # long value:      0x80000000L = 2147483648
160
            ret.append(struct.pack('!L', vlen | 0x80000000))
161
        ret.append(str(key))
162
        ret.append(str(val))
163
    return "".join(ret)
164
165
166
def fcgi_type_begin():
167
    """Return a FCGI_BeginRequestRecord Responder request.
168
169
    :rtype: str
170
    """
171
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_BEGIN_REQUEST,
172
                         1, FCGI_BEGIN_REQUEST_LEN, 0)
173
    body = struct.pack('!HB5x', FCGI_RESPONDER, 0)
174
    return "".join([header, body])
175
176
177
def fcgi_type_params(params):
178
    """Return a FCGI_PARAMS record.
179
180
    :param params: A str returned from fcgi_pack.
181
    :type params: str
182
    :rtype: str
183
    """
184
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_PARAMS,
185
                         1, len(params), 0)
186
    return "".join([header, params])
187
188
189
def fcgi_type_data(data):
190
    """Return a FCGI_DATA record.
191
192
    :param data: A payload string
193
    :type data: str
194
    :rtype: str
195
    """
196
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_DATA,
197
                         1, len(data), 0)
198
    return "".join([header, data])
199
200
201
def fcgi_type_stdin(data):
202
    """Return a FCGI_STDIN record.
203
204
    :param data: The data to send to stdin
205
    :type data: str
206
    :rtype: str
207
    """
208
    header = struct.pack("!BBHHBx", FCGI_VERSION_1, FCGI_STDIN,
209
                         1, len(data), 0)
210
    return "".join([header, data])
211
212
213
class FcgiNet(object):
214
    """A socket reader/writer that reads/writes FastCGI records."""
215
216
    def __init__(self, addr, timeout, sfamily):
217
        """A helper class to read/write FcgiHeaders to/from a server.
218
219
        :param addr: The FastCGI host:port to connect to.
220
        :type addr: str
221
        :param to: An optional timeout
222
        :type to: int
223
        :param sfamily: The socket family for addr (eg. AF_INET or AF_UNIX)
224
        :type sfamily: int
225
        :rtype: FcgiNet
226
        """
227
        self.addr = addr
228
        self.timeout = timeout
229
        self.socket = None
230
        self.sfamily = sfamily
231
232
    def connect(self):
233
        """Connect to the fastcgi server."""
234
        if self.socket:
235
            self.socket.close()
236
        try:
237
            # connect
238
            self.socket = socket.socket(self.sfamily, socket.SOCK_STREAM)
239
            # set timeout for socket operations
240
            self.socket.settimeout(self.timeout)
241
            self.socket.connect(self.addr)
242
        except Exception as exc:
243
            msg = "could not connect to: {0}: {1}"
244
            raise FcgiRequestFailed(msg.format(self.addr, exc))
245
246
    def rx_nbytes(self, nbytes):
247
        """Read nbytes from our socket, return a tuple of the number of bytes
248
        read and the data read.
249
250
        :param nbytes: The number of bytes to attempt to read
251
        :type nbytes: int
252
253
        :rtype: tuple(int, str)
254
        :raises: FcgiRequestFailed
255
        """
256
        dat = deque()
257
        nbytes_rx = 0
258
        while nbytes_rx < nbytes:
259
            buff = None
260
            try:
261
                buff = self.socket.recv(nbytes)
262
            except socket.error as exc:
263
                msg = "Exception reading from remote host {0}:{1}: {2}"
264
                msg = msg.format(self.addr[0], self.addr[1], exc)
265
                raise FcgiRequestFailed(msg)
266
            if not buff:
267
                break
268
            dat.append(buff)
269
            nbytes_rx += len(buff)
270
        return (nbytes_rx, "".join(dat))
271
272
    def rx(self):
273
        """Read a FastCGI header and payload from our socket and return it as a
274
        a tuple of (header, buffer).
275
276
        :raises:
277
            FcgiRequestFailed
278
279
        :rtype: tuple(FcgiHeader, dict)
280
        """
281
        # hdr.data will raise ValueError if data length != FCGI_HEADER_LEN
282
        hlen, dat = self.rx_nbytes(FCGI_HEADER_LEN)
283
        if hlen != FCGI_HEADER_LEN:
284
            msg = "Excpecting header of {0} bytes, received {1}"
285
            raise FcgiRequestFailed(msg.format(FCGI_HEADER_LEN, hlen))
286
287
        hdr = fcgi_header(dat)
288
        dat = ""
289
        dat_len = hdr['data_len']
290
        pad_len = hdr['pad_len']
291
292
        if dat_len:
293
            dlen, dat = self.rx_nbytes(dat_len)
294
            if dlen != dat_len:
295
                msg = "Excpecting data of {0} bytes, received {1}"
296
                raise FcgiRequestFailed(msg.format(dat_len, dlen))
297
298
        if pad_len:
299
            plen, _ = self.rx_nbytes(pad_len)
300
            if plen != pad_len:
301
                msg = "Excpecting padding of {0} bytes, received {1}"
302
                raise FcgiRequestFailed(msg.format(pad_len, plen))
303
304
        return (hdr, dat)
305
306
    def tx(self, dat):
307
        """Send a FastCGI Record to our socket.
308
309
        :param record: The FcgiRecord to send
310
        :type record: FcgiRecord
311
        """
312
        if not self.socket:
313
            self.connect()
314
        self.socket.sendall(dat)
315
316
    def end(self):
317
        """Close the socket."""
318
        if self.socket:
319
            self.socket.close()
320
321
322
def fcgi_call(addr, timeout, sfamily, script, root, query):
0 ignored issues
show
Unused Code introduced by
The argument query seems to be unused.
Loading history...
323
    """Call <script> on local/remote FastCGI server <addr> and return results.
324
325
    The script is called with no arguments and minimal FastCGI parameters
326
    are set.
327
328
    :param addr: The host:port to connect to
329
    :type addr: str
330
    :param timeout: The tcp timeout to set on the socket
331
    :type timeout: int
332
    :param script: The name of the script to call
333
    :type script: str
334
    :param root: The root directory the script lives in
335
    :type root: str
336
    :param query: The query string for the request (eg. json&full)
337
    :type query: str
338
    :rval: tuple()
339
    :raises: FcgiRequestFailed
340
    """
341
    net = FcgiNet(addr, timeout, sfamily)
342
    sout = deque()
343
    serr = deque()
344
    r_begin = fcgi_type_begin()
345
    params = {
346
        "QUERY_STRING": "json",
347
        "REQUEST_METHOD": "GET",
348
        "CONTENT_TYPE": "",
349
        "CONTENT_LENGTH": "",
350
        "SCRIPT_NAME": script,
351
        "REQUEST_URI": script,
352
        "DOCUMENT_ROOT": root,
353
        "SERVER_PROTOCOL": "HTTP/1.1",
354
        "GATEWAY_INTERFACE": "CGI/1.1",
355
        "SERVER_SOFTWARE": "fcgi/1.0",
356
        "REMOTE_ADDR": "127.0.0.1",
357
        "REMOTE_PORT": "0",
358
        "SERVER_ADDR": "127.0.0.1",
359
        "SERVER_PORT": "80",
360
        "SERVER_NAME": "localhost",
361
        "PATH_INFO": script,
362
        "SCRIPT_FILENAME": "{0}/{1}".format(root, script)
363
    }
364
    param_dat = fcgi_pack(params)
365
    r_params = fcgi_type_params(param_dat)
366
    r_stdin = fcgi_type_stdin("")
367
    net.tx("".join([r_begin, r_params, r_stdin]))
368
    while True:
369
        hdr, buff = net.rx()
370
        if hdr['type'] == FCGI_STDOUT:
371
            sout.append(buff)
372
            continue
373
        elif hdr['type'] == FCGI_STDERR:
374
            serr.append(buff)
375
            continue
376
        elif hdr['type'] == FCGI_END_REQUEST:
377
            net.end()
378
            astatus, pstatus = fcgi_end_request(buff)
379
            if pstatus != FCGI_REQUEST_COMPLETE or astatus != 0:
380
                msg = "bad proto/app status: {0}, {1}".format(pstatus, astatus)
381
                raise FcgiRequestFailed(msg)
382
            break
383
        net.end()
384
        msg = "unexpected response: {0} : {1}"
385
        raise FcgiRequestFailed(msg.format(hdr, buff))
386
    net.end()
387
    return ("".join(sout), "".join(serr))
388
389
390
class Php(plumd.Reader):
391
    """Plugin to record php-fpm, opcache and apc metrics."""
392
393
    # default config values
394
    defaults = {
395
        'poll.interval': 10,
396
        'fpm_status': "/fpmstatus",         # match pm.status_path from config
397
        'fpm_status_args': "json",          # query params (json required)
398
        'fpm_pools': {
399
            'www': {                        # add an entry per pool to monitor
400
                'host': '127.0.0.1',
401
                'port': 9000,
402
                'socket': None,             # unix domain socket or tcp
403
                'script': 'cacheinfo.php',
404
                'root': '/var/www'
405
            }
406
        },
407
        'record': {
408
            'apc': ["expunges", "mem_size", "num_entries", "num_hits",
409
                    "num_inserts", "num_misses", "num_slots", "ttl"],
410
            'apc_sma': ["avail_mem", "num_seg", "seg_size"]
411
        },
412
        'record_op': {
413
            "interned_strings_usage": [
414
                "buffer_size", "free_memory", "number_of_strings",
415
                "used_memory"
416
            ],
417
            "memory_usage": [
418
                "current_wasted_percentage", "free_memory", "used_memory", "wasted_memory"
419
            ],
420
            "opcache_statistics": [
421
                "blacklist_miss_ratio", "blacklist_misses", "hash_restarts",
422
                "hits", "manual_restarts", "max_cached_keys", "misses",
423
                "num_cached_keys", "num_cached_scripts", "oom_restarts",
424
                "opcache_hit_rate"
425
            ]
426
        },
427
        'record_status': [
428
            "accepted conn", "active processes", "idle processes",
429
            "listen queue", "listen queue len", "max active processes",
430
            "max children reached", "max listen queue", "slow requests",
431
            "total processes"
432
        ],
433
        'rename': {
434
            'interned_strings_usage': 'strings',
435
            'memory_usage': 'mem',
436
            'opcache_statistics': 'cache',
437
            'buffer_size': 'buf_size',
438
            'free_memory': 'mem_free',
439
            'number_of_strings': 'num_str',
440
            'used_memory': 'mem_used',
441
            'current_wasted_percentage': 'waste_perc',
442
            'wasted_memory': 'mem_wasted',
443
            'blacklist_miss_ratio': 'bl_miss_ratio',
444
            'blacklist_misses': 'bl_miss',
445
            'hash_restarts': 'h_restarts',
446
            'manual_restarts': 'm_restarts',
447
            'max_cached_keys': 'max_keys',
448
            'num_cached_keys': 'num_keys',
449
            'num_cached_scripts': 'num_scripts',
450
            'opcache_hit_rate': 'hit_rate',
451
            'accepted conn': 'con_accepted',
452
            'active processes': 'proc_active',
453
            'idle processes': 'proc_idle',
454
            'listen queue': 'listen_q',
455
            'listen queue len': 'listen_q_len',
456
            'max active processes': 'proc_max_active',
457
            'max children reached': 'proc_max_child',
458
            'max listen queue': 'listen_q_max',
459
            'slow requests': 'req_slow',
460
            'total processes': 'proc_total'
461
        },
462
        'timeout': 10
463
    }
464
465
    def __init__(self, log, config):
466
        """Plugin to record nginx stub_status metrics.
467
468
        :param log: A logger
469
        :type log: logging.RootLogger
470
        :param config: a plumd.config.Conf configuration helper instance.
471
        :type config: plumd.config.Conf
472
        """
473
        super(Php, self).__init__(log, config)
474
        self.config.defaults(Php.defaults)
475
476
        self.fpm_status = self.config.get('fpm_status')
477
        self.fpm_status_args = self.config.get('fpm_status_args')
478
        self.record_status = self.config.get('record_status')
479
        self.pools = self.config.get('pools')
480
        self.timeout = self.config.get('timeout')
481
        self.record = self.config.get("record")
482
        self.record_op = self.config.get("record_op")
483
        self.rename = self.config.get("rename")
484
485
    def poll(self):
486
        """Query PHP-FPM for metrics over a FastCGI connection.
487
488
        Records php-fpm pool stats from pm.status_path and also
489
        output from a php script that returns both opcache and apc metrics.
490
491
        The php-fpm pool stats output is expected to be in json by setting a
492
        QUERY_STRING FastCGI parameter to json (currently does not support
493
        json&full).
494
495
        The opcache and apc metrics are parsed from a custom php script - see
496
        misc in the git repo for the script source.
497
498
        :rtype: ResultSet
499
        """
500
        result = plumd.Result("php")
501
        record = self.config.get('record')
502
        record_op = self.config.get('record_op')
503
        rename = self.config.get('rename')
504
        timeout = self.config.get('timeout')
505
        record_status = self.config.get('record_status')
506
        fpm_status = self.config.get('fpm_status')
507
        fpm_status_args = self.config.get('fpm_status_args')
508
        fpm_pools = self.config.get('fpm_pools')
509
510
        # check each configured pool
511
        for pname, pconf in fpm_pools.items():
512
            try:
513
                addr = pconf['socket']
514
                if addr is None or not os.path.exists(addr):
515
                    addr = (pconf['host'], pconf['port'])
516
                    sfamily = socket.AF_INET
517
                else:
518
                    sfamily = socket.AF_UNIX
519
                script = pconf['script']
520
                root = pconf['root']
521
            except KeyError as e:
522
                msg = "Php: pool {0} config invalid: {1}"
523
                self.log.error(msg.format(pname, e))
524
                continue
525
            # call the status script and get its output
526
            cinfo = {}
527
            try:
528
                sout, serr = fcgi_call(addr, timeout, sfamily, fpm_status,
0 ignored issues
show
Unused Code introduced by
The variable serr seems to be unused.
Loading history...
529
                                       root, fpm_status_args)
530
                lines = [line for line in sout.split("\n")
531
                         if line.startswith("{\"")]
532
                cinfo = json.loads("".join(lines))
533
            except Exception as exc:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
534
                msg = "Php: failed to call {0} for pool {1}: {2}"
535
                self.log.error(msg.format(fpm_status, pname, exc))
536
                continue
537
538
            # record fpm status metrics
539
            for metric in record_status:
540
                if metric not in cinfo:
541
                    continue
542
                mname = metric if metric not in rename else rename[metric]
543
                mstr = "{0}.fpm.{1}".format(pname, mname)
544
                result.add(plumd.Float(mstr, cinfo[metric]))
545
546
            # call the cache info script and get its output
547
            cinfo = {}
548
            try:
549
                sout, serr = fcgi_call(addr, timeout, sfamily, script, root,
550
                                       "")
551
                lines = [line for line in sout.split("\n")
552
                         if line.startswith("{\"")]
553
                cinfo = json.loads("".join(lines))
554
            except Exception as exc:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
555
                msg = "Php: failed to call {0} for pool {1}: {2}"
556
                self.log.error(msg.format(script, pname, exc))
557
                continue
558
559
            # apc
560
            for key, metrics in record.items():
561
                if key not in cinfo:
562
                    continue
563
                mkey = key if key not in rename else rename[key]
564
                for metric in metrics:
565
                    if metric not in cinfo[key]:
566
                        continue
567
                    mname = metric if metric not in rename else rename[metric]
568
                    mstr = "{0}.{1}.{2}".format(pname, mkey, mname)
569
                    result.add(plumd.Float(mstr, cinfo[key][metric]))
570
571
            # opcache
572
            for key, metrics in record_op.items():
573
                if key not in cinfo['op']:
574
                    continue
575
                mkey = key if key not in rename else rename[key]
576
                for metric in metrics:
577
                    if metric not in cinfo['op'][key]:
578
                        continue
579
                    mname = metric if metric not in rename else rename[metric]
580
                    mstr = "{0}.op.{1}.{2}".format(pname, mkey, mname)
581
                    result.add(plumd.Float(mstr, cinfo['op'][key][metric]))
582
583
        return plumd.ResultSet([result])
584
585
586
"""
587
Example php script and pm.status_path outputs:
588
589
Cache output:
590
{
591
    "apc": {
592
        "expunges": 0,
593
        "file_upload_progress": 1,
594
        "mem_size": 65600,
595
        "memory_type": "mmap",
596
        "num_entries": 100,
597
        "num_hits": 0,
598
        "num_inserts": 100,
599
        "num_misses": 0,
600
        "num_slots": 4099,
601
        "start_time": 1472356538,
602
        "ttl": 0
603
    },
604
    "op": {
605
        "cache_full": false,
606
        "interned_strings_usage": {
607
            "buffer_size": 8388608,
608
            "free_memory": 8050800,
609
            "number_of_strings": 3748,
610
            "used_memory": 337808
611
        },
612
        "memory_usage": {
613
            "current_wasted_percentage": 0.0081896781921387,
614
            "free_memory": 123263200,
615
            "used_memory": 10943536,
616
            "wasted_memory": 10992
617
        },
618
        "opcache_enabled": true,
619
        "opcache_statistics": {
620
            "blacklist_miss_ratio": 0,
621
            "blacklist_misses": 0,
622
            "hash_restarts": 0,
623
            "hits": 90,
624
            "last_restart_time": 0,
625
            "manual_restarts": 0,
626
            "max_cached_keys": 7963,
627
            "misses": 5,
628
            "num_cached_keys": 2,
629
            "num_cached_scripts": 2,
630
            "oom_restarts": 0,
631
            "opcache_hit_rate": 94.736842105263,
632
            "start_time": 1472356538
633
        },
634
        "restart_in_progress": false,
635
        "restart_pending": false
636
    }
637
}
638
639
640
641
Cache Output w/ SMA:
642
{
643
    "apc": {
644
        "expunges": 0,
645
        "file_upload_progress": 1,
646
        "mem_size": 65600,
647
        "memory_type": "mmap",
648
        "num_entries": 100,
649
        "num_hits": 0,
650
        "num_inserts": 100,
651
        "num_misses": 0,
652
        "num_slots": 4099,
653
        "start_time": 1472356538,
654
        "ttl": 0
655
    },
656
    "apc_sma": {
657
        "avail_mem": 33452504,
658
        "num_seg": 1,
659
        "seg_size": 33554296
660
    },
661
    "op": {
662
        "cache_full": false,
663
        "interned_strings_usage": {
664
            "buffer_size": 8388608,
665
            "free_memory": 8050800,
666
            "number_of_strings": 3748,
667
            "used_memory": 337808
668
        },
669
        "memory_usage": {
670
            "current_wasted_percentage": 0.0081896781921387,
671
            "free_memory": 123263200,
672
            "used_memory": 10943536,
673
            "wasted_memory": 10992
674
        },
675
        "opcache_enabled": true,
676
        "opcache_statistics": {
677
            "blacklist_miss_ratio": 0,
678
            "blacklist_misses": 0,
679
            "hash_restarts": 0,
680
            "hits": 90,
681
            "last_restart_time": 0,
682
            "manual_restarts": 0,
683
            "max_cached_keys": 7963,
684
            "misses": 5,
685
            "num_cached_keys": 2,
686
            "num_cached_scripts": 2,
687
            "oom_restarts": 0,
688
            "opcache_hit_rate": 94.736842105263,
689
            "start_time": 1472356538
690
        },
691
        "restart_in_progress": false,
692
        "restart_pending": false
693
    }
694
}
695
696
697
698
699
700
Normal output:
701
X-Powered-By: PHP/5.4.16
702
Expires: Thu, 01 Jan 1970 00:00:00 GMT
703
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
704
Content-Type: application/json
705
706
{
707
    "accepted conn": 164,
708
    "active processes": 1,
709
    "idle processes": 5,
710
    "listen queue": 0,
711
    "listen queue len": 128,
712
    "max active processes": 1,
713
    "max children reached": 0,
714
    "max listen queue": 0,
715
    "pool": "www",
716
    "process manager": "dynamic",
717
    "slow requests": 0,
718
    "start since": 16150,
719
    "start time": 1472356538,
720
    "total processes": 6
721
}
722
723
724
Full output:
725
X-Powered-By: PHP/5.4.16
726
Expires: Thu, 01 Jan 1970 00:00:00 GMT
727
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
728
Content-Type: application/json
729
730
{
731
    "accepted conn": 162,
732
    "active processes": 1,
733
    "idle processes": 5,
734
    "listen queue": 0,
735
    "listen queue len": 128,
736
    "max active processes": 1,
737
    "max children reached": 0,
738
    "max listen queue": 0,
739
    "pool": "www",
740
    "process manager": "dynamic",
741
    "processes": [
742
        {
743
            "content length": 0,
744
            "last request cpu": 0.0,
745
            "last request memory": 262144,
746
            "pid": 13170,
747
            "request duration": 790,
748
            "request method": "GET",
749
            "request uri": "/fpmstatus?json&full",
750
            "requests": 29,
751
            "script": "-",
752
            "start since": 15954,
753
            "start time": 1472356538,
754
            "state": "Idle",
755
            "user": "-"
756
        },
757
        {
758
            "content length": 0,
759
            "last request cpu": 0.0,
760
            "last request memory": 0,
761
            "pid": 13171,
762
            "request duration": 219,
763
            "request method": "GET",
764
            "request uri": "/fpmstatus?json&full",
765
            "requests": 29,
766
            "script": "-",
767
            "start since": 15954,
768
            "start time": 1472356538,
769
            "state": "Running",
770
            "user": "-"
771
        },
772
        {
773
            "content length": 0,
774
            "last request cpu": 3802.28,
775
            "last request memory": 262144,
776
            "pid": 13172,
777
            "request duration": 263,
778
            "request method": "GET",
779
            "request uri": "/fpmstatus?json",
780
            "requests": 28,
781
            "script": "-",
782
            "start since": 15954,
783
            "start time": 1472356538,
784
            "state": "Idle",
785
            "user": "-"
786
        },
787
        {
788
            "content length": 0,
789
            "last request cpu": 0.0,
790
            "last request memory": 262144,
791
            "pid": 13173,
792
            "request duration": 216,
793
            "request method": "GET",
794
            "request uri": "/fpmstatus?json",
795
            "requests": 28,
796
            "script": "-",
797
            "start since": 15954,
798
            "start time": 1472356538,
799
            "state": "Idle",
800
            "user": "-"
801
        },
802
        {
803
            "content length": 0,
804
            "last request cpu": 0.0,
805
            "last request memory": 262144,
806
            "pid": 13174,
807
            "request duration": 466,
808
            "request method": "GET",
809
            "request uri": "/fpmstatus?json&full",
810
            "requests": 28,
811
            "script": "-",
812
            "start since": 15954,
813
            "start time": 1472356538,
814
            "state": "Idle",
815
            "user": "-"
816
        },
817
        {
818
            "content length": 0,
819
            "last request cpu": 0.0,
820
            "last request memory": 262144,
821
            "pid": 15823,
822
            "request duration": 780,
823
            "request method": "GET",
824
            "request uri": "/fpmstatus?json&full",
825
            "requests": 20,
826
            "script": "-",
827
            "start since": 875,
828
            "start time": 1472371617,
829
            "state": "Idle",
830
            "user": "-"
831
        }
832
    ],
833
    "slow requests": 0,
834
    "start since": 15954,
835
    "start time": 1472356538,
836
    "total processes": 6
837
}
838
"""
0 ignored issues
show
Unused Code introduced by
This string statement has no effect and could be removed.
Loading history...
839