collect的python plugin内嵌了一个python的解释器,可以方便我们用python自定义collectd的plugin,各个callback的函数定义可以参考collectd python plugin。比如自定义一个统计cpu利用率的plugin:
cpu_usage_plugin.py
#!/usr/bin/env python
import subprocess
import traceback
def get_cpu_usage():
cmd = "grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage}'"
result = run(cmd)
for line in result:
try:
usage = float(line)
return usage
except:
raise Exception("Failed to parse cpu usage")
def run(cmd):
try:
result = []
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, close_fds=True)
(stdout, stderr) = proc.communicate()
output = stdout.split("\n")
for line in output:
if line == '':
continue
result.append(line)
return result
except Exception as err:
raise Exception("failed to execute command: %s, reason: %s" % (' '.join(cmd), err.message))
class CPUStatMon(object):
def __init__(self):
self.plugin_name = "cpu_stat"
self.interval = 30
self.hostname = None
self.verbose_logging = False
def log_verbose(self, msg):
if not self.verbose_logging:
return
collectd.info('%s plugin [verbose]: %s' % (self.plugin_name, msg))
'''
to store/initialize the internal state like a socket connection
def init(self):
self.SOCKET_CONN = create_sockect_conn()
'''
def configure_callback(self, conf):
for node in conf.children:
val = str(node.values[0])
if node.key == "HostName":
self.hostname = val
elif node.key == 'Interval':
self.interval = int(float(val))
elif node.key == 'Verbose':
self.verbose_logging = val in ['True', 'true']
elif node.key == 'PluginName':
self.plugin_name = val
else:
collectd.warning('[plugin] %s: unknown config key: %s' % (self.plugin_name, node.key))
def dispatch_value(self, plugin, host, type, type_instance, value):
self.log_verbose("Dispatching value plugin=%s, host=%s, type=%s, type_instance=%s, value=%s" %
(plugin, host, type, type_instance, str(value)))
val = collectd.Values(type=type)
val.plugin = plugin
val.host = host
val.type_instance = type_instance
val.interval = self.interval
val.values = [value]
val.dispatch()
self.log_verbose("Dispatched value plugin=%s, host=%s, type=%s, type_instance=%s, value=%s" %
(plugin, host, type, type_instance, str(value)))
def read_callback(self):
try:
usage = get_cpu_usage()
type = 'cpu_usage_percent'
type_instance = "used"
value = usage
self.dispatch_value(self.plugin_name, self.hostname, type, type_instance, value)
except Exception as exp:
self.log_verbose(traceback.print_exc())
self.log_verbose("plugin %s run into exception" % (self.plugin_name))
self.log_verbose(exp.message)
if __name__ == '__main__':
result = get_cpu_usage()
print result
else:
import collectd
cpu_status_mon = CPUStatMon()
collectd.register_config(cpu_status_mon.configure_callback)
'''
# register the init function
collectd.register_init(cpu_status_mon.init)
'''
collectd.register_read(cpu_status_mon.read_callback)
collectd.conf中enable python plugin并增加如下配置:
<Plugin python>
ModulePath "/opt/collectd/lib/collectd"
Import "cpu_usage_plugin"
<Module cpu_usage_plugin>
Interval 30
PluginName "cpu_usage"
HostName "demo_host"
Verbose false
</Module>
</Plugin>
值得注意的是:
- 对于dispatch_value函数中type参数的值,可以直接传入字符串gauge,derived,count等collectd的内建数据类型,也可以在types.db中对这些数据类型做一个map,看起来更有意义,如下所示:
types.db cpu_usage_percentage value:GAUGE:0:U
- 如果想维持某些变量的状态,可以把该通过regiseter_init来注册包含该变量的函数,如代码中被注释掉的地方。
- 如果有多个自定义的python plugin,在<Plugin python></Plugin>片段中使用多个Import导入相应的模块,并定义多个<Module></Module>片段。
- 默认情况下,collectd的log在syslog中。