X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fwrite_prometheus.c;h=fe7a5b5996edab97ee12b34d08558d6a26c522ed;hb=604607d3463fbe13eac252308f8a2497fb190167;hp=c91f012ff79cdb262ea0e3fb3bed618f82a90763;hpb=cc893903f8453dc96a797b319bdd4e294052de6f;p=collectd.git diff --git a/src/write_prometheus.c b/src/write_prometheus.c index c91f012f..fe7a5b59 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 @@ -210,8 +210,13 @@ static int http_handler(void *cls, struct MHD_Connection *connection, else format_text(buffer); +#if defined(MHD_VERSION) && MHD_VERSION >= 0x00090500 + struct MHD_Response *res = MHD_create_response_from_buffer( + simple.len, simple.data, MHD_RESPMEM_MUST_COPY); +#else 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, want_proto ? CONTENT_TYPE_PROTO : CONTENT_TYPE_TEXT); @@ -249,22 +254,22 @@ static void label_pair_destroy(Io__Prometheus__Client__LabelPair *msg) { sfree(msg); } -/* label_pair_create allocates and initializes a new label pair. */ -static Io__Prometheus__Client__LabelPair *label_pair_create(char const *name, - char const *value) { - Io__Prometheus__Client__LabelPair *msg = calloc(1, sizeof(*msg)); - if (msg == NULL) +/* label_pair_clone allocates and initializes a new label pair. */ +static Io__Prometheus__Client__LabelPair * +label_pair_clone(Io__Prometheus__Client__LabelPair const *orig) { + Io__Prometheus__Client__LabelPair *copy = calloc(1, sizeof(*copy)); + if (copy == NULL) return NULL; - io__prometheus__client__label_pair__init(msg); + io__prometheus__client__label_pair__init(copy); - msg->name = strdup(name); - msg->value = strdup(value); - if ((msg->name == NULL) || (msg->value == NULL)) { - label_pair_destroy(msg); + copy->name = strdup(orig->name); + copy->value = strdup(orig->value); + if ((copy->name == NULL) || (copy->value == NULL)) { + label_pair_destroy(copy); return NULL; } - return msg; + return copy; } /* metric_destroy frees the memory used by a metric. */ @@ -283,47 +288,6 @@ static void metric_destroy(Io__Prometheus__Client__Metric *msg) { sfree(msg); } -/* metric_add_labels adds the labels that identify this metric to m. - * The logic is copied from the "collectd_exporter". Essentially, the labels - * contain the hostname, the plugin instance and the type instance of a - * value_list_t. */ -static int metric_add_labels(Io__Prometheus__Client__Metric *m, - value_list_t const *vl) { - size_t n_label = 1; - if (strlen(vl->plugin_instance) != 0) - n_label++; - if (strlen(vl->type_instance) != 0) - n_label++; - - m->label = calloc(n_label, sizeof(*m->label)); - if (m->label == NULL) - return ENOMEM; - - if (strlen(vl->plugin_instance) != 0) { - m->label[m->n_label] = label_pair_create(vl->plugin, vl->plugin_instance); - m->n_label++; - } - - if (strlen(vl->type_instance) != 0) { - char const *name = "type"; - if (strlen(vl->plugin_instance) == 0) - name = vl->plugin; - - m->label[m->n_label] = label_pair_create(name, vl->type_instance); - m->n_label++; - } - - m->label[m->n_label] = label_pair_create("instance", vl->host); - m->n_label++; - - for (size_t i = 0; i < m->n_label; i++) { - if (m->label[i] == NULL) - return ENOMEM; - } - - return 0; -} - /* metric_cmp compares two metrics. It's prototype makes it easy to use with * qsort(3) and bsearch(3). */ static int metric_cmp(void const *a, void const *b) { @@ -338,35 +302,104 @@ static int metric_cmp(void const *a, void const *b) { return 1; /* Prometheus does not care about the order of labels. All labels in this - * plugin are created by metric_add_labels(), though, and therefore always + * plugin are created by METRIC_ADD_LABELS(), though, and therefore always * appear in the same order. We take advantage of this and simplify the check - * by making sure all labels are the same in each position. */ + * by making sure all labels are the same in each position. + * + * We also only need to check the label values, because the label names are + * the same for all metrics in a metric family. + * + * 3 labels: + * [0] $plugin="$plugin_instance" => $plugin is the same within a family + * [1] type="$type_instance" => "type" is a static string + * [2] instance="$host" => "instance" is a static string + * + * 2 labels, variant 1: + * [0] $plugin="$plugin_instance" => $plugin is the same within a family + * [1] instance="$host" => "instance" is a static string + * + * 2 labels, variant 2: + * [0] $plugin="$type_instance" => $plugin is the same within a family + * [1] instance="$host" => "instance" is a static string + * + * 1 label: + * [1] instance="$host" => "instance" is a static string + */ for (size_t i = 0; i < m_a->n_label; i++) { - int status = strcmp(m_a->label[i]->name, m_b->label[i]->name); + int status = strcmp(m_a->label[i]->value, m_b->label[i]->value); if (status != 0) return status; - status = strcmp(m_a->label[i]->value, m_b->label[i]->value); - if (status != 0) - return status; +#if COLLECT_DEBUG + assert(strcmp(m_a->label[i]->name, m_b->label[i]->name) == 0); +#endif } return 0; } -/* metric_create allocates and initializes a new metric. */ -static Io__Prometheus__Client__Metric *metric_create(value_list_t const *vl) { - Io__Prometheus__Client__Metric *msg = calloc(1, sizeof(*msg)); - if (msg == NULL) +#define METRIC_INIT \ + &(Io__Prometheus__Client__Metric) { \ + .label = \ + (Io__Prometheus__Client__LabelPair *[]){ \ + &(Io__Prometheus__Client__LabelPair){ \ + .name = NULL, \ + }, \ + &(Io__Prometheus__Client__LabelPair){ \ + .name = NULL, \ + }, \ + &(Io__Prometheus__Client__LabelPair){ \ + .name = NULL, \ + }, \ + }, \ + .n_label = 0, \ + } + +#define METRIC_ADD_LABELS(m, vl) \ + do { \ + if (strlen((vl)->plugin_instance) != 0) { \ + (m)->label[(m)->n_label]->name = (char *)(vl)->plugin; \ + (m)->label[(m)->n_label]->value = (char *)(vl)->plugin_instance; \ + (m)->n_label++; \ + } \ + \ + if (strlen((vl)->type_instance) != 0) { \ + (m)->label[(m)->n_label]->name = "type"; \ + if (strlen((vl)->plugin_instance) == 0) \ + (m)->label[(m)->n_label]->name = (char *)(vl)->plugin; \ + (m)->label[(m)->n_label]->value = (char *)(vl)->type_instance; \ + (m)->n_label++; \ + } \ + \ + (m)->label[(m)->n_label]->name = "instance"; \ + (m)->label[(m)->n_label]->value = (char *)(vl)->host; \ + (m)->n_label++; \ + } while (0) + +/* metric_clone allocates and initializes a new metric based on orig. */ +static Io__Prometheus__Client__Metric * +metric_clone(Io__Prometheus__Client__Metric const *orig) { + Io__Prometheus__Client__Metric *copy = calloc(1, sizeof(*copy)); + if (copy == NULL) return NULL; - io__prometheus__client__metric__init(msg); + io__prometheus__client__metric__init(copy); - if (metric_add_labels(msg, vl) != 0) { - metric_destroy(msg); + copy->n_label = orig->n_label; + copy->label = calloc(copy->n_label, sizeof(*copy->label)); + if (copy->label == NULL) { + sfree(copy); return NULL; } - return msg; + for (size_t i = 0; i < copy->n_label; i++) { + copy->label[i] = label_pair_clone(orig->label[i]); + if (copy->label[i] == NULL) { + metric_destroy(copy); + return NULL; + } + } + + return copy; } /* metric_update stores the new value and timestamp in m. */ @@ -452,9 +485,8 @@ static int metric_family_add_metric(Io__Prometheus__Client__MetricFamily *fam, static int metric_family_delete_metric(Io__Prometheus__Client__MetricFamily *fam, value_list_t const *vl) { - Io__Prometheus__Client__Metric *key = metric_create(vl); - if (key == NULL) - return ENOMEM; + Io__Prometheus__Client__Metric *key = METRIC_INIT; + METRIC_ADD_LABELS(key, vl); size_t i; for (i = 0; i < fam->n_metric; i++) { @@ -484,9 +516,8 @@ metric_family_delete_metric(Io__Prometheus__Client__MetricFamily *fam, static Io__Prometheus__Client__Metric * metric_family_get_metric(Io__Prometheus__Client__MetricFamily *fam, value_list_t const *vl) { - Io__Prometheus__Client__Metric *key = metric_create(vl); - if (key == NULL) - return NULL; + Io__Prometheus__Client__Metric *key = METRIC_INIT; + METRIC_ADD_LABELS(key, vl); /* Metrics are sorted in metric_family_add_metric() so that we can do a binary * search here. */ @@ -494,18 +525,21 @@ metric_family_get_metric(Io__Prometheus__Client__MetricFamily *fam, &key, fam->metric, fam->n_metric, sizeof(*fam->metric), metric_cmp); if (m != NULL) { - metric_destroy(key); return *m; } + Io__Prometheus__Client__Metric *new_metric = metric_clone(key); + if (new_metric == NULL) + return NULL; + DEBUG("write_prometheus plugin: created new metric in family"); - int status = metric_family_add_metric(fam, key); + int status = metric_family_add_metric(fam, new_metric); if (status != 0) { - metric_destroy(key); + metric_destroy(new_metric); return NULL; } - return key; + return new_metric; } /* metric_family_update looks up the matching metric in a metric family, @@ -602,8 +636,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."); @@ -617,6 +651,9 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, return fam; } + if (!allocate) + return NULL; + fam = metric_family_create(name, ds, vl, ds_index); if (fam == NULL) { ERROR("write_prometheus plugin: Allocating metric family failed."); @@ -698,7 +735,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; @@ -724,7 +762,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; @@ -733,6 +772,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; }