Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 1ec2605

Browse files
author
Gavin M. Roy
committed
Add PHP APC support (#23)
1 parent fcab31a commit 1ec2605

File tree

5 files changed

+229
-88
lines changed

5 files changed

+229
-88
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ newrelic_plugin_agent
44
An agent that polls supported backend systems and submits the results to the
55
NewRelic platform. Currently supported backend systems are:
66

7+
- Alternative PHP Cache
78
- Apache HTTP Server
89
- CouchDB
910
- Edgecast CDN "Realtime" API
@@ -15,6 +16,7 @@ NewRelic platform. Currently supported backend systems are:
1516
- RabbitMQ
1617
- Redis
1718
- Riak
19+
- uWSGI
1820

1921
IMPORTANT
2022
---------
@@ -102,6 +104,10 @@ You can also use a single mapping like follows:
102104

103105
The fields for plugin configurations can vary due to a plugin's configuration requirements. The name value in each stanza is only required when using multiple targets in a plugin. If it is only a single target, the name will be taken from the server's hostname.
104106

107+
APC Installation Notes
108+
----------------------
109+
Copy the apc-php.php script to a directory that can be served by your web server or php-fpm application. Edit the newrelic_plugin_agent configuration to point to the appropriate URL.
110+
105111
Apache HTTPd Installation Notes
106112
-------------------------------
107113
Enable the HTTPd server status page in the default virtual host. The following example configuration snippet for Apache HTTPd 2.2 demonstrates how to do this:
@@ -290,6 +296,15 @@ Configuration Example
290296
port: 6000
291297
user: stats
292298

299+
php_apc:
300+
scheme: http
301+
host: localhost
302+
port: 80
303+
path: /apc-nrp.php
304+
#username: foo
305+
#password: bar
306+
#verify_ssl_cert: t
307+
293308
postgresql:
294309
- host: localhost
295310
port: 5432

etc/apc-php.php

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
/**
3+
* PHP APC Data for NewRelic Plugin Agent
4+
*
5+
*/
6+
Header('Content-Type: application/json');
7+
echo json_encode(array('user_stats' => apc_cache_info('user', true),
8+
'shared_memory' => apc_sma_info(true)));

etc/newrelic/newrelic_plugin_agent.cfg

+96-88
Original file line numberDiff line numberDiff line change
@@ -5,107 +5,115 @@ Application:
55
poll_interval: 60
66
#proxy: http://localhost:8080
77

8-
#apache_httpd:
9-
# name: hostname
10-
# scheme: http
11-
# host: localhost
12-
# verify_ssl_cert: true
13-
# port: 80
14-
# path: /server-status
15-
16-
#couchdb:
17-
# name: localhost
18-
# host: localhost
19-
# verify_ssl_cert: true
20-
# port: 5984
21-
# username: foo
22-
# password: bar
23-
24-
#edgecast:
25-
# name: My Edgecase Account
26-
# account: YOUR_ACCOUNT_#
27-
# token: YOUR_API_TOKEN
28-
29-
#memcached:
30-
# name: localhost
31-
# host: localhost
32-
# port: 11211
33-
# path: /path/to/unix/socket
34-
35-
#mongodb:
36-
# name: hostname
37-
# host: localhost
38-
# port: 27017
39-
# databases:
40-
# - test
41-
# - yourdbname
42-
43-
#mongodb: # With Authentication
44-
# name: hostname
45-
# host: localhost
46-
# port: 27017
47-
# admin_username: user
48-
# admin_password: pass
49-
# databases:
50-
# test:
51-
# username: user
52-
# password: pass
53-
# yourdbname:
54-
# username: user
55-
# password: pass
56-
57-
#nginx:
58-
# name: hostname
59-
# host: localhost
60-
# port: 80
61-
# verify_ssl_cert: true
62-
# path: /nginx_stub_status
8+
# apache_httpd:
9+
# name: hostname
10+
# scheme: http
11+
# host: localhost
12+
# verify_ssl_cert: true
13+
# port: 80
14+
# path: /server-status
15+
16+
# couchdb:
17+
# name: localhost
18+
# host: localhost
19+
# verify_ssl_cert: true
20+
# port: 5984
21+
# username: foo
22+
# password: bar
23+
24+
# edgecast:
25+
# name: My Edgecase Account
26+
# account: YOUR_ACCOUNT_#
27+
# token: YOUR_API_TOKEN
28+
29+
# memcached:
30+
# name: localhost
31+
# host: localhost
32+
# port: 11211
33+
# path: /path/to/unix/socket
34+
35+
# mongodb:
36+
# name: hostname
37+
# host: localhost
38+
# port: 27017
39+
# databases:
40+
# - test
41+
# - yourdbname
42+
43+
# mongodb: # With Authentication
44+
# name: hostname
45+
# host: localhost
46+
# port: 27017
47+
# admin_username: user
48+
# admin_password: pass
49+
# databases:
50+
# test:
51+
# username: user
52+
# password: pass
53+
# yourdbname:
54+
# username: user
55+
# password: pass
56+
57+
# nginx:
58+
# name: hostname
59+
# host: localhost
60+
# port: 80
61+
# verify_ssl_cert: true
62+
# path: /nginx_stub_status
6363

6464
# pgbouncer:
6565
# host: localhost
6666
# port: 6000
6767
# user: stats
6868

69+
# php_apc:
70+
# name: hostname
71+
# scheme: http
72+
# host: localhost
73+
# verify_ssl_cert: true
74+
# port: 80
75+
# path: /apc-nrp.php
76+
6977
# postgresql:
7078
# host: localhost
7179
# port: 5432
7280
# user: postgres
7381
# dbname: postgres
7482
# superuser: False
7583

76-
#rabbitmq:
77-
# name: rabbitmq@localhost
78-
# host: localhost
79-
# port: 15672
80-
# verify_ssl_cert: true
81-
# username: guest
82-
# password: guest
83-
# detailed: False
84-
# resolve_dns: False
85-
86-
#redis:
87-
# - name: localhost
88-
# host: localhost
89-
# port: 6379
90-
# db_count: 16
91-
# password: foo # [OPTIONAL]
92-
# - name: localhost
93-
# host: localhost
94-
# port: 6380
95-
# db_count: 16
96-
# password: foo # [OPTIONAL]
97-
98-
#riak:
99-
# name: localhost
100-
# host: node0.riak0.scs.mtmeprod.net
101-
# verify_ssl_cert: true
102-
# port: 8098
103-
104-
#uwsgi:
105-
# name: localhost
106-
# host: localhost
107-
# port: 1717
108-
# path: /path/to/unix/socket
84+
# rabbitmq:
85+
# name: rabbitmq@localhost
86+
# host: localhost
87+
# port: 15672
88+
# verify_ssl_cert: true
89+
# username: guest
90+
# password: guest
91+
# detailed: False
92+
# resolve_dns: False
93+
94+
# redis:
95+
# - name: localhost
96+
# host: localhost
97+
# port: 6379
98+
# db_count: 16
99+
# password: foo # [OPTIONAL]
100+
# - name: localhost
101+
# host: localhost
102+
# port: 6380
103+
# db_count: 16
104+
# password: foo # [OPTIONAL]
105+
106+
# riak:
107+
# name: localhost
108+
# host: node0.riak0.scs.mtmeprod.net
109+
# verify_ssl_cert: true
110+
# port: 8098
111+
112+
# uwsgi:
113+
# name: localhost
114+
# host: localhost
115+
# port: 1717
116+
# path: /path/to/unix/socket
109117

110118
Daemon:
111119
user: newrelic

newrelic_plugin_agent/agent.py

+6
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ def start_plugin_polling(self):
263263
self.poll_plugin(plugin, pgbouncer.PgBouncer,
264264
self.application_config.get(plugin))
265265

266+
elif plugin == 'php_apc':
267+
if 'php_apc' not in globals():
268+
from newrelic_plugin_agent.plugins import php_apc
269+
self.poll_plugin(plugin, php_apc.APC,
270+
self.application_config.get(plugin))
271+
266272
elif plugin == 'postgresql':
267273
if 'postgresql' not in globals():
268274
from newrelic_plugin_agent.plugins import postgresql
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""
2+
PHP APC Support
3+
4+
"""
5+
import logging
6+
import requests
7+
import time
8+
9+
from newrelic_plugin_agent.plugins import base
10+
11+
LOGGER = logging.getLogger(__name__)
12+
13+
class APC(base.Plugin):
14+
15+
GUID = 'com.meetme.newrelic_php_apc_agent'
16+
17+
def add_datapoints(self, stats):
18+
"""Add all of the data points for a node
19+
20+
:param str stats: The stats content from APC as a string
21+
22+
"""
23+
shared_memory = stats.get('shared_memory', dict())
24+
self.add_gauge_value('Shared Memory/Available', 'Bytes',
25+
shared_memory.get('avail_mem', 0))
26+
self.add_gauge_value('Shared Memory/Segment Size', 'Bytes',
27+
shared_memory.get('seg_size', 0))
28+
self.add_gauge_value('Shared Memory/Segment Count', '',
29+
shared_memory.get('num_seg', 0))
30+
31+
user_stats = stats.get('user_stats', dict())
32+
self.add_gauge_value('User Cache/Slots', '',
33+
user_stats.get('num_slots', 0))
34+
self.add_gauge_value('User Cache/Entries', '',
35+
user_stats.get('num_entries', 0))
36+
self.add_gauge_value('User Cache/Size', 'Bytes',
37+
user_stats.get('mem_size', 0))
38+
self.add_gauge_value('User Cache/Expunges', '',
39+
user_stats.get('expunges', 0))
40+
41+
hits = user_stats.get('num_hits', 0)
42+
misses = user_stats.get('num_misses', 0)
43+
total = hits + misses
44+
if total > 0:
45+
effectiveness = float(float(hits) / float(total)) * 100
46+
else:
47+
effectiveness = 0
48+
self.add_gauge_value('User Cache/Effectiveness', '%', effectiveness)
49+
50+
self.add_derive_value('User Cache/Hits', '', hits)
51+
self.add_derive_value('User Cache/Misses', '', misses)
52+
self.add_derive_value('User Cache/Inserts', '',
53+
user_stats.get('num_inserts', 0))
54+
55+
@property
56+
def apc_stats_url(self):
57+
if 'scheme' not in self.config:
58+
self.config['scheme'] = 'http'
59+
return '%(scheme)s://%(host)s:%(port)s%(path)s?auto' % self.config
60+
61+
def fetch_data(self):
62+
"""Fetch the data from the APC script
63+
64+
:rtype: str
65+
66+
"""
67+
kwargs = {'url': self.apc_stats_url,
68+
'verify': self.config.get('verify_ssl_cert', True)}
69+
if 'username' in self.config and 'password' in self.config:
70+
kwargs['auth'] = (self.config['username'], self.config['password'])
71+
72+
try:
73+
response = requests.get(**kwargs)
74+
except requests.ConnectionError as error:
75+
LOGGER.error('Error polling APC Stats: %s', error)
76+
return {}
77+
78+
if response.status_code == 200:
79+
try:
80+
return response.json()
81+
except Exception as error:
82+
LOGGER.error('JSON decoding error: %r', error)
83+
return ''
84+
LOGGER.error('Error response from %s (%s): %s', self.apc_stats_url,
85+
response.status_code, response.content)
86+
return ''
87+
88+
def poll(self):
89+
LOGGER.info('Polling APC Stats via %s', self.apc_stats_url)
90+
start_time = time.time()
91+
if 'scheme' not in self.config:
92+
self.config['scheme'] = 'http'
93+
self.derive = dict()
94+
self.gauge = dict()
95+
self.rate = dict()
96+
data = self.fetch_data()
97+
if data:
98+
self.add_datapoints(data)
99+
LOGGER.info('Polling complete in %.2f seconds',
100+
time.time() - start_time)
101+
else:
102+
LOGGER.error('No data was returned from APC. Ensure '
103+
'configuration is correct and that %s is reachable '
104+
'by the agent', self.apc_stats_url)

0 commit comments

Comments
 (0)