X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fwrite_prometheus.c;h=61c43fed7ae921265e049c663804a92e0273a060;hb=295947714f23935be771c98f1071564d5567d33a;hp=6fb5a8664f3a13be51bc234448020a39bd265e10;hpb=dc57f6e8f2394b51dd35d1d8231ef110c075b831;p=collectd.git diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 6fb5a866..61c43fed 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -37,7 +37,7 @@ #include #ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA -#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T(300) +#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300) #endif #define VARINT_UINT32_BYTES 5 @@ -98,6 +98,43 @@ static void format_protobuf(ProtobufCBuffer *buffer) { pthread_mutex_unlock(&metrics_lock); } +static char const *escape_label_value(char *buffer, size_t buffer_size, + char const *value) { + /* shortcut for values that don't need escaping. */ + if (strpbrk(value, "\n\"\\") == NULL) + return value; + + size_t value_len = strlen(value); + size_t buffer_len = 0; + + for (size_t i = 0; i < value_len; i++) { + switch (value[i]) { + case '\n': + case '"': + case '\\': + if ((buffer_size - buffer_len) < 3) { + break; + } + buffer[buffer_len] = '\\'; + buffer[buffer_len + 1] = (value[i] == '\n') ? 'n' : value[i]; + buffer_len += 2; + break; + + default: + if ((buffer_size - buffer_len) < 2) { + break; + } + buffer[buffer_len] = value[i]; + buffer_len++; + break; + } + } + + assert(buffer_len < buffer_size); + buffer[buffer_len] = 0; + return buffer; +} + /* format_labels formats a metric's labels in Prometheus-compatible format. This * format looks like this: * @@ -109,16 +146,22 @@ static char *format_labels(char *buffer, size_t buffer_size, assert(m->n_label >= 1); assert(m->n_label <= 3); -#define LABEL_BUFFER_SIZE (2 * DATA_MAX_NAME_LEN + 4) +#define LABEL_KEY_SIZE DATA_MAX_NAME_LEN +#define LABEL_VALUE_SIZE (2 * DATA_MAX_NAME_LEN - 1) +#define LABEL_BUFFER_SIZE (LABEL_KEY_SIZE + LABEL_VALUE_SIZE + 4) char *labels[3] = { (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, }; - for (size_t i = 0; i < m->n_label; i++) + /* N.B.: the label *names* are hard-coded by this plugin and therefore we + * know that they are sane. */ + for (size_t i = 0; i < m->n_label; i++) { + char value[LABEL_VALUE_SIZE]; ssnprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, - m->label[i]->value); + escape_label_value(value, sizeof(value), m->label[i]->value)); + } strjoin(buffer, buffer_size, labels, m->n_label, ","); return buffer; @@ -210,12 +253,11 @@ static int http_handler(void *cls, struct MHD_Connection *connection, else format_text(buffer); - struct MHD_Response *res = #if defined(MHD_VERSION) && MHD_VERSION >= 0x00090500 - MHD_create_response_from_buffer( + struct MHD_Response *res = MHD_create_response_from_buffer( simple.len, simple.data, MHD_RESPMEM_MUST_COPY); #else - MHD_create_response_from_data( + struct MHD_Response *res = MHD_create_response_from_data( simple.len, simple.data, /* must_free = */ 0, /* must_copy = */ 1); #endif MHD_add_response_header(res, MHD_HTTP_HEADER_CONTENT_TYPE, @@ -637,8 +679,8 @@ static char *metric_family_name(data_set_t const *ds, value_list_t const *vl, /* metric_family_get looks up the matching metric family, allocating it if * necessary. */ static Io__Prometheus__Client__MetricFamily * -metric_family_get(data_set_t const *ds, value_list_t const *vl, - size_t ds_index) { +metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index, + _Bool allocate) { char *name = metric_family_name(ds, vl, ds_index); if (name == NULL) { ERROR("write_prometheus plugin: Allocating metric family name failed."); @@ -652,6 +694,11 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, return fam; } + if (!allocate) { + sfree(name); + return NULL; + } + fam = metric_family_create(name, ds, vl, ds_index); if (fam == NULL) { ERROR("write_prometheus plugin: Allocating metric family failed."); @@ -733,7 +780,8 @@ static int prom_write(data_set_t const *ds, value_list_t const *vl, pthread_mutex_lock(&metrics_lock); for (size_t i = 0; i < ds->ds_num; i++) { - Io__Prometheus__Client__MetricFamily *fam = metric_family_get(ds, vl, i); + Io__Prometheus__Client__MetricFamily *fam = + metric_family_get(ds, vl, i, /* allocate = */ 1); if (fam == NULL) continue; @@ -759,7 +807,8 @@ static int prom_missing(value_list_t const *vl, pthread_mutex_lock(&metrics_lock); for (size_t i = 0; i < ds->ds_num; i++) { - Io__Prometheus__Client__MetricFamily *fam = metric_family_get(ds, vl, i); + Io__Prometheus__Client__MetricFamily *fam = + metric_family_get(ds, vl, i, /* allocate = */ 0); if (fam == NULL) continue; @@ -768,6 +817,7 @@ static int prom_missing(value_list_t const *vl, ERROR("write_prometheus plugin: Deleting a metric in family \"%s\" " "failed with status %d", fam->name, status); + continue; } @@ -820,5 +870,3 @@ void module_register() { /* user data = */ NULL); plugin_register_shutdown("write_prometheus", prom_shutdown); } - -/* vim: set sw=2 sts=2 et fdm=marker : */