Collecting Metrics from Ruby Processes with Zabbix Trappers

Article summary

A trapper and his brother, Noah, preparing a beaver trapJustin and I have recently started using Zabbix for monitoring, in place of Nagios. We’ve also taken the opportunity to start collecting even more metrics than before.

One nice thing about Zabbix is that it can use pre-existing Nagios monitoring plugins out of the box. But what if you also want to collect metrics from say, a Ruby process? You’re in luck! Zabbix can collect various forms of information (from numerical metrics to arbitrary strings, to log data) via the Zabbix sender protocol. Let’s set this up.

Server Side Setup

There are two things we’ll have to do from the Zabbix web interface: create a host, and create an item.

h3. Create a Host

First we’ll create a host. This could either be a real host for which we’ll track and monitor other attributes, or simply a dummy host acting as a container for the metrics we’d like to collect from one or more sources.

Under Configuration -> Hosts, click on Create to start creating a new host. We will need to enter a Host name (e.g. “myappserver”), add our host to at least one group (e.g. “mygroup” or “Linux servers”), and enter its I.P. address (or you can leave it as 127.0.0.1 if this is a just dummy host to hold items).

“Zabbix Manual Quickstart – New Host”:http://www.zabbix.com/documentation/2.0/manual/quickstart/host

h3. Create an Item

Next we’ll create an “item” on that host, which will receive and store our data. In the list of hosts, you should now see the host we created (“myappserver”). In that row, click the link for its items (“Items (0)”), then click “Create Item”.

The new item page gives us a lot of configurable parameters, but we’ll start with the essentials. First, we’ll want to specify that the “Type” of this item should be “Zabbix trapper”. Then, let’s fill in a “Name” (used in lists and a few other places around the web ui), a “Key” (the unique name and identifier for the data we’ll be collecting). Finally, we’ll also need to select the “Type of information” we want to collect (in our case, let’s use “Numeric (unsigned)” and use the default “Decimal” data type).

Click “Save”, and we’ll be ready to start receiving data.

“Zabbix Manual Quickstart – New Item”:http://www.zabbix.com/documentation/2.0/manual/quickstart/item

h2. Client Side (Implementing the Protocol)

Looking at the protocol definition page on the Zabbix wiki, we see that it is “…. Zabbix header… JSON”. JSON is easy, but what’s a “Zabbix header”? Looking at the snippet of Java source provided, it appears that the “Zabbix header” is the string of characters, ZBXD, followed by 0x01 and a bit-packed/padded integer representing the length of the JSON blob which follows. Not so bad. we can achieve this in a few lines of Ruby:

require 'json'

msg = {
  "request" => "sender data",
  "data" => [
    {
      "host" => "myappserver",
      "key" => "mydata",
      "value" => "1",
    }]
}
body = JSON.generate msg
data_length = body.bytesize
data_header = "ZBXD\1".encode("ascii") + \
              [data_length].pack("i") + \
              "\x00\x00\x00\x00"
data_to_send = data_header + body

Then we can simply send this off to the Zabbix server (listening on port 10051) like so:

s = TCPSocket.new("zabbixserver", 10051)
s.write message.to_s
response_header = s.recv(5)
if not response_header == "ZBXD\1"
  puts "response: #{response_header}"
  raise 'Got invalid response'
end
response_data_header = s.recv(8)
response_length = response_data_header[0,4].unpack("i")[0]
response_raw = s.recv(response_length)
s.close
response = JSON.load(response_raw)

In a future post, we’ll look at what we can do with the data we’ve collected.
 

Conversation
  • […] this nice article  as a base I have packed everthing in a class so that you can use […]

  • Comments are closed.