X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fwrite_prometheus.c;h=eac0d68efb672607f684bede0ace98630a27bf07;hb=e746ad785774de37a30302fef65f1c4aaf8698ab;hp=fe7a5b5996edab97ee12b34d08558d6a26c522ed;hpb=604607d3463fbe13eac252308f8a2497fb190167;p=collectd.git diff --git a/src/write_prometheus.c b/src/write_prometheus.c index fe7a5b59..eac0d68e 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -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++) - ssnprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, - m->label[i]->value); + /* 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]; + snprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, + escape_label_value(value, sizeof(value), m->label[i]->value)); + } strjoin(buffer, buffer_size, labels, m->n_label, ","); return buffer; @@ -135,13 +178,13 @@ static void format_text(ProtobufCBuffer *buffer) { while (c_avl_iterator_next(iter, (void *)&unused_name, (void *)&fam) == 0) { char line[1024]; /* 4x DATA_MAX_NAME_LEN? */ - ssnprintf(line, sizeof(line), "# HELP %s %s\n", fam->name, fam->help); + snprintf(line, sizeof(line), "# HELP %s %s\n", fam->name, fam->help); buffer->append(buffer, strlen(line), (uint8_t *)line); - ssnprintf(line, sizeof(line), "# TYPE %s %s\n", fam->name, - (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) - ? "gauge" - : "counter"); + snprintf(line, sizeof(line), "# TYPE %s %s\n", fam->name, + (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) + ? "gauge" + : "counter"); buffer->append(buffer, strlen(line), (uint8_t *)line); for (size_t i = 0; i < fam->n_metric; i++) { @@ -151,17 +194,17 @@ static void format_text(ProtobufCBuffer *buffer) { char timestamp_ms[24] = ""; if (m->has_timestamp_ms) - ssnprintf(timestamp_ms, sizeof(timestamp_ms), " %" PRIi64, - m->timestamp_ms); + snprintf(timestamp_ms, sizeof(timestamp_ms), " %" PRIi64, + m->timestamp_ms); if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) - ssnprintf(line, sizeof(line), "%s{%s} " GAUGE_FORMAT "%s\n", fam->name, - format_labels(labels, sizeof(labels), m), m->gauge->value, - timestamp_ms); + snprintf(line, sizeof(line), "%s{%s} " GAUGE_FORMAT "%s\n", fam->name, + format_labels(labels, sizeof(labels), m), m->gauge->value, + timestamp_ms); else /* if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__COUNTER) */ - ssnprintf(line, sizeof(line), "%s{%s} %.0f%s\n", fam->name, - format_labels(labels, sizeof(labels), m), m->counter->value, - timestamp_ms); + snprintf(line, sizeof(line), "%s{%s} %.0f%s\n", fam->name, + format_labels(labels, sizeof(labels), m), m->counter->value, + timestamp_ms); buffer->append(buffer, strlen(line), (uint8_t *)line); } @@ -169,8 +212,8 @@ static void format_text(ProtobufCBuffer *buffer) { c_avl_iterator_destroy(iter); char server[1024]; - ssnprintf(server, sizeof(server), "\n# collectd/write_prometheus %s at %s\n", - PACKAGE_VERSION, hostname_g); + snprintf(server, sizeof(server), "\n# collectd/write_prometheus %s at %s\n", + PACKAGE_VERSION, hostname_g); buffer->append(buffer, strlen(server), (uint8_t *)server); pthread_mutex_unlock(&metrics_lock); @@ -503,9 +546,14 @@ metric_family_delete_metric(Io__Prometheus__Client__MetricFamily *fam, ((fam->n_metric - 1) - i) * sizeof(fam->metric[i])); fam->n_metric--; + if (fam->n_metric == 0) { + sfree(fam->metric); + return 0; + } + Io__Prometheus__Client__Metric **tmp = realloc(fam->metric, fam->n_metric * sizeof(*fam->metric)); - if ((tmp != NULL) || (fam->n_metric == 0)) + if (tmp != NULL) fam->metric = tmp; return 0; @@ -583,7 +631,7 @@ metric_family_create(char *name, data_set_t const *ds, value_list_t const *vl, msg->name = name; char help[1024]; - ssnprintf( + snprintf( help, sizeof(help), "write_prometheus plugin: '%s' Type: '%s', Dstype: '%s', Dsname: '%s'", vl->plugin, vl->type, DS_TYPE_TO_STRING(ds->ds[ds_index].type), @@ -651,8 +699,10 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index, return fam; } - if (!allocate) + if (!allocate) { + sfree(name); return NULL; + } fam = metric_family_create(name, ds, vl, ds_index); if (fam == NULL) { @@ -825,5 +875,3 @@ void module_register() { /* user data = */ NULL); plugin_register_shutdown("write_prometheus", prom_shutdown); } - -/* vim: set sw=2 sts=2 et fdm=marker : */