Skip to content

Instantly share code, notes, and snippets.

@hajimeni
Last active May 28, 2019 05:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hajimeni/7e6d780d74df2963cca3c600f349fce2 to your computer and use it in GitHub Desktop.
Save hajimeni/7e6d780d74df2963cca3c600f349fce2 to your computer and use it in GitHub Desktop.
DataDog Jolokia AgentCheck
# stdlib
import re
import copy
# 3rd party
import requests
# project
from checks import AgentCheck
class JolokiaCheck(AgentCheck):
"""Tracks customized jolokia metrics via the status module
config example
---
init_configs: []
instances:
- host: 192.168.65.1
port: 3333
beans:
- { "mbean": "com.zaxxer.hikari:type=Pool (*" } ## -> jolokia.com_zaxxer_hikari_type_pool_db.active_connections
- { "mbean": "com.zaxxer.hikari:type=Pool (*", "mbean_re":"com\\.zaxxer\\.hikari:type=Pool\\ \\((?P<name>.+)\\)", "metric_key": "hikaricp_pool.{name}.{attribute}" } ## -> jolokia.hiraricp_pool.db.active_connections
tags:
- "host:hoge"
- "jolokia:test"
"""
DEFAULT_BEANS= [
{ 'mbean': 'java.lang:type=Threading', 'attribute': ['ThreadCount','TotalStartedThreadCount'], 'metric_key': '{type}.{attribute}', 'mbean_re': 'java.lang:type=(?P<type>Threading)' },
{ 'mbean': 'java.lang:name=*,type=MemoryPool', 'attribute': ['Usage'], 'metric_key': '{type}.{name}.{attribute}.{key}', 'mbean_re': 'java.lang:name=(?P<name>.+),type=(?P<type>MemoryPool)' },
{ 'mbean': 'java.lang:name=*,type=GarbageCollector', 'attribute': 'CollectionTime,CollectionCount', 'metric_key': '{type}.{name}.{attribute}', 'mbean_re': 'java.lang:name=(?P<name>.+),type=(?P<type>GarbageCollector)' },
]
JOLOKIA_PREFIX = 'jolokia'
def check(self, instance):
self.log.debug('======== instance:{}'.format(instance))
if 'host' not in instance and 'port' not in instance:
raise Exception('Jolokia instance missing "host" or "port" value.')
beans = []
beans.extend(JolokiaCheck.DEFAULT_BEANS)
beans.extend(instance.get('beans', []))
payload = [self.__to_payload_cassette(b) for b in beans]
host = instance['host']
port = instance['port']
tags = instance.get('tags', [])
service_check_tags = ['host:%s' % host, 'port:%s' % port]
service_check_name = JolokiaCheck.JOLOKIA_PREFIX + '.can_connect'
base_url = 'http://{}:{}/jolokia/'.format(host, port)
try:
res = requests.post(base_url, json=payload)
res.raise_for_status()
except Exception:
self.service_check(service_check_name, AgentCheck.CRITICAL,
tags=service_check_tags)
raise
else:
self.service_check(service_check_name, AgentCheck.OK,
tags=service_check_tags)
json_data = res.json()
res_values = []
if len(json_data) != len(beans):
raise Exception('Response length not equals to request')
for info, bean in zip(json_data, beans):
if not 'value' in info:
self.log.warning('Jolokia response error: {}'.format(info))
continue
value = info['value']
if '*' in bean['mbean']:
for mbean_name, attr_name_value in value.items():
for attr_name, attr_value in attr_name_value.items():
res_values.extend(self.__extract_attr_value(mbean_name, attr_name, attr_value, bean))
else:
for attr_name, attr_value in value.items():
res_values.extend(self.__extract_attr_value(bean['mbean'], attr_name, attr_value, bean))
funcs = {
'gauge': self.gauge,
'rate': self.rate,
'count': self.count
}
for v in res_values:
func = funcs[v['metric_func']]
func(str(v['metric_name']), v['value'], tags)
return
def __extract_attr_value(self, mbean_name, attr_name, attr_value, bean):
res = []
d_template = {
'mbean': JolokiaCheck.normalize_bean_name(mbean_name),
'attribute': JolokiaCheck.normalize_bean_name(attr_name)
}
if 'mbean_re' in bean:
match = re.match(bean['mbean_re'], mbean_name)
if match:
for k, v in match.groupdict().items():
d_template[k] = JolokiaCheck.normalize_bean_name(v)
metric_key = bean.get('metric_key', '{mbean}.{attribute}')
if isinstance(attr_value, dict):
for key, value in attr_value.items():
if 'include_keys' in bean and not key in bean['include_keys']:
continue
if 'exlude_keys' in bean and key in bean['exclude_keys']:
continue
res.append(
{
'metric_name': JolokiaCheck.JOLOKIA_PREFIX + '.' + metric_key.format(key=key, **d_template),
'value': value,
'metric_func': bean.get('metric_type', 'gauge')
}
)
else:
res.append(
{
'metric_name': JolokiaCheck.JOLOKIA_PREFIX + '.' + metric_key.format(**d_template),
'value': attr_value,
'metric_func': bean.get('metric_type', 'gauge')
}
)
return res
def __to_payload_cassette(self, bean):
cassette = {
'type': 'read',
'mbean': bean['mbean']
}
if 'attribute' in bean:
attr = bean['attribute']
if isinstance(attr, str):
attr = attr.split(',')
cassette['attribute'] = attr
if 'path' in bean:
cassette['path'] = bean['path']
return cassette
@classmethod
def normalize_bean_name(cls, name):
if not name:
return None
n = TO_SNAKECASE_RE.sub(r'_\1', name).lower().strip('_') ## camplecase to snakecase
return TO_NORMALIZE_RE.sub('_', n) ## replace not alphanumeric to underscore
TO_SNAKECASE_RE = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
TO_NORMALIZE_RE = re.compile('[^a-zA-Z0-9]+|__+')
if __name__ == '__main__':
check, instances = JolokiaCheck.from_yaml('/etc/dd-agent/checks.d/jolokia.yaml')
for instance in instances:
print "\nRunning the check against url: %s" % (instance['host'])
check.check(instance)
if check.has_events():
print 'Events: %s' % (check.get_events())
ms = check.get_metrics()
ms.sort()
print 'Metrics:================(count={})'.format(len(ms))
for e in ms:
print('{}'.format(e))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment