endif
if BUILD_WITH_LIBYAJL2
-noinst_LTLIBRARIES += libformat_gcm.la
-libformat_gcm_la_SOURCES = \
- src/utils_format_gcm.c \
- src/utils_format_gcm.h
-libformat_gcm_la_CPPFLAGS = \
+noinst_LTLIBRARIES += libformat_stackdriver.la
+libformat_stackdriver_la_SOURCES = \
+ src/utils_format_stackdriver.c \
+ src/utils_format_stackdriver.h
+libformat_stackdriver_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(BUILD_WITH_LIBYAJL_CPPFLAGS)
-libformat_gcm_la_LIBADD = \
+libformat_stackdriver_la_LIBADD = \
libavltree.la \
$(BUILD_WITH_LIBSSL_LIBS) \
$(BUILD_WITH_LIBYAJL_LIBS)
-check_PROGRAMS += test_format_gcm
-TESTS += test_format_gcm
-test_format_gcm_SOURCES = \
- src/utils_format_gcm_test.c \
+check_PROGRAMS += test_format_stackdriver
+TESTS += test_format_stackdriver
+test_format_stackdriver_SOURCES = \
+ src/utils_format_stackdriver_test.c \
src/testing.h
-test_format_gcm_LDADD = \
- libformat_gcm.la \
+test_format_stackdriver_LDADD = \
+ libformat_stackdriver.la \
libplugin_mock.la \
-lm
endif
write_http_la_LIBADD = libformat_json.la $(BUILD_WITH_LIBCURL_LIBS)
endif
-if BUILD_PLUGIN_WRITE_GCM
-pkglib_LTLIBRARIES += write_gcm.la
-write_gcm_la_SOURCES = src/write_gcm.c
-write_gcm_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-write_gcm_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
-write_gcm_la_LIBADD = libformat_gcm.la libgce.la liboauth.la \
- $(BUILD_WITH_LIBCURL_LIBS)
-endif
-
if BUILD_PLUGIN_WRITE_KAFKA
pkglib_LTLIBRARIES += write_kafka.la
write_kafka_la_SOURCES = src/write_kafka.c
write_sensu_la_LDFLAGS = $(PLUGIN_LDFLAGS)
endif
+if BUILD_PLUGIN_WRITE_STACKDRIVER
+pkglib_LTLIBRARIES += write_stackdriver.la
+write_stackdriver_la_SOURCES = src/write_stackdriver.c
+write_stackdriver_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_stackdriver_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
+write_stackdriver_la_LIBADD = libformat_stackdriver.la libgce.la liboauth.la \
+ $(BUILD_WITH_LIBCURL_LIBS)
+endif
+
if BUILD_PLUGIN_WRITE_TSDB
pkglib_LTLIBRARIES += write_tsdb.la
write_tsdb_la_SOURCES = src/write_tsdb.c
fi
if test "x$with_libcurl" = "xyes" && test "x$with_libssl" = "xyes" && test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then
- plugin_write_gcm="yes"
+ plugin_write_stackdriver="yes"
fi
if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"; then
AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
AC_PLUGIN([write_graphite], [yes], [Graphite / Carbon output plugin])
AC_PLUGIN([write_http], [$with_libcurl], [HTTP output plugin])
-AC_PLUGIN([write_gcm], [$plugin_write_gcm], [Google cloud monitoring output plugin])
+AC_PLUGIN([write_stackdriver], [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin])
AC_PLUGIN([write_kafka], [$with_librdkafka], [Kafka output plugin])
AC_PLUGIN([write_log], [yes], [Log output plugin])
AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin])
AC_MSG_RESULT([ wireless . . . . . . $enable_wireless])
AC_MSG_RESULT([ write_graphite . . . $enable_write_graphite])
AC_MSG_RESULT([ write_http . . . . . $enable_write_http])
-AC_MSG_RESULT([ write_gcm . . . . . . $enable_write_gcm])
AC_MSG_RESULT([ write_kafka . . . . . $enable_write_kafka])
AC_MSG_RESULT([ write_log . . . . . . $enable_write_log])
AC_MSG_RESULT([ write_mongodb . . . . $enable_write_mongodb])
AC_MSG_RESULT([ write_redis . . . . . $enable_write_redis])
AC_MSG_RESULT([ write_riemann . . . . $enable_write_riemann])
AC_MSG_RESULT([ write_sensu . . . . . $enable_write_sensu])
+AC_MSG_RESULT([ write_stackdriver . . $enable_write_stackdriver])
AC_MSG_RESULT([ write_tsdb . . . . . $enable_write_tsdb])
AC_MSG_RESULT([ xencpu . . . . . . . $enable_xencpu])
AC_MSG_RESULT([ xmms . . . . . . . . $enable_xmms])
#@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem
#@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
#@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
-#@BUILD_PLUGIN_WRITE_GCM_TRUE@LoadPlugin write_gcm
#@BUILD_PLUGIN_WRITE_GRAPHITE_TRUE@LoadPlugin write_graphite
#@BUILD_PLUGIN_WRITE_HTTP_TRUE@LoadPlugin write_http
#@BUILD_PLUGIN_WRITE_KAFKA_TRUE@LoadPlugin write_kafka
#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
#@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
#@BUILD_PLUGIN_WRITE_SENSU_TRUE@LoadPlugin write_sensu
+#@BUILD_PLUGIN_WRITE_STACKDRIVER_TRUE@LoadPlugin write_stackdriver
#@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb
#@BUILD_PLUGIN_XENCPU_TRUE@LoadPlugin xencpu
#@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
# Verbose false
#</Plugin>
-#<Plugin write_gcm>
-# Project "stackdriver-account"
-# CredentialFile "/path/to/gcp-project-id-12345.json"
-# Email "123456789012@developer.gserviceaccount.com"
-# <Resource "global">
-# project_id "gcp-project-id"
-# </Resource>
-# Url "https://monitoring.googleapis.com/v3"
-#</Plugin>
-
#<Plugin write_graphite>
# <Node "example">
# Host "localhost"
# Attribute "foo" "bar"
#</Plugin>
+#<Plugin write_stackdriver>
+# Project "stackdriver-account"
+# CredentialFile "/path/to/gcp-project-id-12345.json"
+# Email "123456789012@developer.gserviceaccount.com"
+# <Resource "global">
+# project_id "gcp-project-id"
+# </Resource>
+# Url "https://monitoring.googleapis.com/v3"
+#</Plugin>
+
#<Plugin write_tsdb>
# <Node>
# Host "localhost"
collect on-wire traffic you could, for example, use the logging facilities of
iptables to feed data for the guest IPs into the iptables plugin.
-=head2 Plugin C<write_gcm>
-
-The C<write_gcm> plugin writes metrics to the I<Google Cloud Monitoring> (GCM)
-service.
-
-This plugin supports two authentication methods: When configured, credentials
-are read from the JSON credentials file specified with B<CredentialFile>.
-Alternatively, when running on
-I<Google Compute Engine> (GCE), an I<OAuth> token is retrieved from the
-I<metadata server> and used to authenticate to GCM.
-
-B<Synopsis:>
-
- <Plugin write_gcm>
- CredentialFile "/path/to/service_account.json"
- <Resource "global">
- project_id "monitored_project"
- </Resource>
- </Plugin>
-
-=over 4
-
-=item B<CredentialFile> I<file>
-
-Path to a JSON credentials file holding the credentials for a GCP service
-account.
-
-If not specified, I<Application Default Credentials>. If running on GCE,
-B<Email> may be set to chose a different service account associated with the
-instance.
-
-=item B<Project> I<Project>
-
-The I<Project ID> or the I<Project Number> of the I<Stackdriver Account>. The
-I<Project ID> is a string identifying the GCP project, which you can chose
-freely when creating a new project. The I<Project Number> is a 12-digit decimal
-number. You can look up both on the I<Developer Console>.
-
-This setting is optional. If not set, the project ID is read from the
-credentials file or determined from the GCE's metadata service.
-
-=item B<Email> I<Email>
-
-Email address of an GCE I<Service Account>. This setting is only effective when
-running on GCE and using I<Application Default Credentials> (see
-B<CredentialFile> above).
-
-=item B<Resource> I<ResourceType>
-
-Configures the I<Monitored Resource> to use when storing metrics. This option
-takes a I<ResourceType> and arbitrary string options which are used as labels.
-
-On GCE, defaults to the equivalent of this config:
-
- <Resource "gce_instance">
- project_id "${meta/project/project-id}"
- instance_id "${meta/instance/id}"
- zone "${meta/instance/zone}"
- </Resource>
-
-Where C<${meta/...}> are values read from the meta data service.
-
-When not running on GCE, defaults to the equivalent of this config:
-
- <Resource "global">
- project_id "${Project}"
- </Resource>
-
-Where C<${Project}> refers to the B<Project> option.
-
-See L<https://cloud.google.com/monitoring/api/resources> for more information
-on I<Monitored Resource Types>.
-
-=item B<Url> I<Url>
-
-URL of the I<Google Cloud Monitoring> API. Defaults to
-C<https://monitoring.googleapis.com/v3>.
-
-=back
-
=head2 Plugin C<write_graphite>
The C<write_graphite> plugin writes data to I<Graphite>, an open-source metrics
=back
+=head2 Plugin C<write_stackdriver>
+
+The C<write_stackdriver> plugin writes metrics to the
+I<Google Stackdriver Monitoring> service.
+
+This plugin supports two authentication methods: When configured, credentials
+are read from the JSON credentials file specified with B<CredentialFile>.
+Alternatively, when running on
+I<Google Compute Engine> (GCE), an I<OAuth> token is retrieved from the
+I<metadata server> and used to authenticate to GCM.
+
+B<Synopsis:>
+
+ <Plugin write_stackdriver>
+ CredentialFile "/path/to/service_account.json"
+ <Resource "global">
+ project_id "monitored_project"
+ </Resource>
+ </Plugin>
+
+=over 4
+
+=item B<CredentialFile> I<file>
+
+Path to a JSON credentials file holding the credentials for a GCP service
+account.
+
+If not specified, I<Application Default Credentials>. If running on GCE,
+B<Email> may be set to chose a different service account associated with the
+instance.
+
+=item B<Project> I<Project>
+
+The I<Project ID> or the I<Project Number> of the I<Stackdriver Account>. The
+I<Project ID> is a string identifying the GCP project, which you can chose
+freely when creating a new project. The I<Project Number> is a 12-digit decimal
+number. You can look up both on the I<Developer Console>.
+
+This setting is optional. If not set, the project ID is read from the
+credentials file or determined from the GCE's metadata service.
+
+=item B<Email> I<Email>
+
+Email address of an GCE I<Service Account>. This setting is only effective when
+running on GCE and using I<Application Default Credentials> (see
+B<CredentialFile> above).
+
+=item B<Resource> I<ResourceType>
+
+Configures the I<Monitored Resource> to use when storing metrics. This option
+takes a I<ResourceType> and arbitrary string options which are used as labels.
+
+On GCE, defaults to the equivalent of this config:
+
+ <Resource "gce_instance">
+ project_id "${meta/project/project-id}"
+ instance_id "${meta/instance/id}"
+ zone "${meta/instance/zone}"
+ </Resource>
+
+Where C<${meta/...}> are values read from the meta data service.
+
+When not running on GCE, defaults to the equivalent of this config:
+
+ <Resource "global">
+ project_id "${Project}"
+ </Resource>
+
+Where C<${Project}> refers to the B<Project> option.
+
+See L<https://cloud.google.com/monitoring/api/resources> for more information
+on I<Monitored Resource Types>.
+
+=item B<Url> I<Url>
+
+URL of the I<Google Cloud Monitoring> API. Defaults to
+C<https://monitoring.googleapis.com/v3>.
+
+=back
+
=head2 Plugin C<xencpu>
This plugin collects metrics of hardware CPU load for machine running Xen
+++ /dev/null
-/**
- * collectd - src/utils_format_gcm.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_FORMAT_GCM_H
-#define UTILS_FORMAT_GCM_H 1
-
-#include "collectd.h"
-#include "plugin.h"
-
-/* gcm_output_t is a buffer to which value_list_t* can be added and from which
- * an appropriately formatted char* can be read. */
-struct gcm_output_s;
-typedef struct gcm_output_s gcm_output_t;
-
-/* gcm_resource_t represents a MonitoredResource. */
-struct gcm_resource_s;
-typedef struct gcm_resource_s gcm_resource_t;
-
-gcm_output_t *gcm_output_create(gcm_resource_t *res);
-
-/* gcm_output_destroy frees all memory used by out, including the
- * gcm_resource_t* passed to gcm_output_create. */
-void gcm_output_destroy(gcm_output_t *out);
-
-/* gcm_output_add adds a value_list_t* to "out".
- *
- * Return values:
- * - 0 Success
- * - ENOBUFS Success, but the buffer should be flushed soon.
- * - EEXIST The value list is already encoded in the buffer.
- * Flush the buffer, then call gcm_output_add again.
- * - ENOENT First time we encounter this metric. Create a metric descriptor
- * using the GCM API and then call gcm_output_register_metric.
- */
-int gcm_output_add(gcm_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* gcm_output_register_metric adds the metric descriptor which vl maps to, to
- * the list of known metric descriptors. */
-int gcm_output_register_metric(gcm_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* gcm_output_reset resets the output and returns the previous content of the
- * buffer. It is the caller's responsibility to call free() with the returned
- * pointer. */
-char *gcm_output_reset(gcm_output_t *out);
-
-gcm_resource_t *gcm_resource_create(char const *type);
-void gcm_resource_destroy(gcm_resource_t *res);
-int gcm_resource_add_label(gcm_resource_t *res, char const *key,
- char const *value);
-
-/* gcm_format_metric_descriptor creates the payload for a
- * projects.metricDescriptors.create() request. */
-int gcm_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index);
-
-#endif /* UTILS_FORMAT_GCM_H */
/**
- * collectd - src/utils_format_gcm.c
+ * collectd - src/utils_format_stackdriver.c
* ISC license
*
* Copyright (C) 2017 Florian Forster
#include "collectd.h"
-#include "utils_format_gcm.h"
+#include "utils_format_stackdriver.h"
#include "common.h"
#include "plugin.h"
#include <yajl/yajl_version.h>
#endif
-struct gcm_output_s {
- gcm_resource_t *res;
+struct sd_output_s {
+ sd_resource_t *res;
yajl_gen gen;
c_avl_tree_t *staged;
c_avl_tree_t *metric_descriptors;
};
-struct gcm_label_s {
+struct sd_label_s {
char *key;
char *value;
};
-typedef struct gcm_label_s gcm_label_t;
+typedef struct sd_label_s sd_label_t;
-struct gcm_resource_s {
+struct sd_resource_s {
char *type;
- gcm_label_t *labels;
+ sd_label_t *labels;
size_t labels_num;
};
* }
* }
*/
-static int format_gcm_resource(yajl_gen gen, gcm_resource_t *res) /* {{{ */
+static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
{
int status;
*/
static int format_time_series(yajl_gen gen, data_set_t const *ds,
value_list_t const *vl, int ds_index,
- gcm_resource_t *res) {
+ sd_resource_t *res) {
/* {{{ */
yajl_gen_map_open(gen);
* ],
* }
*/
-static int gcm_output_initialize(gcm_output_t *out) /* {{{ */
+static int sd_output_initialize(sd_output_t *out) /* {{{ */
{
yajl_gen_map_open(out->gen);
yajl_gen_array_open(out->gen);
return 0;
-} /* }}} int gcm_output_initialize */
+} /* }}} int sd_output_initialize */
-static int gcm_output_finalize(gcm_output_t *out) /* {{{ */
+static int sd_output_finalize(sd_output_t *out) /* {{{ */
{
yajl_gen_array_close(out->gen);
yajl_gen_map_close(out->gen);
return 0;
-} /* }}} int gcm_output_finalize */
+} /* }}} int sd_output_finalize */
-static void gcm_output_reset_staged(gcm_output_t *out) /* {{{ */
+static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
{
void *key = NULL;
while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
sfree(key);
-} /* }}} void gcm_output_reset_staged */
+} /* }}} void sd_output_reset_staged */
-gcm_output_t *gcm_output_create(gcm_resource_t *res) /* {{{ */
+sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
{
- gcm_output_t *out = calloc(1, sizeof(*out));
+ sd_output_t *out = calloc(1, sizeof(*out));
if (out == NULL)
return NULL;
out->gen = yajl_gen_alloc(/* funcs = */ NULL);
if (out->gen == NULL) {
- gcm_output_destroy(out);
+ sd_output_destroy(out);
return NULL;
}
out->staged = c_avl_create((void *)strcmp);
if (out->staged == NULL) {
- gcm_output_destroy(out);
+ sd_output_destroy(out);
return NULL;
}
out->metric_descriptors = c_avl_create((void *)strcmp);
if (out->metric_descriptors == NULL) {
- gcm_output_destroy(out);
+ sd_output_destroy(out);
return NULL;
}
- gcm_output_initialize(out);
+ sd_output_initialize(out);
return out;
-} /* }}} gcm_output_t *gcm_output_create */
+} /* }}} sd_output_t *sd_output_create */
-void gcm_output_destroy(gcm_output_t *out) /* {{{ */
+void sd_output_destroy(sd_output_t *out) /* {{{ */
{
if (out == NULL)
return;
}
if (out->staged != NULL) {
- gcm_output_reset_staged(out);
+ sd_output_reset_staged(out);
c_avl_destroy(out->staged);
out->staged = NULL;
}
}
if (out->res != NULL) {
- gcm_resource_destroy(out->res);
+ sd_resource_destroy(out->res);
out->res = NULL;
}
sfree(out);
-} /* }}} void gcm_output_destroy */
+} /* }}} void sd_output_destroy */
-int gcm_output_add(gcm_output_t *out, data_set_t const *ds,
- value_list_t const *vl) /* {{{ */
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) /* {{{ */
{
char key[6 * DATA_MAX_NAME_LEN];
int status;
status = FORMAT_VL(key, sizeof(key), vl);
if (status != 0) {
- ERROR("gcm_output_add: FORMAT_VL failed with status %d.", status);
+ ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
return status;
}
for (size_t i = 0; i < ds->ds_num; i++) {
int status = format_time_series(out->gen, ds, vl, i, out->res);
if (status != 0) {
- ERROR("gcm_output_add: format_time_series failed with status %d.",
- status);
+ ERROR("sd_output_add: format_time_series failed with status %d.", status);
return status;
}
}
return ENOBUFS;
return 0;
-} /* }}} int gcm_output_add */
+} /* }}} int sd_output_add */
-int gcm_output_register_metric(gcm_output_t *out, data_set_t const *ds,
- value_list_t const *vl) {
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) {
/* {{{ */
for (size_t i = 0; i < ds->ds_num; i++) {
char buffer[4 * DATA_MAX_NAME_LEN];
}
return 0;
-} /* }}} int gcm_output_register_metric */
+} /* }}} int sd_output_register_metric */
-char *gcm_output_reset(gcm_output_t *out) /* {{{ */
+char *sd_output_reset(sd_output_t *out) /* {{{ */
{
unsigned char const *json_buffer = NULL;
char *ret;
- gcm_output_finalize(out);
+ sd_output_finalize(out);
yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
ret = strdup((void const *)json_buffer);
- gcm_output_reset_staged(out);
+ sd_output_reset_staged(out);
yajl_gen_free(out->gen);
out->gen = yajl_gen_alloc(/* funcs = */ NULL);
- gcm_output_initialize(out);
+ sd_output_initialize(out);
return ret;
-} /* }}} char *gcm_output_reset */
+} /* }}} char *sd_output_reset */
-gcm_resource_t *gcm_resource_create(char const *type) /* {{{ */
+sd_resource_t *sd_resource_create(char const *type) /* {{{ */
{
- gcm_resource_t *res;
+ sd_resource_t *res;
res = malloc(sizeof(*res));
if (res == NULL)
res->labels_num = 0;
return res;
-} /* }}} gcm_resource_t *gcm_resource_create */
+} /* }}} sd_resource_t *sd_resource_create */
-void gcm_resource_destroy(gcm_resource_t *res) /* {{{ */
+void sd_resource_destroy(sd_resource_t *res) /* {{{ */
{
size_t i;
sfree(res->labels);
sfree(res->type);
sfree(res);
-} /* }}} void gcm_resource_destroy */
+} /* }}} void sd_resource_destroy */
-int gcm_resource_add_label(gcm_resource_t *res, char const *key,
- char const *value) /* {{{ */
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value) /* {{{ */
{
- gcm_label_t *l;
+ sd_label_t *l;
if ((res == NULL) || (key == NULL) || (value == NULL))
return EINVAL;
res->labels_num++;
return 0;
-} /* }}} int gcm_resource_add_label */
+} /* }}} int sd_resource_add_label */
/* LabelDescriptor
*
* "displayName": string,
* }
*/
-int gcm_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index) {
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index) {
/* {{{ */
yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
if (gen == NULL) {
yajl_gen_free(gen);
return 0;
-} /* }}} int gcm_format_metric_descriptor */
+} /* }}} int sd_format_metric_descriptor */
/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_FORMAT_STACKDRIVER_H
+#define UTILS_FORMAT_STACKDRIVER_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+/* sd_output_t is a buffer to which value_list_t* can be added and from which
+ * an appropriately formatted char* can be read. */
+struct sd_output_s;
+typedef struct sd_output_s sd_output_t;
+
+/* sd_resource_t represents a MonitoredResource. */
+struct sd_resource_s;
+typedef struct sd_resource_s sd_resource_t;
+
+sd_output_t *sd_output_create(sd_resource_t *res);
+
+/* sd_output_destroy frees all memory used by out, including the
+ * sd_resource_t* passed to sd_output_create. */
+void sd_output_destroy(sd_output_t *out);
+
+/* sd_output_add adds a value_list_t* to "out".
+ *
+ * Return values:
+ * - 0 Success
+ * - ENOBUFS Success, but the buffer should be flushed soon.
+ * - EEXIST The value list is already encoded in the buffer.
+ * Flush the buffer, then call sd_output_add again.
+ * - ENOENT First time we encounter this metric. Create a metric descriptor
+ * using the Stackdriver API and then call
+ * sd_output_register_metric.
+ */
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_register_metric adds the metric descriptor which vl maps to, to
+ * the list of known metric descriptors. */
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_reset resets the output and returns the previous content of the
+ * buffer. It is the caller's responsibility to call free() with the returned
+ * pointer. */
+char *sd_output_reset(sd_output_t *out);
+
+sd_resource_t *sd_resource_create(char const *type);
+void sd_resource_destroy(sd_resource_t *res);
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value);
+
+/* sd_format_metric_descriptor creates the payload for a
+ * projects.metricDescriptors.create() request. */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index);
+
+#endif /* UTILS_FORMAT_STACKDRIVER_H */
/**
- * collectd - src/utils_format_gcm_test.c
+ * collectd - src/utils_format_stackdriver_test.c
* ISC license
*
* Copyright (C) 2017 Florian Forster
* Florian Forster <octo at collectd.org>
**/
-#include "utils_format_gcm.h"
#include "testing.h"
+#include "utils_format_stackdriver.h"
-DEF_TEST(gcm_format_metric_descriptor) {
+DEF_TEST(sd_format_metric_descriptor) {
value_list_t vl = {
.host = "example.com", .plugin = "unit-test", .type = "example",
};
},
};
EXPECT_EQ_INT(
- 0, gcm_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
char const *want_single =
"{\"type\":\"custom.googleapis.com/collectd/unit_test/"
"example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
},
};
EXPECT_EQ_INT(
- 0, gcm_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
char const *want_double =
"{\"type\":\"custom.googleapis.com/collectd/unit_test/"
"example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
}
int main(int argc, char **argv) {
- RUN_TEST(gcm_format_metric_descriptor);
+ RUN_TEST(sd_format_metric_descriptor);
END_TEST;
}
/**
- * collectd - src/write_gcm.c
+ * collectd - src/write_stackdriver.c
* ISC license
*
* Copyright (C) 2017 Florian Forster
#include "common.h"
#include "configfile.h"
#include "plugin.h"
-#include "utils_format_gcm.h"
+#include "utils_format_stackdriver.h"
#include "utils_gce.h"
#include "utils_oauth.h"
char *email;
char *project;
char *url;
- gcm_resource_t *resource;
+ sd_resource_t *resource;
/* runtime */
oauth_t *auth;
- gcm_output_t *formatter;
+ sd_output_t *formatter;
CURL *curl;
char curl_errbuf[CURL_ERROR_SIZE];
/* used by flush */
else
status = gce_access_token(cb->email, access_token, sizeof(access_token));
if (status != 0) {
- ERROR("write_gcm plugin: Failed to get access token");
+ ERROR("write_stackdriver plugin: Failed to get access token");
return NULL;
}
CURL *curl = curl_easy_init();
if (!curl) {
- ERROR("write_gcm plugin: curl_easy_init failed.");
+ ERROR("write_stackdriver plugin: curl_easy_init failed.");
curl_slist_free_all(headers);
sfree(authorization_header);
return -1;
status = curl_easy_perform(curl);
if (status != CURLE_OK) {
- ERROR("write_gcm plugin: curl_easy_perform failed with status %d: %s",
- status, curl_errbuf);
+ ERROR(
+ "write_stackdriver plugin: curl_easy_perform failed with status %d: %s",
+ status, curl_errbuf);
sfree(res.memory);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if ((http_code < 200) || (http_code >= 300)) {
- ERROR("write_gcm plugin: POST request to %s failed: HTTP error %ld",
+ ERROR("write_stackdriver plugin: POST request to %s failed: HTTP error %ld",
final_url, http_code);
- INFO("write_gcm plugin: Server replied: %s", res.memory);
+ INFO("write_stackdriver plugin: Server replied: %s", res.memory);
sfree(res.memory);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
status = curl_easy_perform(cb->curl);
if (status != CURLE_OK) {
- ERROR("write_gcm plugin: curl_easy_perform failed with status %d: %s",
- status, cb->curl_errbuf);
+ ERROR(
+ "write_stackdriver plugin: curl_easy_perform failed with status %d: %s",
+ status, cb->curl_errbuf);
sfree(res.memory);
curl_slist_free_all(headers);
sfree(authorization_header);
long http_code = 0;
curl_easy_getinfo(cb->curl, CURLINFO_RESPONSE_CODE, &http_code);
if ((http_code < 200) || (http_code >= 300)) {
- ERROR("write_gcm plugin: POST request to %s failed: HTTP error %ld",
+ ERROR("write_stackdriver plugin: POST request to %s failed: HTTP error %ld",
final_url, http_code);
- INFO("write_gcm plugin: Server replied: %s", res.memory);
+ INFO("write_stackdriver plugin: Server replied: %s", res.memory);
sfree(res.memory);
curl_slist_free_all(headers);
sfree(authorization_header);
if (cb->curl != NULL)
return 0;
- cb->formatter = gcm_output_create(cb->resource);
+ cb->formatter = sd_output_create(cb->resource);
if (cb->formatter == NULL) {
- ERROR("write_gcm plugin: gcm_output_create failed.");
+ ERROR("write_stackdriver plugin: sd_output_create failed.");
return -1;
}
cb->curl = curl_easy_init();
if (cb->curl == NULL) {
- ERROR("write_gcm plugin: curl_easy_init failed.");
+ ERROR("write_stackdriver plugin: curl_easy_init failed.");
return -1;
}
return 0;
}
- char *payload = gcm_output_reset(cb->formatter);
+ char *payload = sd_output_reset(cb->formatter);
int status = wg_call_timeseries_write(cb, payload);
if (status != 0) {
- ERROR("write_gcm plugin: Sending buffer failed with status %d.", status);
+ ERROR("write_stackdriver plugin: Sending buffer failed with status %d.",
+ status);
}
wg_reset_buffer(cb);
if (cb->curl == NULL) {
status = wg_callback_init(cb);
if (status != 0) {
- ERROR("write_gcm plugin: wg_callback_init failed.");
+ ERROR("write_stackdriver plugin: wg_callback_init failed.");
pthread_mutex_unlock(&cb->lock);
return -1;
}
if (cb == NULL)
return;
- gcm_output_destroy(cb->formatter);
+ sd_output_destroy(cb->formatter);
cb->formatter = NULL;
sfree(cb->email);
for (size_t i = 0; i < ds->ds_num; i++) {
char buffer[4096];
- int status =
- gcm_format_metric_descriptor(buffer, sizeof(buffer), ds, vl, i);
+ int status = sd_format_metric_descriptor(buffer, sizeof(buffer), ds, vl, i);
if (status != 0) {
- ERROR("write_gcm plugin: gcm_format_metric_descriptor failed with status "
+ ERROR("write_stackdriver plugin: sd_format_metric_descriptor failed "
+ "with status "
"%d",
status);
return status;
status = wg_call_metricdescriptor_create(cb, buffer);
if (status != 0) {
- ERROR("write_gcm plugin: wg_call_metricdescriptor_create failed with "
+ ERROR("write_stackdriver plugin: wg_call_metricdescriptor_create failed "
+ "with "
"status %d",
status);
return status;
}
}
- return gcm_output_register_metric(cb->formatter, ds, vl);
+ return sd_output_register_metric(cb->formatter, ds, vl);
} /* }}} int wg_metric_descriptors_create */
static int wg_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
if (cb->curl == NULL) {
int status = wg_callback_init(cb);
if (status != 0) {
- ERROR("write_gcm plugin: wg_callback_init failed.");
+ ERROR("write_stackdriver plugin: wg_callback_init failed.");
pthread_mutex_unlock(&cb->lock);
return status;
}
int status;
while (42) {
- status = gcm_output_add(cb->formatter, ds, vl);
+ status = sd_output_add(cb->formatter, ds, vl);
if (status == 0) { /* success */
break;
} else if (status == ENOBUFS) { /* success, flush */
{
char *scope = gce_scope(email);
if (scope == NULL) {
- WARNING("write_gcm plugin: Unable to determine scope of this instance.");
+ WARNING("write_stackdriver plugin: Unable to determine scope of this "
+ "instance.");
return;
}
while ((scope_len > 0) && (iscntrl((int)scope[scope_len - 1])))
scope[--scope_len] = 0;
- WARNING("write_gcm plugin: The determined scope of this instance "
+ WARNING("write_stackdriver plugin: The determined scope of this instance "
"(\"%s\") does not contain the monitoring scope (\"%s\"). You need "
"to add this scope to the list of scopes passed to gcutil with "
"--service_account_scopes when creating the instance. "
static int wg_config_resource(oconfig_item_t *ci, wg_callback_t *cb) /* {{{ */
{
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
- ERROR("write_gcm plugin: The \"%s\" option requires exactly one string "
+ ERROR("write_stackdriver plugin: The \"%s\" option requires exactly one "
+ "string "
"argument.",
ci->key);
return EINVAL;
char *resource_type = ci->values[0].value.string;
if (cb->resource != NULL) {
- gcm_resource_destroy(cb->resource);
+ sd_resource_destroy(cb->resource);
}
- cb->resource = gcm_resource_create(resource_type);
+ cb->resource = sd_resource_create(resource_type);
if (cb->resource == NULL) {
- ERROR("write_gcm plugin: gcm_resource_create(\"%s\") failed.",
+ ERROR("write_stackdriver plugin: sd_resource_create(\"%s\") failed.",
resource_type);
return ENOMEM;
}
if ((child->values_num != 1) ||
(child->values[0].type != OCONFIG_TYPE_STRING)) {
- ERROR("write_gcm plugin: Resource labels must have exactly one string "
+ ERROR("write_stackdriver plugin: Resource labels must have exactly one "
+ "string "
"value. Ignoring label \"%s\".",
child->key);
continue;
}
- gcm_resource_add_label(cb->resource, child->key,
- child->values[0].value.string);
+ sd_resource_add_label(cb->resource, child->key,
+ child->values[0].value.string);
}
return 0;
wg_callback_t *cb = calloc(1, sizeof(*cb));
if (cb == NULL) {
- ERROR("write_gcm plugin: calloc failed.");
+ ERROR("write_stackdriver plugin: calloc failed.");
return ENOMEM;
}
cb->url = strdup(GCM_API_URL);
else if (strcasecmp("Resource", child->key) == 0)
wg_config_resource(child, cb);
else {
- ERROR("write_gcm plugin: Invalid configuration option: %s.", child->key);
+ ERROR("write_stackdriver plugin: Invalid configuration option: %s.",
+ child->key);
wg_callback_free(cb);
return EINVAL;
}
oauth_google_t cfg =
oauth_create_google_file(credential_file, MONITORING_SCOPE);
if (cfg.oauth == NULL) {
- ERROR("write_gcm plugin: oauth_create_google_file failed");
+ ERROR("write_stackdriver plugin: oauth_create_google_file failed");
wg_callback_free(cb);
return EINVAL;
}
if (cb->project == NULL) {
cb->project = cfg.project_id;
- INFO("write_gcm plugin: Automatically detected project ID: \"%s\"",
- cb->project);
+ INFO(
+ "write_stackdriver plugin: Automatically detected project ID: \"%s\"",
+ cb->project);
} else {
sfree(cfg.project_id);
}
if (cb->project == NULL) {
cb->project = cfg.project_id;
- INFO("write_gcm plugin: Automatically detected project ID: \"%s\"",
- cb->project);
+ INFO(
+ "write_stackdriver plugin: Automatically detected project ID: \"%s\"",
+ cb->project);
} else {
sfree(cfg.project_id);
}
}
if ((cb->auth != NULL) && (cb->email != NULL)) {
- NOTICE("write_gcm plugin: A service account email was configured but is "
+ NOTICE("write_stackdriver plugin: A service account email was configured "
+ "but is "
"not used for authentication because %s used instead.",
(credential_file != NULL) ? "a credential file was"
: "application default credentials were");
if ((cb->auth == NULL) && gce_check()) {
wg_check_scope(cb->email);
} else if (cb->auth == NULL) {
- ERROR("write_gcm plugin: Unable to determine credentials. Please either "
+ ERROR("write_stackdriver plugin: Unable to determine credentials. Please "
+ "either "
"specify the \"Credentials\" option or set up Application Default "
"Credentials.");
wg_callback_free(cb);
cb->project = gce_project_id();
}
if (cb->project == NULL) {
- ERROR("write_gcm plugin: Unable to determine the project number. "
+ ERROR("write_stackdriver plugin: Unable to determine the project number. "
"Please specify the \"Project\" option manually.");
wg_callback_free(cb);
return EINVAL;
if ((cb->resource == NULL) && gce_check()) {
/* TODO(octo): add error handling */
- cb->resource = gcm_resource_create("gce_instance");
- gcm_resource_add_label(cb->resource, "project_id", gce_project_id());
- gcm_resource_add_label(cb->resource, "instance_id", gce_instance_id());
- gcm_resource_add_label(cb->resource, "zone", gce_zone());
+ cb->resource = sd_resource_create("gce_instance");
+ sd_resource_add_label(cb->resource, "project_id", gce_project_id());
+ sd_resource_add_label(cb->resource, "instance_id", gce_instance_id());
+ sd_resource_add_label(cb->resource, "zone", gce_zone());
}
if (cb->resource == NULL) {
/* TODO(octo): add error handling */
- cb->resource = gcm_resource_create("global");
- gcm_resource_add_label(cb->resource, "project_id", cb->project);
+ cb->resource = sd_resource_create("global");
+ sd_resource_add_label(cb->resource, "project_id", cb->project);
}
- DEBUG("write_gcm plugin: Registering write callback with URL %s", cb->url);
+ DEBUG("write_stackdriver plugin: Registering write callback with URL %s",
+ cb->url);
assert((cb->auth != NULL) || gce_check());
user_data_t user_data = {
.data = cb,
};
- plugin_register_flush("write_gcm", wg_flush, &user_data);
+ plugin_register_flush("write_stackdriver", wg_flush, &user_data);
user_data.free_func = wg_callback_free;
- plugin_register_write("write_gcm", wg_write, &user_data);
+ plugin_register_write("write_stackdriver", wg_write, &user_data);
return 0;
} /* }}} int wg_config */
void module_register(void) /* {{{ */
{
- plugin_register_complex_config("write_gcm", wg_config);
- plugin_register_init("write_gcm", wg_init);
+ plugin_register_complex_config("write_stackdriver", wg_config);
+ plugin_register_init("write_stackdriver", wg_init);
} /* }}} void module_register */
/* vim: set sw=2 sts=2 et fdm=marker : */