From: Sebastian Harl Date: Sat, 5 Jul 2008 10:03:51 +0000 (+0200) Subject: postgresql plugin: Added a plugin to collect PostgreSQL database statistics. X-Git-Tag: collectd-4.5.0~101 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=a0b4b6b4dc1039670f17854b296281d3286a8e01;p=collectd.git postgresql plugin: Added a plugin to collect PostgreSQL database statistics. This plugin collects active server connections, transaction numbers, block IO and table row manipulations of PostgreSQL databases. For this purpose it keeps persistent connections to all configured databases. The statistics are collected from PostgreSQL's "statistics collector" subsystem. Currently, statistics are collected from the pg_stat_database, pg_stat_user_tables and pg_statio_user_tables views. The following new types have been introduced: pg_blks, pg_n_tup_c, pg_n_tup_g, pg_numbackends, pg_scan and pg_xact. The type names are basically the same as the column names in the queried views to keep things consistent. For the number of tuples, counter and gauge values exist, so the type names have been prefixed with "_c" and "_g" respectively. Sample plugin configuration: Host "hostname" Port "5432" User "username" Password "secret" SSLMode "prefer" KRBSrvName "kerberos_service_name" Service "service_name" Signed-off-by: Sebastian Harl Signed-off-by: Florian Forster --- diff --git a/README b/README index ca2bdf6b..97119ac5 100644 --- a/README +++ b/README @@ -141,6 +141,10 @@ Features Network latency: Time to reach the default gateway or another given host. + - postgresql + PostgreSQL database statistics: active server connections, transaction + numbers, block IO, table row manipulations. + - processes Process counts: Number of running, sleeping, zombie, ... processes. @@ -364,6 +368,9 @@ Prerequisites Obviously used by the `perl' plugin. The library has to be compiled with ithread support (introduced in Perl 5.6.0). + * libpq (optional) + The PostgreSQL C client library used by the `postgresql' plugin. + * librrd (optional; headers and library; rrdtool 1.0 and 1.2 both work fine) If built without `librrd' the resulting binary will be `client only', i.e. will send its values via multicast and not create any RRD files itself. diff --git a/configure.in b/configure.in index 73fa8410..6873d8f1 100644 --- a/configure.in +++ b/configure.in @@ -2113,6 +2113,93 @@ then AC_SUBST(BUILD_WITH_OPENIPMI_LIBS) fi +dnl Check for libpq. +with_pg_config="pg_config" +with_libpq_includedir="" +with_libpq_libdir="" +with_libpq_cppflags="" +with_libpq_ldflags="" +AC_ARG_WITH(libpq, [AS_HELP_STRING([--with-libpq@<:@=PREFIX@:>@], + [Path to libpq.])], +[ + if test "x$withval" = "xno" + then + with_libpq="no" + else if test "x$withval" = "xyes" + then + with_libpq="yes" + else + if test -f "$withval" && test -x "$withval"; + then + with_pg_config="$withval" + else if test -x "$withval/bin/pg_config" + then + with_pg_config="$withval/bin/pg_config" + fi; fi + with_libpq="yes" + fi; fi +], +[ + with_libpq="yes" +]) +if test "x$with_libpq" = "xyes" +then + with_libpq_includedir=`$with_pg_config --includedir 2> /dev/null` + pg_config_status=$? + + if test $pg_config_status -eq 0 + then + if test -n "$with_libpq_includedir"; then + for dir in $with_libpq_includedir; do + with_libpq_cppflags="$with_libpq_cppflags -I$dir" + done + fi + else + AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) + fi + + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libpq_cppflags" + + AC_CHECK_HEADERS(libpq-fe.h, [], + [with_libpq="no (libpq-fe.h not found)"], []) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libpq" = "xyes" +then + with_libpq_libdir=`$with_pg_config --libdir 2> /dev/null` + pg_config_status=$? + + if test $pg_config_status -eq 0 + then + if test -n "$with_libpq_libdir"; then + for dir in $with_libpq_libdir; do + with_libpq_ldflags="$with_libpq_ldflags -L$dir" + done + fi + else + AC_MSG_WARN([$with_pg_config returned with status $pg_config_status]) + fi + + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $with_libpq_ldflags" + + AC_CHECK_LIB(pq, PQconnectdb, + [with_libpq="yes"], + [with_libpq="no (symbol 'PQconnectdb' not found)"]) + + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_libpq" = "xyes" +then + BUILD_WITH_LIBPQ_CPPFLAGS="$with_libpq_cppflags" + BUILD_WITH_LIBPQ_LDFLAGS="$with_libpq_ldflags" + AC_SUBST(BUILD_WITH_LIBPQ_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBPQ_LDFLAGS) +fi +AM_CONDITIONAL(BUILD_WITH_LIBPQ, test "x$with_libpq" = "xyes") + dnl Check for libvirt and libxml2 libraries. with_libxml2="no (pkg-config isn't available)" with_libxml2_cflags="" @@ -2544,6 +2631,7 @@ AC_PLUGIN([ntpd], [yes], [NTPd statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) AC_PLUGIN([ping], [$with_liboping], [Network latency statistics]) +AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) AC_PLUGIN([processes], [$plugin_processes], [Process statistics]) AC_PLUGIN([qmail], [yes], [QMail queue statistics]) @@ -2655,6 +2743,7 @@ Configuration: libpcap . . . . . . . $with_libpcap libperl . . . . . . . $with_libperl libpthread . . . . . $with_libpthread + libpq . . . . . . . . $with_libpq librrd . . . . . . . $with_rrdtool libsensors . . . . . $with_lm_sensors libstatgrab . . . . . $with_libstatgrab @@ -2709,6 +2798,7 @@ Configuration: nut . . . . . . . . . $enable_nut perl . . . . . . . . $enable_perl ping . . . . . . . . $enable_ping + postgresql . . . . . $enable_postgresql powerdns . . . . . . $enable_powerdns processes . . . . . . $enable_processes qmail . . . . . . . . $enable_qmail diff --git a/src/Makefile.am b/src/Makefile.am index 4f0496db..3d8b1466 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -536,6 +536,16 @@ collectd_LDADD += "-dlopen" ping.la collectd_DEPENDENCIES += ping.la endif +if BUILD_PLUGIN_POSTGRESQL +pkglib_LTLIBRARIES += postgresql.la +postgresql_la_SOURCES = postgresql.c +postgresql_la_CPPFLAGS = $(BUILD_WITH_LIBPQ_CPPFLAGS) +postgresql_la_LDFLAGS = -module -avoid-version \ + $(BUILD_WITH_LIBPQ_LDFLAGS) -lpq +collectd_LDADD += "-dlopen" postgresql.la +collectd_DEPENDENCIES += postgresql.la +endif + if BUILD_PLUGIN_POWERDNS pkglib_LTLIBRARIES += powerdns.la powerdns_la_SOURCES = powerdns.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index e76f997a..f73ac99c 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -51,6 +51,7 @@ FQDNLookup true @BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut @BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl @BUILD_PLUGIN_PING_TRUE@LoadPlugin ping +@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql @BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns @BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes @BUILD_PLUGIN_RRDTOOL_TRUE@LoadPlugin rrdtool @@ -247,6 +248,22 @@ FQDNLookup true # TTL 255 # +# +# +# Host "hostname" +# Port 5432 +# User "username" +# Password "secret" +# +# SSLMode "prefer" +# KRBSrvName "kerberos_service_name" +# +# +# +# Service "service_name" +# +# + # # # Collect "latency" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 107b3afa..faff2ffe 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -948,6 +948,110 @@ Sets the Time-To-Live of generated ICMP packets. =back +=head2 Plugin C + +The C plugin queries statistics from PostgreSQL databases. It +keeps a persistent connection to all configured databases and tries to +reconnect if the connection has been interrupted. The statistics are collected +from PostgreSQL's B which thus has to be enabled for +this plugin to work correctly. This should usually be the case by default. +See the section "The Statistics Collector" of the B +for details. + +The B manual can be found at +L. + + + + Host "hostname" + Port "5432" + User "username" + Password "secret" + SSLMode "prefer" + KRBSrvName "kerberos_service_name" + + + Service "service_name" + + + +=over 4 + +=item B block + +The B block defines one PostgreSQL database for which to collect +statistics. It accepts a single mandatory argument which specifies the +database name. None of the other options are required. PostgreSQL will use +default values as documented in the section "CONNECTING TO A DATABASE" in the +L manpage. However, be aware that those defaults may be influenced by +the user collectd is run as and special environment variables. See the manpage +for details. + +=item B I + +Specify the hostname or IP of the PostgreSQL server to connect to. If the +value begins with a slash, it is interpreted as the directory name in which to +look for the UNIX domain socket. + +This option is also used to determine the hostname that is associated with a +collected data set. If it has been omitted or either begins with with a slash +or equals B it will be replaced with the global hostname definition +of collectd. Any other value will be passed literally to collectd when +dispatching values. Also see the global B and B options. + +=item B I + +Specify the TCP port or the local UNIX domain socket file extension of the +server. + +=item B I + +Specify the username to be used when connecting to the server. + +=item B I + +Specify the password to be used when connecting to the server. + +=item B I|I|I|I + +Specify whether to use an SSL connection when contacting the server. The +following modes are supported: + +=over 4 + +=item I + +Do not use SSL at all. + +=item I + +First, try to connect without using SSL. If that fails, try using SSL. + +=item I (default) + +First, try to connect using SSL. If that fails, try without using SSL. + +=item I + +Use SSL only. + +=back + +=item B I + +Specify the Kerberos service name to use when authenticating with Kerberos 5 +or GSSAPI. See the sections "Kerberos authentication" and "GSSAPI" of the +B for details. + +=item B I + +Specify the PostgreSQL service name to use for additional parameters. That +service has to be defined in F and holds additional +connection parameters. See the section "The Connection Service File" in the +B for details. + +=back + =head2 Plugin C The C plugin queries statistics from an authoritative PowerDNS @@ -1652,6 +1756,7 @@ L, L, L, L, +L, L, L diff --git a/src/postgresql.c b/src/postgresql.c new file mode 100644 index 00000000..40f8ec57 --- /dev/null +++ b/src/postgresql.c @@ -0,0 +1,519 @@ +/** + * collectd - src/postgresql.c + * 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; only version 2 of the License is applicable. + * + * 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 + * + * Author: + * Sebastian Harl + **/ + +/* + * This module collects PostgreSQL database statistics. + */ + +#include "collectd.h" +#include "common.h" + +#include "configfile.h" +#include "plugin.h" + +#include "utils_complain.h" + +#include +#include + +#define log_err(...) ERROR ("postgresql: " __VA_ARGS__) +#define log_warn(...) WARNING ("postgresql: " __VA_ARGS__) +#define log_info(...) INFO ("postgresql: " __VA_ARGS__) + +/* Appends the (parameter, value) pair to the string + * pointed to by 'buf' suitable to be used as argument + * for PQconnectdb(). If value equals NULL, the pair + * is ignored. */ +#define C_PSQL_PAR_APPEND(buf, buf_len, parameter, value) \ + if ((0 < (buf_len)) && (NULL != (value)) && ('\0' != *(value))) { \ + int s = ssnprintf (buf, buf_len, " %s = '%s'", parameter, value); \ + if (0 < s) { \ + buf += s; \ + buf_len -= s; \ + } \ + } + +/* Returns the tuple (major, minor, patchlevel) + * for the given version number. */ +#define C_PSQL_SERVER_VERSION3(server_version) \ + (server_version) / 10000, \ + (server_version) / 100 - (int)((server_version) / 10000) * 100, \ + (server_version) - (int)((server_version) / 100) * 100 + +/* Returns true if the given host specifies a + * UNIX domain socket. */ +#define C_PSQL_IS_UNIX_DOMAIN_SOCKET(host) \ + ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host))) + +/* Returns the tuple (host, delimiter, port) for a + * given (host, port) pair. Depending on the value of + * 'host' a UNIX domain socket or a TCP socket is + * assumed. */ +#define C_PSQL_SOCKET3(host, port) \ + ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \ + C_PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \ + port + +typedef struct { + PGconn *conn; + c_complain_t conn_complaint; + + /* user configuration */ + char *host; + char *port; + char *database; + char *user; + char *password; + + char *sslmode; + + char *krbsrvname; + + char *service; +} c_psql_database_t; + +static c_psql_database_t *databases = NULL; +static int databases_num = 0; + +static void submit (const c_psql_database_t *db, + const char *type, const char *type_instance, + value_t *values, size_t values_len) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = values; + vl.values_len = values_len; + vl.time = time (NULL); + + if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host) + || (0 == strcmp (db->host, "localhost"))) + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + else + sstrncpy (vl.host, db->host, sizeof (vl.host)); + + sstrncpy (vl.plugin, "postgresql", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, db->database, sizeof (vl.plugin_instance)); + + sstrncpy (vl.type, type, sizeof (vl.type)); + + if (NULL != type_instance) + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); + return; +} /* submit */ + +static void submit_counter (const c_psql_database_t *db, + const char *type, const char *type_instance, + const char *value) +{ + value_t values[1]; + + if ((NULL == value) || ('\0' == *value)) + return; + + values[0].counter = atoll (value); + submit (db, type, type_instance, values, 1); + return; +} /* submit_counter */ + +static void submit_gauge (const c_psql_database_t *db, + const char *type, const char *type_instance, + const char *value) +{ + value_t values[1]; + + if ((NULL == value) || ('\0' == *value)) + return; + + values[0].gauge = atof (value); + submit (db, type, type_instance, values, 1); + return; +} /* submit_gauge */ + +static int c_psql_check_connection (c_psql_database_t *db) +{ + /* "ping" */ + PQclear (PQexec (db->conn, "SELECT 42;")); + + if (CONNECTION_OK != PQstatus (db->conn)) { + PQreset (db->conn); + + /* trigger c_release() */ + if (0 == db->conn_complaint.interval) + db->conn_complaint.interval = 1; + + if (CONNECTION_OK != PQstatus (db->conn)) { + c_complain (LOG_ERR, &db->conn_complaint, + "Failed to connect to database %s: %s", + db->database, PQerrorMessage (db->conn)); + return -1; + } + } + + c_release (LOG_INFO, &db->conn_complaint, + "Successfully reconnected to database %s", PQdb (db->conn)); + return 0; +} /* c_psql_check_connection */ + +static int c_psql_stat_database (c_psql_database_t *db) +{ + const char *const query = + "SELECT numbackends, xact_commit, xact_rollback " + "FROM pg_stat_database " + "WHERE datname = $1;"; + + PGresult *res; + + int n; + + res = PQexecParams (db->conn, query, /* number of parameters */ 1, + NULL, (const char *const *)&db->database, NULL, NULL, + /* return text data */ 0); + + if (PGRES_TUPLES_OK != PQresultStatus (res)) { + log_err ("Failed to execute SQL query: %s", + PQerrorMessage (db->conn)); + log_info ("SQL query was: %s", query); + PQclear (res); + return -1; + } + + n = PQntuples (res); + if (1 < n) { + log_warn ("pg_stat_database has more than one entry " + "for database %s - ignoring additional results.", + db->database); + } + else if (1 > n) { + log_err ("pg_stat_database has no entry for database %s", + db->database); + PQclear (res); + return -1; + } + + submit_gauge (db, "pg_numbackends", NULL, PQgetvalue (res, 0, 0)); + + submit_counter (db, "pg_xact", "commit", PQgetvalue (res, 0, 1)); + submit_counter (db, "pg_xact", "rollback", PQgetvalue (res, 0, 2)); + + PQclear (res); + return 0; +} /* c_psql_stat_database */ + +static int c_psql_stat_user_tables (c_psql_database_t *db) +{ + const char *const query = + "SELECT sum(seq_scan), sum(seq_tup_read), " + "sum(idx_scan), sum(idx_tup_fetch), " + "sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del), " + "sum(n_tup_hot_upd), sum(n_live_tup), sum(n_dead_tup) " + "FROM pg_stat_user_tables;"; + + PGresult *res; + + int n; + + res = PQexec (db->conn, query); + + if (PGRES_TUPLES_OK != PQresultStatus (res)) { + log_err ("Failed to execute SQL query: %s", + PQerrorMessage (db->conn)); + log_info ("SQL query was: %s", query); + PQclear (res); + return -1; + } + + n = PQntuples (res); + assert (1 >= n); + + if (1 > n) /* no user tables */ + return 0; + + submit_counter (db, "pg_scan", "seq", PQgetvalue (res, 0, 0)); + submit_counter (db, "pg_scan", "seq_tup_read", PQgetvalue (res, 0, 1)); + submit_counter (db, "pg_scan", "idx", PQgetvalue (res, 0, 2)); + submit_counter (db, "pg_scan", "idx_tup_fetch", PQgetvalue (res, 0, 3)); + + submit_counter (db, "pg_n_tup_c", "ins", PQgetvalue (res, 0, 4)); + submit_counter (db, "pg_n_tup_c", "upd", PQgetvalue (res, 0, 5)); + submit_counter (db, "pg_n_tup_c", "del", PQgetvalue (res, 0, 6)); + submit_counter (db, "pg_n_tup_c", "hot_upd", PQgetvalue (res, 0, 7)); + + submit_gauge (db, "pg_n_tup_g", "live", PQgetvalue (res, 0, 8)); + submit_gauge (db, "pg_n_tup_g", "dead", PQgetvalue (res, 0, 9)); + + PQclear (res); + return 0; +} /* c_psql_stat_user_tables */ + +static int c_psql_statio_user_tables (c_psql_database_t *db) +{ + const char *const query = + "SELECT sum(heap_blks_read), sum(heap_blks_hit), " + "sum(idx_blks_read), sum(idx_blks_hit), " + "sum(toast_blks_read), sum(toast_blks_hit), " + "sum(tidx_blks_read), sum(tidx_blks_hit) " + "FROM pg_statio_user_tables;"; + + PGresult *res; + + int n; + + res = PQexec (db->conn, query); + + if (PGRES_TUPLES_OK != PQresultStatus (res)) { + log_err ("Failed to execute SQL query: %s", + PQerrorMessage (db->conn)); + log_info ("SQL query was: %s", query); + PQclear (res); + return -1; + } + + n = PQntuples (res); + assert (1 >= n); + + if (1 > n) /* no user tables */ + return 0; + + submit_counter (db, "pg_blks", "heap_read", PQgetvalue (res, 0, 0)); + submit_counter (db, "pg_blks", "heap_hit", PQgetvalue (res, 0, 1)); + + submit_counter (db, "pg_blks", "idx_read", PQgetvalue (res, 0, 2)); + submit_counter (db, "pg_blks", "idx_hit", PQgetvalue (res, 0, 3)); + + submit_counter (db, "pg_blks", "toast_read", PQgetvalue (res, 0, 4)); + submit_counter (db, "pg_blks", "toast_hit", PQgetvalue (res, 0, 5)); + + submit_counter (db, "pg_blks", "tidx_read", PQgetvalue (res, 0, 6)); + submit_counter (db, "pg_blks", "tidx_hit", PQgetvalue (res, 0, 7)); + + PQclear (res); + return 0; +} /* c_psql_statio_user_tables */ + +static int c_psql_read (void) +{ + int success = 0; + int i; + + for (i = 0; i < databases_num; ++i) { + c_psql_database_t *db = databases + i; + + assert (NULL != db->database); + + if (0 != c_psql_check_connection (db)) + continue; + + c_psql_stat_database (db); + c_psql_stat_user_tables (db); + c_psql_statio_user_tables (db); + + ++success; + } + + if (! success) + return -1; + return 0; +} /* c_psql_read */ + +static int c_psql_shutdown (void) +{ + int i; + + if ((NULL == databases) || (0 == databases_num)) + return 0; + + plugin_unregister_read ("postgresql"); + plugin_unregister_shutdown ("postgresql"); + + for (i = 0; i < databases_num; ++i) { + c_psql_database_t *db = databases + i; + + PQfinish (db->conn); + + sfree (db->database); + sfree (db->host); + sfree (db->port); + sfree (db->user); + sfree (db->password); + + sfree (db->sslmode); + + sfree (db->krbsrvname); + + sfree (db->service); + } + + sfree (databases); + databases_num = 0; + return 0; +} /* c_psql_shutdown */ + +static int c_psql_init (void) +{ + int i; + + if ((NULL == databases) || (0 == databases_num)) + return 0; + + for (i = 0; i < databases_num; ++i) { + c_psql_database_t *db = databases + i; + + char conninfo[4096]; + char *buf = conninfo; + int buf_len = sizeof (conninfo); + int status; + + char *server_host; + int server_version; + + status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database); + if (0 < status) { + buf += status; + buf_len -= status; + } + + C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host); + C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port); + C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user); + C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password); + C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode); + C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname); + C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service); + + db->conn = PQconnectdb (conninfo); + if (0 != c_psql_check_connection (db)) + continue; + + server_host = PQhost (db->conn); + server_version = PQserverVersion (db->conn); + log_info ("Sucessfully connected to database %s (user %s) " + "at server %s%s%s (server version: %d.%d.%d, " + "protocol version: %d, pid: %d)", + PQdb (db->conn), PQuser (db->conn), + C_PSQL_SOCKET3(server_host, PQport (db->conn)), + C_PSQL_SERVER_VERSION3 (server_version), + PQprotocolVersion (db->conn), PQbackendPID (db->conn)); + } + + plugin_register_read ("postgresql", c_psql_read); + plugin_register_shutdown ("postgresql", c_psql_shutdown); + return 0; +} /* c_psql_init */ + +static int config_set (char *name, char **var, const oconfig_item_t *ci) +{ + if ((0 != ci->children_num) || (1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err ("%s expects a single string argument.", name); + return 1; + } + + sfree (*var); + *var = sstrdup (ci->values[0].value.string); + return 0; +} /* config_set */ + +static int c_psql_config_database (oconfig_item_t *ci) +{ + c_psql_database_t *db; + + int i; + + if ((1 != ci->values_num) + || (OCONFIG_TYPE_STRING != ci->values[0].type)) { + log_err (" expects a single string argument."); + return 1; + } + + ++databases_num; + if (NULL == (databases = (c_psql_database_t *)realloc (databases, + databases_num * sizeof (*databases)))) { + log_err ("Out of memory."); + exit (5); + } + + db = databases + (databases_num - 1); + + db->conn = NULL; + + db->conn_complaint.last = 0; + db->conn_complaint.interval = 0; + + db->database = sstrdup (ci->values[0].value.string); + db->host = NULL; + db->port = NULL; + db->user = NULL; + db->password = NULL; + + db->sslmode = NULL; + + db->krbsrvname = NULL; + + db->service = NULL; + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "Host")) + config_set ("Host", &db->host, c); + else if (0 == strcasecmp (c->key, "Port")) + config_set ("Port", &db->port, c); + else if (0 == strcasecmp (c->key, "User")) + config_set ("User", &db->user, c); + else if (0 == strcasecmp (c->key, "Password")) + config_set ("Password", &db->password, c); + else if (0 == strcasecmp (c->key, "SSLMode")) + config_set ("SSLMode", &db->sslmode, c); + else if (0 == strcasecmp (c->key, "KRBSrvName")) + config_set ("KRBSrvName", &db->krbsrvname, c); + else if (0 == strcasecmp (c->key, "Service")) + config_set ("Service", &db->service, c); + else + log_warn ("Ignoring unknown config key \"%s\".", c->key); + } + return 0; +} + +static int c_psql_config (oconfig_item_t *ci) +{ + int i; + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (0 == strcasecmp (c->key, "Database")) + c_psql_config_database (c); + else + log_warn ("Ignoring unknown config key \"%s\".", c->key); + } + return 0; +} /* c_psql_config */ + +void module_register (void) +{ + plugin_register_complex_config ("postgresql", c_psql_config); + plugin_register_init ("postgresql", c_psql_init); +} /* module_register */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ + diff --git a/src/types.db b/src/types.db index 8350fb47..fabae124 100644 --- a/src/types.db +++ b/src/types.db @@ -63,6 +63,12 @@ nfs_procedure value:COUNTER:0:4294967295 nginx_connections value:GAUGE:0:U nginx_requests value:COUNTER:0:134217728 percent percent:GAUGE:0:100.1 +pg_blks value:COUNTER:0:U +pg_n_tup_c value:COUNTER:0:U +pg_n_tup_g value:GAUGE:0:U +pg_numbackends value:GAUGE:0:U +pg_scan value:COUNTER:0:U +pg_xact value:COUNTER:0:U ping ping:GAUGE:0:65535 players value:GAUGE:0:1000000 power value:GAUGE:0:U