Hi
I've ported changes from apache plugin to support many instances into
the nginx plugin. Originally I've made this in 5.1.0 release, but as I
don't use it currently, I backported the changes to 4.10.1 (version
from Debian stable). This version I've tested in production and it
seems to work ok, so I'm sending it here with the hope for inclusion in
collectd sources (as multiple nginx instances on one host are quite
common "in the wild").
So once again: attached is a nginx plugin for collectd-4.10.1 (as
found in Debian stable) supporting many instances on on host.
I'm not sending it as a patch, because almost whole file is different,
so the patch file will be even bigger.
Of course this lacks an appropriate patch for collectd.conf manpage.
--
Greetings
Rafal Bisingier
/**
* collectd - src/nginx.c
* Copyright (C) 2006-2010 Florian octo Forster
* Copyright (C) 2008 Sebastian Harl
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
* Florian octo Forster <octo at collectd.org>
* Sebastian Harl <sh at tokkee.org>
**/
#include "collectd.h"
#include "common.h"
#include "plugin.h"
#include "configfile.h"
#include <curl/curl.h>
struct nginx_s
{
char *name;
char *host;
char *url;
char *user;
char *pass;
int verify_peer;
int verify_host;
char *cacert;
char *nginx_buffer;
char nginx_curl_error[CURL_ERROR_SIZE];
size_t nginx_buffer_size;
size_t nginx_buffer_fill;
CURL *curl;
}; /* nginx_s */
typedef struct nginx_s nginx_t;
/* TODO: Remove this prototype */
static int nginx_read_host (user_data_t *user_data);
static void nginx_free (nginx_t *st)
{
if (st == NULL)
return;
sfree (st->name);
sfree (st->host);
sfree (st->url);
sfree (st->user);
sfree (st->pass);
sfree (st->cacert);
sfree (st->nginx_buffer);
if (st->curl) {
curl_easy_cleanup(st->curl);
st->curl = NULL;
}
} /* nginx_free */
static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb,
void *user_data)
{
size_t len = size * nmemb;
nginx_t *st;
st = user_data;
if (st == NULL)
{
ERROR ("nginx plugin: nginx_curl_callback: "
"user_data pointer is NULL.");
return (0);
}
if (len <= 0)
return (len);
if ((st->nginx_buffer_fill + len) >= st->nginx_buffer_size)
{
char *temp;
temp = (char *) realloc (st->nginx_buffer,
st->nginx_buffer_fill + len + 1);
if (temp == NULL)
{
ERROR ("nginx plugin: realloc failed.");
return (0);
}
st->nginx_buffer = temp;
st->nginx_buffer_size = st->nginx_buffer_fill + len + 1;
}
memcpy (st->nginx_buffer + st->nginx_buffer_fill, (char *) buf, len);
st->nginx_buffer_fill += len;
st->nginx_buffer[st->nginx_buffer_fill] = 0;
return (len);
} /* int nginx_curl_callback */
/* Configuration handling functiions
* <Plugin nginx>
* <Instance "instance_name">
* URL ...
* </Instance>
* URL ...
* </Plugin>
*/
static int config_set_string (char **ret_string, /* {{{ */
oconfig_item_t *ci)
{
char *string;
if ((ci->values_num != 1)
|| (ci->values[0].type != OCONFIG_TYPE_STRING))
{
WARNING ("nginx plugin: The `%s' config option "
"needs exactly one string argument.", ci->key);
return (-1);
}
string = strdup (ci->values[0].value.string);
if (string == NULL)
{
ERROR ("nginx plugin: strdup failed.");
return (-1);
}
if (*ret_string != NULL)
free (*ret_string);
*ret_string = string;
return (0);
} /* }}} int config_set_string */
static int config_set_boolean (int *ret_boolean, /* {{{ */
oconfig_item_t *ci)
{
if ((ci->values_num != 1)
|| ((ci->values[0].type != OCONFIG_TYPE_BOOLEAN)
&& (ci->values[0].type != OCONFIG_TYPE_STRING)))
{
WARNING ("nginx plugin: The `%s' config option "
"needs exactly one boolean argument.", ci->key);
return (-1);
}
if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
{
if (ci->values[0].value.boolean)
*ret_boolean = 1;
else
*ret_boolean = 0;
}
else /* if (ci->values[0].type != OCONFIG_TYPE_STRING) */
{
char *string = ci->values[0].value.string;
if (strcmp (string, "true") == 0)
*ret_boolean = 1;
else if (IS_FALSE (string))
*ret_boolean = 0;
else
{
ERROR ("nginx plugin: Cannot parse string "
"as boolean value: %s", string);
return (-1);
}
}
return (0);
} /* }}} int config_set_boolean */
static int config_add (oconfig_item_t *ci) /* {{{ */
{
nginx_t *st;
int i;
int status;
if ((ci->values_num != 1)
|| (ci->values[0].type != OCONFIG_TYPE_STRING))
{
WARNING ("nginx plugin: The `%s' config option "
"needs exactly one string argument.", ci->key);
return (-1);
}
st = (nginx_t *) malloc (sizeof (*st));
if (st == NULL)
{
ERROR ("nginx plugin: malloc failed.");
return (-1);
}
memset (st, 0, sizeof (*st));
status = config_set_string (&st->name, ci);
if (status != 0)
{
sfree (st);
return (status);
}
assert (st->name != NULL);
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
if (strcasecmp ("URL", child->key) == 0)
status = config_set_string (&st->url, child);
else if (strcasecmp ("Host", child->key) == 0)
status = config_set_string (&st->host, child);
else if (strcasecmp ("User", child->key) == 0)
status = config_set_string (&st->user, child);
else if (strcasecmp ("Password", child->key) == 0)
status = config_set_string (&st->pass, child);
else if (strcasecmp ("VerifyPeer", child->key) == 0)
status = config_set_boolean (&st->verify_peer, child);
else if (strcasecmp ("VerifyHost", child->key) == 0)
status = config_set_boolean (&st->verify_host, child);
else if (strcasecmp ("CACert", child->key) == 0)
status = config_set_string (&st->cacert, child);
else
{
WARNING ("nginx plugin: Option `%s' not allowed here.",
child->key);
status = -1;
}
if (status != 0)
break;
}
/* Check if struct is complete.. */
if ((status == 0) && (st->url == NULL))
{
ERROR ("nginx plugin: Instance `%s': "
"No URL has been configured.",
st->name);
status = -1;
}
if (status == 0)
{
user_data_t ud;
char callback_name[3*DATA_MAX_NAME_LEN];
memset (&ud, 0, sizeof (ud));
ud.data = st;
ud.free_func = (void *) nginx_free;
memset (callback_name, 0, sizeof (callback_name));
ssnprintf (callback_name, sizeof (callback_name),
"nginx/%s/%s",
(st->host != NULL) ? st->host : hostname_g,
(st->name != NULL) ? st->name : "default"),
status = plugin_register_complex_read (/* group = */ NULL,
/* name = */ callback_name,
/* callback = */ nginx_read_host,
/* interval = */ NULL,
/* user_data = */ &ud);
}
if (status != 0)
{
nginx_free(st);
return (-1);
}
return (0);
} /* int config_add }}} */
static int config (oconfig_item_t *ci)
{
int status = 0;
int i;
oconfig_item_t *lci = NULL; /* legacy config */
for (i = 0; i < ci->children_num; i++)
{
oconfig_item_t *child = ci->children + i;
if (strcasecmp ("Instance", child->key) == 0 && child->children_num > 0)
config_add (child);
else
{
/* legacy mode - convert to <Instance ...> config */
if (lci == NULL)
{
lci = malloc (sizeof(*lci));
if (lci == NULL)
{
ERROR ("nginx plugin: malloc failed.");
return (-1);
}
memset (lci, '\0', sizeof (*lci));
}
lci->children_num++;
lci->children =
realloc (lci->children,
lci->children_num * sizeof (*child));
if (lci->children == NULL)
{
ERROR ("nginx plugin: realloc failed.");
return (-1);
}
memcpy (&lci->children[lci->children_num-1], child, sizeof (*child));
}
} /* for (ci->children) */
if (lci)
{
/* create a <Instance ""> entry */
lci->key = "Instance";
lci->values_num = 1;
lci->values = (oconfig_value_t *) malloc (lci->values_num * sizeof (oconfig_value_t));
lci->values[0].type = OCONFIG_TYPE_STRING;
lci->values[0].value.string = "";
status = config_add (lci);
sfree (lci->values);
sfree (lci->children);
sfree (lci);
}
return (status);
} /* int config */
/* initialize curl for each host */
static int init_host (nginx_t *st) /* {{{ */
{
static char credentials[1024];
assert (st->url != NULL);
/* (Assured by `config_add') */
if (st->curl != NULL)
{
curl_easy_cleanup (st->curl);
st->curl = NULL;
}
if ((st->curl = curl_easy_init ()) == NULL)
{
ERROR ("nginx plugin: init_host: `curl_easy_init' failed.");
return (-1);
}
curl_easy_setopt (st->curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt (st->curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
curl_easy_setopt (st->curl, CURLOPT_WRITEDATA, st);
curl_easy_setopt (st->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
curl_easy_setopt (st->curl, CURLOPT_ERRORBUFFER, st->nginx_curl_error);
if (st->user != NULL)
{
int status;
status = ssnprintf (credentials, sizeof (credentials), "%s:%s",
st->user, (st->pass == NULL) ? "" : st->pass);
if ((status < 0) || ((size_t) status >= sizeof (credentials)))
{
ERROR ("nginx plugin: init_host: Returning an error "
"because the credentials have been "
"truncated.");
curl_easy_cleanup (st->curl);
st->curl = NULL;
return (-1);
}
curl_easy_setopt (st->curl, CURLOPT_USERPWD, credentials);
}
curl_easy_setopt (st->curl, CURLOPT_URL, st->url);
curl_easy_setopt (st->curl, CURLOPT_FOLLOWLOCATION, 1);
if (st->verify_peer != 0)
{
curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 1);
}
else
{
curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYPEER, 0);
}
if (st->verify_host != 0)
{
curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 2);
}
else
{
curl_easy_setopt (st->curl, CURLOPT_SSL_VERIFYHOST, 0);
}
if (st->cacert != NULL)
{
curl_easy_setopt (st->curl, CURLOPT_CAINFO, st->cacert);
}
return (0);
} /* }}} int init_host */
static void submit_value (const char *type, const char *type_instance,
value_t value, nginx_t *st)
{
value_list_t vl = VALUE_LIST_INIT;
vl.values = &value;
vl.values_len = 1;
sstrncpy (vl.host, (st->host != NULL) ? st->host : hostname_g,
sizeof (vl.host));
sstrncpy (vl.plugin, "nginx", sizeof (vl.plugin));
if (st->name != NULL)
sstrncpy (vl.plugin_instance, st->name,
sizeof (vl.plugin_instance));
sstrncpy (vl.type, type, sizeof (vl.type));
if (type_instance != NULL)
sstrncpy (vl.type_instance, type_instance,
sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
} /* void submit_value */
static void submit_counter (const char *type, const char *type_instance,
counter_t c, nginx_t *st)
{
value_t v;
v.counter = c;
submit_value (type, type_instance, v, st);
} /* void submit_counter */
/*
static void submit_derive (const char *type, const char *type_instance,
derive_t c, nginx_t *st)
{
value_t v;
v.derive = c;
submit_value (type, type_instance, v, st);
}*/ /* void submit_derive */
static void submit_gauge (const char *type, const char *type_instance,
gauge_t g, nginx_t *st)
{
value_t v;
v.gauge = g;
submit_value (type, type_instance, v, st);
} /* void submit_gauge */
static int nginx_read_host (user_data_t *user_data) /* {{{ */
{
int i;
char *ptr;
char *saveptr;
char *lines[16];
int lines_num = 0;
char *fields[16];
int fields_num;
nginx_t *st;
st = user_data->data;
assert (st->url != NULL);
/* (Assured by `config_add') */
if (st->curl == NULL)
{
int status;
status = init_host (st);
if (status != 0)
return (-1);
}
assert (st->curl != NULL);
st->nginx_buffer_fill = 0;
if (curl_easy_perform (st->curl) != 0)
{
ERROR ("nginx: curl_easy_perform failed: %s",
st->nginx_curl_error);
return (-1);
}
ptr = st->nginx_buffer;
saveptr = NULL;
while ((lines[lines_num] = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
{
ptr = NULL;
lines_num++;
if (lines_num >= 16)
break;
}
/*
* Active connections: 291
* server accepts handled requests
* 16630948 16630948 31070465
* Reading: 6 Writing: 179 Waiting: 106
*/
for (i = 0; i < lines_num; i++)
{
fields_num = strsplit (lines[i], fields, (sizeof (fields) / sizeof (fields[0])));
if (fields_num == 3)
{
if ((strcmp (fields[0], "Active") == 0)
&& (strcmp (fields[1], "connections:") == 0))
submit_gauge ("nginx_connections", "active",
atoll (fields[2]), st);
else if ((atoll (fields[0]) != 0)
&& (atoll (fields[1]) != 0)
&& (atoll (fields[2]) != 0))
submit_counter ("nginx_requests", "",
atoll (fields[2]), st);
}
else if (fields_num == 6)
{
if ((strcmp (fields[0], "Reading:") == 0)
&& (strcmp (fields[2], "Writing:") == 0)
&& (strcmp (fields[4], "Waiting:") == 0))
{
submit_gauge ("nginx_connections", "reading", atoll (fields[1]), st);
submit_gauge ("nginx_connections", "writing", atoll (fields[3]), st);
submit_gauge ("nginx_connections", "waiting", atoll (fields[5]), st);
}
}
}
st->nginx_buffer_fill = 0;
return (0);
} /* }}} int nginx_read_host */
void module_register (void)
{
plugin_register_complex_config ("nginx", config);
} /* void module_register */
/* vim: set sw=8 noet fdm=marker : */
_______________________________________________
collectd mailing list
[email protected]
http://mailman.verplant.org/listinfo/collectd