2 * collectd - src/write_riemann.c
3 * Copyright (C) 2012,2013 Pierre-Yves Ritschard
4 * Copyright (C) 2013 Florian octo Forster
5 * Copyright (C) 2015,2016 Gergely Nagy
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
26 * Pierre-Yves Ritschard <pyr at spootnik.org>
27 * Florian octo Forster <octo at collectd.org>
28 * Gergely Nagy <algernon at madhouse-project.org>
35 #include "utils_cache.h"
36 #include "utils_complain.h"
37 #include "write_riemann_threshold.h"
39 #include <riemann/riemann-client.h>
41 #define RIEMANN_HOST "localhost"
42 #define RIEMANN_PORT 5555
43 #define RIEMANN_TTL_FACTOR 2.0
44 #define RIEMANN_BATCH_MAX 8192
47 c_complain_t init_complaint;
49 char *event_service_prefix;
53 _Bool check_thresholds;
55 _Bool always_append_ds;
58 riemann_client_type_t client_type;
59 riemann_client_t *client;
65 riemann_message_t *batch_msg;
69 struct timeval timeout;
72 static char **riemann_tags;
73 static size_t riemann_tags_num;
74 static char **riemann_attrs;
75 static size_t riemann_attrs_num;
77 /* host->lock must be held when calling this function. */
78 static int wrr_connect(struct riemann_host *host) /* {{{ */
86 node = (host->node != NULL) ? host->node : RIEMANN_HOST;
87 port = (host->port) ? host->port : RIEMANN_PORT;
91 host->client = riemann_client_create(
92 host->client_type, node, port, RIEMANN_CLIENT_OPTION_TLS_CA_FILE,
93 host->tls_ca_file, RIEMANN_CLIENT_OPTION_TLS_CERT_FILE,
94 host->tls_cert_file, RIEMANN_CLIENT_OPTION_TLS_KEY_FILE,
95 host->tls_key_file, RIEMANN_CLIENT_OPTION_NONE);
96 if (host->client == NULL) {
97 c_complain(LOG_ERR, &host->init_complaint,
98 "write_riemann plugin: Unable to connect to Riemann at %s:%d",
102 #if RCC_VERSION_NUMBER >= 0x010800
103 if (host->timeout.tv_sec != 0) {
104 if (riemann_client_set_timeout(host->client, &host->timeout) != 0) {
105 riemann_client_free(host->client);
107 c_complain(LOG_ERR, &host->init_complaint,
108 "write_riemann plugin: Unable to connect to Riemann at %s:%d",
115 set_sock_opts(riemann_client_get_fd(host->client));
117 c_release(LOG_INFO, &host->init_complaint,
118 "write_riemann plugin: Successfully connected to %s:%d", node,
122 } /* }}} int wrr_connect */
124 /* host->lock must be held when calling this function. */
125 static int wrr_disconnect(struct riemann_host *host) /* {{{ */
130 riemann_client_free(host->client);
134 } /* }}} int wrr_disconnect */
137 * Function to send messages to riemann.
139 * Acquires the host lock, disconnects on errors.
141 static int wrr_send_nolock(struct riemann_host *host,
142 riemann_message_t *msg) /* {{{ */
146 status = wrr_connect(host);
151 status = riemann_client_send_message(host->client, msg);
153 wrr_disconnect(host);
158 * For TCP we need to receive message acknowledgemenent.
160 if (host->client_type != RIEMANN_CLIENT_UDP) {
161 riemann_message_t *response;
163 response = riemann_client_recv_message(host->client);
165 if (response == NULL) {
166 wrr_disconnect(host);
169 riemann_message_free(response);
173 } /* }}} int wrr_send */
175 static int wrr_send(struct riemann_host *host, riemann_message_t *msg) {
178 pthread_mutex_lock(&host->lock);
179 status = wrr_send_nolock(host, msg);
180 pthread_mutex_unlock(&host->lock);
184 static riemann_message_t *
185 wrr_notification_to_message(struct riemann_host *host, /* {{{ */
186 notification_t const *n) {
187 riemann_message_t *msg;
188 riemann_event_t *event;
189 char service_buffer[6 * DATA_MAX_NAME_LEN];
190 char const *severity;
192 switch (n->severity) {
197 severity = "warning";
200 severity = "critical";
203 severity = "unknown";
206 format_name(service_buffer, sizeof(service_buffer),
207 /* host = */ "", n->plugin, n->plugin_instance, n->type,
210 event = riemann_event_create(
211 RIEMANN_EVENT_FIELD_HOST, n->host, RIEMANN_EVENT_FIELD_TIME,
212 (int64_t)CDTIME_T_TO_TIME_T(n->time), RIEMANN_EVENT_FIELD_TAGS,
213 "notification", NULL, RIEMANN_EVENT_FIELD_STATE, severity,
214 RIEMANN_EVENT_FIELD_SERVICE, &service_buffer[1],
215 RIEMANN_EVENT_FIELD_NONE);
217 #if RCC_VERSION_NUMBER >= 0x010A00
218 riemann_event_set(event, RIEMANN_EVENT_FIELD_TIME_MICROS,
219 (int64_t)CDTIME_T_TO_US(n->time));
223 riemann_event_string_attribute_add(event, "host", n->host);
224 if (n->plugin[0] != 0)
225 riemann_event_string_attribute_add(event, "plugin", n->plugin);
226 if (n->plugin_instance[0] != 0)
227 riemann_event_string_attribute_add(event, "plugin_instance",
231 riemann_event_string_attribute_add(event, "type", n->type);
232 if (n->type_instance[0] != 0)
233 riemann_event_string_attribute_add(event, "type_instance",
236 for (size_t i = 0; i < riemann_attrs_num; i += 2)
237 riemann_event_string_attribute_add(event, riemann_attrs[i],
238 riemann_attrs[i + 1]);
240 for (size_t i = 0; i < riemann_tags_num; i++)
241 riemann_event_tag_add(event, riemann_tags[i]);
243 if (n->message[0] != 0)
244 riemann_event_string_attribute_add(event, "description", n->message);
246 /* Pull in values from threshold and add extra attributes */
247 for (notification_meta_t *meta = n->meta; meta != NULL; meta = meta->next) {
248 if (strcasecmp("CurrentValue", meta->name) == 0 &&
249 meta->type == NM_TYPE_DOUBLE) {
250 riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_D,
251 (double)meta->nm_value.nm_double,
252 RIEMANN_EVENT_FIELD_NONE);
256 if (meta->type == NM_TYPE_STRING) {
257 riemann_event_string_attribute_add(event, meta->name,
258 meta->nm_value.nm_string);
263 msg = riemann_message_create_with_events(event, NULL);
265 ERROR("write_riemann plugin: riemann_message_create_with_events() failed.");
266 riemann_event_free(event);
270 DEBUG("write_riemann plugin: Successfully created message for notification: "
271 "host = \"%s\", service = \"%s\", state = \"%s\"",
272 event->host, event->service, event->state);
274 } /* }}} riemann_message_t *wrr_notification_to_message */
276 static riemann_event_t *
277 wrr_value_to_event(struct riemann_host const *host, /* {{{ */
278 data_set_t const *ds, value_list_t const *vl, size_t index,
279 gauge_t const *rates, int status) {
280 riemann_event_t *event;
281 char name_buffer[5 * DATA_MAX_NAME_LEN];
282 char service_buffer[6 * DATA_MAX_NAME_LEN];
285 event = riemann_event_new();
287 ERROR("write_riemann plugin: riemann_event_new() failed.");
291 format_name(name_buffer, sizeof(name_buffer),
292 /* host = */ "", vl->plugin, vl->plugin_instance, vl->type,
294 if (host->always_append_ds || (ds->ds_num > 1)) {
295 if (host->event_service_prefix == NULL)
296 snprintf(service_buffer, sizeof(service_buffer), "%s/%s", &name_buffer[1],
299 snprintf(service_buffer, sizeof(service_buffer), "%s%s/%s",
300 host->event_service_prefix, &name_buffer[1], ds->ds[index].name);
302 if (host->event_service_prefix == NULL)
303 sstrncpy(service_buffer, &name_buffer[1], sizeof(service_buffer));
305 snprintf(service_buffer, sizeof(service_buffer), "%s%s",
306 host->event_service_prefix, &name_buffer[1]);
310 event, RIEMANN_EVENT_FIELD_HOST, vl->host, RIEMANN_EVENT_FIELD_TIME,
311 (int64_t)CDTIME_T_TO_TIME_T(vl->time), RIEMANN_EVENT_FIELD_TTL,
312 (float)CDTIME_T_TO_DOUBLE(vl->interval) * host->ttl_factor,
313 RIEMANN_EVENT_FIELD_STRING_ATTRIBUTES, "plugin", vl->plugin, "type",
314 vl->type, "ds_name", ds->ds[index].name, NULL,
315 RIEMANN_EVENT_FIELD_SERVICE, service_buffer, RIEMANN_EVENT_FIELD_NONE);
317 #if RCC_VERSION_NUMBER >= 0x010A00
318 riemann_event_set(event, RIEMANN_EVENT_FIELD_TIME_MICROS,
319 (int64_t)CDTIME_T_TO_US(vl->time));
322 if (host->check_thresholds) {
323 const char *state = NULL;
340 riemann_event_set(event, RIEMANN_EVENT_FIELD_STATE, state,
341 RIEMANN_EVENT_FIELD_NONE);
344 if (vl->plugin_instance[0] != 0)
345 riemann_event_string_attribute_add(event, "plugin_instance",
346 vl->plugin_instance);
347 if (vl->type_instance[0] != 0)
348 riemann_event_string_attribute_add(event, "type_instance",
351 if ((ds->ds[index].type != DS_TYPE_GAUGE) && (rates != NULL)) {
352 char ds_type[DATA_MAX_NAME_LEN];
354 snprintf(ds_type, sizeof(ds_type), "%s:rate",
355 DS_TYPE_TO_STRING(ds->ds[index].type));
356 riemann_event_string_attribute_add(event, "ds_type", ds_type);
358 riemann_event_string_attribute_add(event, "ds_type",
359 DS_TYPE_TO_STRING(ds->ds[index].type));
363 char ds_index[DATA_MAX_NAME_LEN];
365 snprintf(ds_index, sizeof(ds_index), "%zu", index);
366 riemann_event_string_attribute_add(event, "ds_index", ds_index);
369 for (i = 0; i < riemann_attrs_num; i += 2)
370 riemann_event_string_attribute_add(event, riemann_attrs[i],
371 riemann_attrs[i + 1]);
373 for (i = 0; i < riemann_tags_num; i++)
374 riemann_event_tag_add(event, riemann_tags[i]);
376 if (ds->ds[index].type == DS_TYPE_GAUGE) {
377 riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_D,
378 (double)vl->values[index].gauge,
379 RIEMANN_EVENT_FIELD_NONE);
380 } else if (rates != NULL) {
381 riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_D, (double)rates[index],
382 RIEMANN_EVENT_FIELD_NONE);
386 if (ds->ds[index].type == DS_TYPE_DERIVE)
387 metric = (int64_t)vl->values[index].derive;
388 else if (ds->ds[index].type == DS_TYPE_ABSOLUTE)
389 metric = (int64_t)vl->values[index].absolute;
391 metric = (int64_t)vl->values[index].counter;
393 riemann_event_set(event, RIEMANN_EVENT_FIELD_METRIC_S64, (int64_t)metric,
394 RIEMANN_EVENT_FIELD_NONE);
397 DEBUG("write_riemann plugin: Successfully created message for metric: "
398 "host = \"%s\", service = \"%s\"",
399 event->host, event->service);
401 } /* }}} riemann_event_t *wrr_value_to_event */
403 static riemann_message_t *
404 wrr_value_list_to_message(struct riemann_host const *host, /* {{{ */
405 data_set_t const *ds, value_list_t const *vl,
407 riemann_message_t *msg;
409 gauge_t *rates = NULL;
411 /* Initialize the Msg structure. */
412 msg = riemann_message_new();
414 ERROR("write_riemann plugin: riemann_message_new failed.");
418 if (host->store_rates) {
419 rates = uc_get_rate(ds, vl);
421 ERROR("write_riemann plugin: uc_get_rate failed.");
422 riemann_message_free(msg);
427 for (i = 0; i < vl->values_len; i++) {
428 riemann_event_t *event;
430 event = wrr_value_to_event(host, ds, vl, (int)i, rates, statuses[i]);
432 riemann_message_free(msg);
436 riemann_message_append_events(msg, event, NULL);
441 } /* }}} riemann_message_t *wrr_value_list_to_message */
444 * Always call while holding host->lock !
446 static int wrr_batch_flush_nolock(cdtime_t timeout, struct riemann_host *host) {
452 if ((host->batch_init + timeout) > now) {
456 wrr_send_nolock(host, host->batch_msg);
457 riemann_message_free(host->batch_msg);
459 host->batch_init = now;
460 host->batch_msg = NULL;
464 static int wrr_batch_flush(cdtime_t timeout,
465 const char *identifier __attribute__((unused)),
466 user_data_t *user_data) {
467 struct riemann_host *host;
470 if (user_data == NULL)
473 host = user_data->data;
474 pthread_mutex_lock(&host->lock);
475 status = wrr_batch_flush_nolock(timeout, host);
478 LOG_ERR, &host->init_complaint,
479 "write_riemann plugin: riemann_client_send failed with status %i",
482 c_release(LOG_DEBUG, &host->init_complaint,
483 "write_riemann plugin: batch sent.");
485 pthread_mutex_unlock(&host->lock);
489 static int wrr_batch_add_value_list(struct riemann_host *host, /* {{{ */
490 data_set_t const *ds,
491 value_list_t const *vl, int *statuses) {
492 riemann_message_t *msg;
497 msg = wrr_value_list_to_message(host, ds, vl, statuses);
501 pthread_mutex_lock(&host->lock);
503 if (host->batch_msg == NULL) {
504 host->batch_msg = msg;
508 status = riemann_message_append_events_n(host->batch_msg, msg->n_events,
513 riemann_message_free(msg);
516 pthread_mutex_unlock(&host->lock);
517 ERROR("write_riemann plugin: out of memory");
522 len = riemann_message_get_packed_size(host->batch_msg);
524 if ((host->batch_max < 0) || (((size_t)host->batch_max) <= len)) {
525 ret = wrr_batch_flush_nolock(0, host);
527 if (host->batch_timeout > 0) {
528 timeout = TIME_T_TO_CDTIME_T((time_t)host->batch_timeout);
529 ret = wrr_batch_flush_nolock(timeout, host);
533 pthread_mutex_unlock(&host->lock);
535 } /* }}} riemann_message_t *wrr_batch_add_value_list */
537 static int wrr_notification(const notification_t *n, user_data_t *ud) /* {{{ */
540 struct riemann_host *host = ud->data;
541 riemann_message_t *msg;
543 if (!host->notifications)
547 * Never batch for notifications, send them ASAP
549 msg = wrr_notification_to_message(host, n);
553 status = wrr_send(host, msg);
556 LOG_ERR, &host->init_complaint,
557 "write_riemann plugin: riemann_client_send failed with status %i",
560 c_release(LOG_DEBUG, &host->init_complaint,
561 "write_riemann plugin: riemann_client_send succeeded");
563 riemann_message_free(msg);
565 } /* }}} int wrr_notification */
567 static int wrr_write(const data_set_t *ds, /* {{{ */
568 const value_list_t *vl, user_data_t *ud) {
570 int statuses[vl->values_len];
571 struct riemann_host *host = ud->data;
572 riemann_message_t *msg;
574 if (host->check_thresholds) {
575 status = write_riemann_threshold_check(ds, vl, statuses);
579 memset(statuses, 0, sizeof(statuses));
582 if (host->client_type != RIEMANN_CLIENT_UDP && host->batch_mode) {
583 wrr_batch_add_value_list(host, ds, vl, statuses);
585 msg = wrr_value_list_to_message(host, ds, vl, statuses);
589 status = wrr_send(host, msg);
591 riemann_message_free(msg);
594 } /* }}} int wrr_write */
596 static void wrr_free(void *p) /* {{{ */
598 struct riemann_host *host = p;
603 pthread_mutex_lock(&host->lock);
605 host->reference_count--;
606 if (host->reference_count > 0) {
607 pthread_mutex_unlock(&host->lock);
611 wrr_disconnect(host);
613 pthread_mutex_destroy(&host->lock);
615 } /* }}} void wrr_free */
617 static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
619 struct riemann_host *host = NULL;
622 oconfig_item_t *child;
623 char callback_name[DATA_MAX_NAME_LEN];
625 if ((host = calloc(1, sizeof(*host))) == NULL) {
626 ERROR("write_riemann plugin: calloc failed.");
629 pthread_mutex_init(&host->lock, NULL);
630 C_COMPLAIN_INIT(&host->init_complaint);
631 host->reference_count = 1;
634 host->notifications = 1;
635 host->check_thresholds = 0;
636 host->store_rates = 1;
637 host->always_append_ds = 0;
638 host->batch_mode = 1;
639 host->batch_max = RIEMANN_BATCH_MAX; /* typical MSS */
640 host->batch_init = cdtime();
641 host->batch_timeout = 0;
642 host->ttl_factor = RIEMANN_TTL_FACTOR;
644 host->client_type = RIEMANN_CLIENT_TCP;
645 host->timeout.tv_sec = 0;
646 host->timeout.tv_usec = 0;
648 status = cf_util_get_string(ci, &host->name);
650 WARNING("write_riemann plugin: Required host name is missing.");
655 for (i = 0; i < ci->children_num; i++) {
657 * The code here could be simplified but makes room
658 * for easy adding of new options later on.
660 child = &ci->children[i];
663 if (strcasecmp("Host", child->key) == 0) {
664 status = cf_util_get_string(child, &host->node);
667 } else if (strcasecmp("Notifications", child->key) == 0) {
668 status = cf_util_get_boolean(child, &host->notifications);
671 } else if (strcasecmp("EventServicePrefix", child->key) == 0) {
672 status = cf_util_get_string(child, &host->event_service_prefix);
675 } else if (strcasecmp("CheckThresholds", child->key) == 0) {
676 status = cf_util_get_boolean(child, &host->check_thresholds);
679 } else if (strcasecmp("Batch", child->key) == 0) {
680 status = cf_util_get_boolean(child, &host->batch_mode);
683 } else if (strcasecmp("BatchMaxSize", child->key) == 0) {
684 status = cf_util_get_int(child, &host->batch_max);
687 } else if (strcasecmp("BatchFlushTimeout", child->key) == 0) {
688 status = cf_util_get_int(child, &host->batch_timeout);
691 } else if (strcasecmp("Timeout", child->key) == 0) {
692 #if RCC_VERSION_NUMBER >= 0x010800
693 status = cf_util_get_int(child, (int *)&host->timeout.tv_sec);
697 WARNING("write_riemann plugin: The Timeout option is not supported. "
698 "Please upgrade the Riemann client to at least 1.8.0.");
700 } else if (strcasecmp("Port", child->key) == 0) {
701 host->port = cf_util_get_port_number(child);
702 if (host->port == -1) {
703 ERROR("write_riemann plugin: Invalid argument "
704 "configured for the \"Port\" "
708 } else if (strcasecmp("Protocol", child->key) == 0) {
710 status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
712 ERROR("write_riemann plugin: cf_util_get_"
713 "string_buffer failed with "
719 if (strcasecmp("UDP", tmp) == 0)
720 host->client_type = RIEMANN_CLIENT_UDP;
721 else if (strcasecmp("TCP", tmp) == 0)
722 host->client_type = RIEMANN_CLIENT_TCP;
723 else if (strcasecmp("TLS", tmp) == 0)
724 host->client_type = RIEMANN_CLIENT_TLS;
726 WARNING("write_riemann plugin: The value "
727 "\"%s\" is not valid for the "
728 "\"Protocol\" option. Use "
729 "either \"UDP\", \"TCP\" or \"TLS\".",
731 } else if (strcasecmp("TLSCAFile", child->key) == 0) {
732 status = cf_util_get_string(child, &host->tls_ca_file);
734 ERROR("write_riemann plugin: cf_util_get_"
735 "string_buffer failed with "
740 } else if (strcasecmp("TLSCertFile", child->key) == 0) {
741 status = cf_util_get_string(child, &host->tls_cert_file);
743 ERROR("write_riemann plugin: cf_util_get_"
744 "string_buffer failed with "
749 } else if (strcasecmp("TLSKeyFile", child->key) == 0) {
750 status = cf_util_get_string(child, &host->tls_key_file);
752 ERROR("write_riemann plugin: cf_util_get_"
753 "string_buffer failed with "
758 } else if (strcasecmp("StoreRates", child->key) == 0) {
759 status = cf_util_get_boolean(child, &host->store_rates);
762 } else if (strcasecmp("AlwaysAppendDS", child->key) == 0) {
763 status = cf_util_get_boolean(child, &host->always_append_ds);
766 } else if (strcasecmp("TTLFactor", child->key) == 0) {
768 status = cf_util_get_double(child, &tmp);
772 host->ttl_factor = tmp;
773 } else if (tmp >= 1.0) {
774 NOTICE("write_riemann plugin: The configured "
775 "TTLFactor is very small "
776 "(%.1f). A value of 2.0 or "
777 "greater is recommended.",
779 host->ttl_factor = tmp;
780 } else if (tmp > 0.0) {
781 WARNING("write_riemann plugin: The configured "
782 "TTLFactor is too small to be "
783 "useful (%.1f). I'll use it "
784 "since the user knows best, "
785 "but under protest.",
787 host->ttl_factor = tmp;
788 } else { /* zero, negative and NAN */
789 ERROR("write_riemann plugin: The configured "
790 "TTLFactor is invalid (%.1f).",
794 WARNING("write_riemann plugin: ignoring unknown config "
804 snprintf(callback_name, sizeof(callback_name), "write_riemann/%s",
807 user_data_t ud = {.data = host, .free_func = wrr_free};
809 pthread_mutex_lock(&host->lock);
811 status = plugin_register_write(callback_name, wrr_write, &ud);
813 if (host->client_type != RIEMANN_CLIENT_UDP && host->batch_mode) {
815 plugin_register_flush(callback_name, wrr_batch_flush, &ud);
818 WARNING("write_riemann plugin: plugin_register_write (\"%s\") "
819 "failed with status %i.",
820 callback_name, status);
822 host->reference_count++;
824 status = plugin_register_notification(callback_name, wrr_notification, &ud);
826 WARNING("write_riemann plugin: plugin_register_notification (\"%s\") "
827 "failed with status %i.",
828 callback_name, status);
830 host->reference_count++;
832 if (host->reference_count <= 1) {
833 /* Both callbacks failed => free memory.
834 * We need to unlock here, because riemann_free() will lock.
835 * This is not a race condition, because we're the only one
836 * holding a reference. */
837 pthread_mutex_unlock(&host->lock);
842 host->reference_count--;
843 pthread_mutex_unlock(&host->lock);
846 } /* }}} int wrr_config_node */
848 static int wrr_config(oconfig_item_t *ci) /* {{{ */
851 oconfig_item_t *child;
854 for (i = 0; i < ci->children_num; i++) {
855 child = &ci->children[i];
857 if (strcasecmp("Node", child->key) == 0) {
858 wrr_config_node(child);
859 } else if (strcasecmp(child->key, "attribute") == 0) {
863 if (child->values_num != 2) {
864 WARNING("riemann attributes need both a key and a value.");
867 if (child->values[0].type != OCONFIG_TYPE_STRING ||
868 child->values[1].type != OCONFIG_TYPE_STRING) {
869 WARNING("riemann attribute needs string arguments.");
872 if ((key = strdup(child->values[0].value.string)) == NULL) {
873 WARNING("cannot allocate memory for attribute key.");
876 if ((val = strdup(child->values[1].value.string)) == NULL) {
877 WARNING("cannot allocate memory for attribute value.");
881 strarray_add(&riemann_attrs, &riemann_attrs_num, key);
882 strarray_add(&riemann_attrs, &riemann_attrs_num, val);
883 DEBUG("write_riemann: got attr: %s => %s", key, val);
886 } else if (strcasecmp(child->key, "tag") == 0) {
888 status = cf_util_get_string(child, &tmp);
892 strarray_add(&riemann_tags, &riemann_tags_num, tmp);
893 DEBUG("write_riemann plugin: Got tag: %s", tmp);
896 WARNING("write_riemann plugin: Ignoring unknown "
897 "configuration option \"%s\" at top level.",
902 } /* }}} int wrr_config */
904 void module_register(void) {
905 plugin_register_complex_config("write_riemann", wrr_config);