*/
#include "collectd.h"
+
#include "plugin.h"
#include "common.h"
-#include "configfile.h"
-#include "utils_cache.h"
#include "utils_cmd_putval.h"
#include "utils_format_graphite.h"
#include "utils_format_json.h"
-#include "utils_crc32.h"
#include <stdint.h>
#include <librdkafka/rdkafka.h>
-#include <pthread.h>
-#include <zlib.h>
#include <errno.h>
struct kafka_topic_context {
static int32_t kafka_partition(const rd_kafka_topic_t *, const void *, size_t,
int32_t, void *, void *);
-#if defined HAVE_LIBRDKAFKA_LOGGER || defined HAVE_LIBRDKAFKA_LOG_CB
+/* Version 0.9.0 of librdkafka deprecates rd_kafka_set_logger() in favor of
+ * rd_kafka_conf_set_log_cb(). This is to make sure we're not using the
+ * deprecated function. */
+#ifdef HAVE_LIBRDKAFKA_LOG_CB
+# undef HAVE_LIBRDKAFKA_LOGGER
+#endif
+
+#if defined(HAVE_LIBRDKAFKA_LOGGER) || defined(HAVE_LIBRDKAFKA_LOG_CB)
static void kafka_log(const rd_kafka_t *, int, const char *, const char *);
static void kafka_log(const rd_kafka_t *rkt, int level,
}
#endif
+static uint32_t kafka_hash(const char *keydata, size_t keylen)
+{
+ uint32_t hash = 5381;
+ for (; keylen > 0; keylen--)
+ hash = ((hash << 5) + hash) + keydata[keylen - 1];
+ return hash;
+}
+
+/* 31 bit -> 4 byte -> 8 byte hex string + null byte */
+#define KAFKA_RANDOM_KEY_SIZE 9
+#define KAFKA_RANDOM_KEY_BUFFER (char[KAFKA_RANDOM_KEY_SIZE]) {""}
+static char *kafka_random_key(char buffer[static KAFKA_RANDOM_KEY_SIZE])
+{
+ ssnprintf(buffer, KAFKA_RANDOM_KEY_SIZE, "%08lX", (unsigned long) mrand48());
+ return buffer;
+}
+
static int32_t kafka_partition(const rd_kafka_topic_t *rkt,
const void *keydata, size_t keylen,
int32_t partition_cnt, void *p, void *m)
{
- uint32_t key = *((uint32_t *)keydata );
+ uint32_t key = kafka_hash(keydata, keylen);
uint32_t target = key % partition_cnt;
int32_t i = partition_cnt;
switch (ctx->format) {
case KAFKA_FORMAT_COMMAND:
- status = create_putval(buffer, sizeof(buffer), ds, vl);
+ status = cmd_create_putval(buffer, sizeof(buffer), ds, vl);
if (status != 0) {
- ERROR("write_kafka plugin: create_putval failed with status %i.",
+ ERROR("write_kafka plugin: cmd_create_putval failed with status %i.",
status);
return status;
}
return -1;
}
- key = ctx->key;
- if (key != NULL)
- keylen = strlen (key);
- else
- keylen = 0;
+ key = (ctx->key != NULL)
+ ? ctx->key
+ : kafka_random_key(KAFKA_RANDOM_KEY_BUFFER);
+ keylen = strlen (key);
rd_kafka_produce(ctx->topic, RD_KAFKA_PARTITION_UA,
RD_KAFKA_MSG_F_COPY, buffer, blen,
static void kafka_config_topic(rd_kafka_conf_t *conf, oconfig_item_t *ci) /* {{{ */
{
int status;
- int i;
struct kafka_topic_context *tctx;
char *key = NULL;
char *val;
char callback_name[DATA_MAX_NAME_LEN];
char errbuf[1024];
- user_data_t ud;
oconfig_item_t *child;
rd_kafka_conf_res_t ret;
goto errout;
}
- for (i = 0; i < ci->children_num; i++) {
+ for (int i = 0; i < ci->children_num; i++) {
/*
* The code here could be simplified but makes room
* for easy adding of new options later on.
}
} else if (strcasecmp ("Key", child->key) == 0) {
- cf_util_get_string (child, &tctx->key);
- assert (tctx->key != NULL);
+ if (cf_util_get_string (child, &tctx->key) != 0)
+ continue;
+ if (strcasecmp ("Random", tctx->key) == 0) {
+ sfree(tctx->key);
+ tctx->key = strdup (kafka_random_key (KAFKA_RANDOM_KEY_BUFFER));
+ }
} else if (strcasecmp ("Format", child->key) == 0) {
status = cf_util_get_string(child, &key);
if (status != 0)
ssnprintf(callback_name, sizeof(callback_name),
"write_kafka/%s", tctx->topic_name);
- ud.data = tctx;
- ud.free_func = kafka_topic_context_free;
-
- status = plugin_register_write (callback_name, kafka_write, &ud);
+ status = plugin_register_write (callback_name, kafka_write,
+ &(user_data_t) {
+ .data = tctx,
+ .free_func = kafka_topic_context_free,
+ });
if (status != 0) {
WARNING ("write_kafka plugin: plugin_register_write (\"%s\") "
"failed with status %i.",
static int kafka_config(oconfig_item_t *ci) /* {{{ */
{
- int i;
oconfig_item_t *child;
rd_kafka_conf_t *conf;
rd_kafka_conf_res_t ret;
WARNING("cannot allocate kafka configuration.");
return -1;
}
- for (i = 0; i < ci->children_num; i++) {
+ for (int i = 0; i < ci->children_num; i++) {
child = &ci->children[i];
if (strcasecmp("Topic", child->key) == 0) {