Pull to refresh

Расширяем функциональность collectd путем добавления своих биндингов

Reading time 3 min
Views 4.5K
Здравствуй, %habraUser%
В этой статье пойдет речь о том, как расширять систему сбора статистики collectd путем добавления новых биндингов на python.
Эта статья является дополнением этой статьи

Постановка задачи


Нужно собирать данные с помощью collectd с модуля ustats для nginx. Нужно собирать HTTP 499, HTTP 500, HTTP 503 и TCP ошибки и выводить их на одном графике.

Решение задачи


После изучения способов сбора статистики collectd, стало ясно, что собирать данные следующими способами:
  • curl-json
  • exec плагин
  • python/perl/java плагин

Решил написать свой плагин на python чтобы детальнее разобраться в работе самого collectd

Алгоритм работы плагина следующий
  • Забрать данные
  • Распарсить их
  • Положить в collectd.

Забираем данные:

Любой модуль для collectd должен содержать в себе
import collectd
Подгрузим также нужные нам модули
import json, urllib2
В переменной url будем хранить ссылку, где расположены данные ustats в json формате, которую будем получать из конфига collectd.
url = None
Получаем данные с ustats
def fetch_data(url):
  response = urllib2.urlopen(urllib2.Request(url))
  data = json.loads(response.read())
  return data


Забираем данные из конфига:

def configure_callback(conf):
  global url
  for c in conf.children:
    if c.key  == 'UstatsURL':
      url = c.values[0]
    elif c.key == 'Verbose':
      VERBOSE_LOGGING = bool(c.values[0])
    else:
      collectd.warning ('ustats_info plugin: Unknown config key: %s.' % c.key)
  log_verbose('Configured with url=%s' % (url))


Далее, плагин у нас будет «читателем», соотвественно, нужно будет разобратся с функцией dispatch(), которая будет класть данные в collectd.
dispatch([type][, values][, plugin_instance][, type_instance][, plugin][, host][, time][, interval]) -> None.
type — тип данных
values — Актуальные значения, которые отправляются в collectd. Типы данных описаны в types.db
plugin_instance — инстанция плагина
type_instance — инстанция типа
plugin — имя плагина, по дефолту python
interval — Промежуток времени в секундах, между первой и второй отправкой одного и того же типа данных. Должно быть положительным целым числом. Если число отрицательное, то interval принимает значение по умолчанию(10 секунд)

Функция для отправки данных в collectd

def dispatch_value(plugin_instance, info, key, type, type_instance=None):
  if not type_instance:
    type_instance = key

  value = int(info)
  log_verbose('Sending value: %s=%s' % (type_instance, value))

  val = collectd.Values(plugin='ustats_info')
  val.plugin_instance = plugin_instance
  val.type = type
  val.type_instance = type_instance
  val.values = [value]
  val.dispatch()


collectd хранит данные по такой схеме:
host "/" plugin ["-" plugin instance] "/" type ["-" type instance]
Тут у нас:
plugin_instance = upstream
type = backend
type_instance = ошибки
На выходе мы получим примерно следующее:
host/plugin-upstream/backend-tcperror.rrd

Теперь основная функция, которая будет выполнятся collectd в цикле

def read_callback():
  global old_data

  log_verbose('Read callback called')
  data = fetch_data(url)
  last_backend = None
  if not data:
    collectd.error('ustats plugin: No data received')
    return
  if old_data == None:
    old_data = data

  upstreams = []
  for key in data.keys():
    upstreams.append(key)

  for upstream in upstreams:
    for index in range(len(data[upstream])):
      if data[upstream][index] and data[upstream][index] !=1 and  data[upstream][index][0] != last_backend:
        dispatch_value(upstream, getValue(data[upstream][index][4], old_data[upstream][index][4]), 'http_499_errors',  data[upstream][index][0].partition(":")[0])
        dispatch_value(upstream, getValue(data[upstream][index][5], old_data[upstream][index][5]), 'http_500_errors',  data[upstream][index][0].partition(":")[0])
        dispatch_value(upstream, getValue(data[upstream][index][6], old_data[upstream][index][6]), 'http_503_errors',  data[upstream][index][0].partition(":")[0])
        dispatch_value(upstream, getValue(data[upstream][index][7], old_data[upstream][index][7]), 'tcp_errors',  data[upstream][index][0].partition(":")[0])
        dispatch_value(upstream, getValue(data[upstream][index][13], old_data[upstream][index][13]), 'total_errors', data[upstream][index][0].partition(":")[0])
        last_backend = data[upstream][index][0]
  old_data = data


После регестрируем функцию читателя и конфигурирующую функцию
collectd.register_config(configure_callback)
collectd.register_read(read_callback)


Плагин готов, но мы использовали типы, которые не известны collectd, добавляем в my_types.db
backend value:GAUGE:0:65535
описываем плагин в collectd.conf
<Plugin python >
	ModulePath "/usr/lib/collectd/plugins/python" # Пусть до папки, где лежит модуль
	Import "ustats_info" # Импортируем модуль
	
	<Module ustats_info>
		UstatsURL "http://localhost/ustats?json"
		Verbose true
	</Module>
</Plugin>


Заключение


Графики получаются такого вида

Полный текст скрипта на GitHub
Надеюсь, что статья поможет кому-нибудь в расширении сбора статистики collectd
Tags:
Hubs:
+18
Comments 1
Comments Comments 1

Articles