diff --git a/lib/cmetrics/CMakeLists.txt b/lib/cmetrics/CMakeLists.txt index 01826099978..52ea99f042a 100644 --- a/lib/cmetrics/CMakeLists.txt +++ b/lib/cmetrics/CMakeLists.txt @@ -5,8 +5,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # CMetrics Version set(CMT_VERSION_MAJOR 0) -set(CMT_VERSION_MINOR 6) -set(CMT_VERSION_PATCH 6) +set(CMT_VERSION_MINOR 7) +set(CMT_VERSION_PATCH 0) set(CMT_VERSION_STR "${CMT_VERSION_MAJOR}.${CMT_VERSION_MINOR}.${CMT_VERSION_PATCH}") # Include helpers diff --git a/lib/cmetrics/include/cmetrics/cmt_cat.h b/lib/cmetrics/include/cmetrics/cmt_cat.h index 220dc2eebcd..d393baed34e 100644 --- a/lib/cmetrics/include/cmetrics/cmt_cat.h +++ b/lib/cmetrics/include/cmetrics/cmt_cat.h @@ -22,6 +22,17 @@ #include +struct cmt_counter; +struct cmt_gauge; +struct cmt_untyped; +struct cmt_histogram; +struct cmt_summary; + +int cmt_cat_counter(struct cmt *cmt, struct cmt_counter *counter); +int cmt_cat_gauge(struct cmt *cmt, struct cmt_gauge *gauge); +int cmt_cat_untyped(struct cmt *cmt, struct cmt_untyped *untyped); +int cmt_cat_histogram(struct cmt *cmt, struct cmt_histogram *histogram); +int cmt_cat_summary(struct cmt *cmt, struct cmt_summary *summary); int cmt_cat(struct cmt *dst, struct cmt *src); #endif diff --git a/lib/cmetrics/include/cmetrics/cmt_encode_cloudwatch_emf.h b/lib/cmetrics/include/cmetrics/cmt_encode_cloudwatch_emf.h new file mode 100644 index 00000000000..802324dfd89 --- /dev/null +++ b/lib/cmetrics/include/cmetrics/cmt_encode_cloudwatch_emf.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2024 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef CMT_ENCODE_CLOUDWATCH_EMF_H +#define CMT_ENCODE_CLOUDWATCH_EMF_H + +#include +#include +#include + +#define CMT_ENCODE_CLOUDWATCH_EMF_SUCCESS 0 +#define CMT_ENCODE_CLOUDWATCH_EMF_INVALID_ARGUMENT_ERROR -1 +#define CMT_ENCODE_CLOUDWATCH_EMF_CREATION_FAILED -2 +#define CMT_ENCODE_CLOUDWATCH_EMF_INVALID_DATA_ERROR -4 + +/* Metric Unit */ +#define CMT_EMF_UNIT_PERCENT "Percent" +#define CMT_EMF_UNIT_BYTES "Bytes" +#define CMT_EMF_UNIT_COUNTER "Counter" + +int cmt_encode_cloudwatch_emf_create(struct cmt *cmt, + char **out_buf, size_t *out_size, + int wrap_array); +void cmt_encode_cloudwatch_emf_destroy(char *out_buf); + +#endif diff --git a/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h b/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h index c160c27153d..9351ed6d4e6 100644 --- a/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h +++ b/lib/cmetrics/include/cmetrics/cmt_encode_prometheus_remote_write.h @@ -25,12 +25,14 @@ #include #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ADD_METADATA CMT_FALSE +#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_THRESHOLD 60L*60L*1000000000L #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS 0 #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ALLOCATION_ERROR 1 #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_UNEXPECTED_ERROR 2 #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_INVALID_ARGUMENT_ERROR 3 #define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_UNEXPECTED_METRIC_TYPE 4 +#define CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR 5 struct cmt_prometheus_metric_metadata { Prometheus__MetricMetadata data; diff --git a/lib/cmetrics/include/cmetrics/cmt_filter.h b/lib/cmetrics/include/cmetrics/cmt_filter.h new file mode 100644 index 00000000000..7676cbf5c3d --- /dev/null +++ b/lib/cmetrics/include/cmetrics/cmt_filter.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2021-2024 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CMT_FILTER_H +#define CMT_FILTER_H + +#include +#include + +#define CMT_FILTER_EXCLUDE (1 << 1) +#define CMT_FILTER_PREFIX (1 << 2) +#define CMT_FILTER_SUBSTRING (1 << 3) +#define CMT_FILTER_REGEX_SEARCH_LABELS (1 << 4) + +#define CMT_FILTER_SUCCESS 0 +#define CMT_FILTER_INVALID_ARGUMENT -1 +#define CMT_FILTER_INVALID_FLAGS -2 +#define CMT_FILTER_FAILED_OPERATION -3 + +int cmt_filter(struct cmt *dst, struct cmt *src, + const char *fqname, const char *label_key, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags); + +#endif diff --git a/lib/cmetrics/src/CMakeLists.txt b/lib/cmetrics/src/CMakeLists.txt index e7db7596c4c..166fa0583d6 100644 --- a/lib/cmetrics/src/CMakeLists.txt +++ b/lib/cmetrics/src/CMakeLists.txt @@ -22,12 +22,14 @@ set(src cmt_time.c cmt_label.c cmt_cat.c + cmt_filter.c cmetrics.c cmt_encode_opentelemetry.c cmt_decode_opentelemetry.c cmt_encode_prometheus.c cmt_encode_prometheus_remote_write.c cmt_encode_splunk_hec.c + cmt_encode_cloudwatch_emf.c cmt_encode_text.c cmt_encode_influx.c cmt_encode_msgpack.c diff --git a/lib/cmetrics/src/cmt_cat.c b/lib/cmetrics/src/cmt_cat.c index 5475a98a3f3..d93e5415f3a 100644 --- a/lib/cmetrics/src/cmt_cat.c +++ b/lib/cmetrics/src/cmt_cat.c @@ -178,7 +178,7 @@ static int copy_map(struct cmt_opts *opts, struct cmt_map *dst, struct cmt_map * } -static int copy_counter(struct cmt *cmt, struct cmt_counter *counter) +int cmt_cat_counter(struct cmt *cmt, struct cmt_counter *counter) { int ret; char **labels = NULL; @@ -213,7 +213,7 @@ static int copy_counter(struct cmt *cmt, struct cmt_counter *counter) return 0; } -static int copy_gauge(struct cmt *cmt, struct cmt_gauge *gauge) +int cmt_cat_gauge(struct cmt *cmt, struct cmt_gauge *gauge) { int ret; char **labels = NULL; @@ -247,7 +247,7 @@ static int copy_gauge(struct cmt *cmt, struct cmt_gauge *gauge) return 0; } -static int copy_untyped(struct cmt *cmt, struct cmt_untyped *untyped) +int cmt_cat_untyped(struct cmt *cmt, struct cmt_untyped *untyped) { int ret; char **labels = NULL; @@ -281,7 +281,7 @@ static int copy_untyped(struct cmt *cmt, struct cmt_untyped *untyped) return 0; } -static int copy_histogram(struct cmt *cmt, struct cmt_histogram *histogram) +int cmt_cat_histogram(struct cmt *cmt, struct cmt_histogram *histogram) { int i; double val; @@ -331,7 +331,7 @@ static int copy_histogram(struct cmt *cmt, struct cmt_histogram *histogram) return 0; } -static int copy_summary(struct cmt *cmt, struct cmt_summary *summary) +int cmt_cat_summary(struct cmt *cmt, struct cmt_summary *summary) { int i; int ret; @@ -395,7 +395,7 @@ static int append_context(struct cmt *dst, struct cmt *src) /* Counters */ cfl_list_foreach(head, &src->counters) { counter = cfl_list_entry(head, struct cmt_counter, _head); - ret = copy_counter(dst, counter); + ret = cmt_cat_counter(dst, counter); if (ret == -1) { return -1; } @@ -404,7 +404,7 @@ static int append_context(struct cmt *dst, struct cmt *src) /* Gauges */ cfl_list_foreach(head, &src->gauges) { gauge = cfl_list_entry(head, struct cmt_gauge, _head); - ret = copy_gauge(dst, gauge); + ret = cmt_cat_gauge(dst, gauge); if (ret == -1) { return -1; } @@ -413,7 +413,7 @@ static int append_context(struct cmt *dst, struct cmt *src) /* Untyped */ cfl_list_foreach(head, &src->untypeds) { untyped = cfl_list_entry(head, struct cmt_untyped, _head); - ret = copy_untyped(dst, untyped); + ret = cmt_cat_untyped(dst, untyped); if (ret == -1) { return -1; } @@ -422,7 +422,7 @@ static int append_context(struct cmt *dst, struct cmt *src) /* Histogram */ cfl_list_foreach(head, &src->histograms) { histogram = cfl_list_entry(head, struct cmt_histogram, _head); - ret = copy_histogram(dst, histogram); + ret = cmt_cat_histogram(dst, histogram); if (ret == -1) { return -1; } @@ -431,7 +431,7 @@ static int append_context(struct cmt *dst, struct cmt *src) /* Summary */ cfl_list_foreach(head, &src->summaries) { summary = cfl_list_entry(head, struct cmt_summary, _head); - ret = copy_summary(dst, summary); + ret = cmt_cat_summary(dst, summary); if (ret == -1) { return -1; } diff --git a/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c b/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c new file mode 100644 index 00000000000..af36eaa0798 --- /dev/null +++ b/lib/cmetrics/src/cmt_encode_cloudwatch_emf.c @@ -0,0 +1,475 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2024 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pack_basic_header(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map, struct cmt_metric *metric) +{ + int labels = 0; + int static_labels = 0; + struct cfl_list *head; + struct cmt_map_label *label_k; + struct cmt_label *slabel; + struct cmt_opts *opts = map->opts; + + mpack_write_cstr(writer, "_aws"); + + mpack_start_map(writer, 2); + + /* Millisecond precision */ + mpack_write_cstr(writer, "Timestamp"); + mpack_write_u64(writer, cmt_metric_get_timestamp(metric) / 1000000L); + + mpack_write_cstr(writer, "CloudWatchMetrics"); + mpack_start_array(writer, 1); + + mpack_start_map(writer, 3); + + mpack_write_cstr(writer, "Namespace"); + if (opts->ns) { + mpack_write_cstr(writer, opts->ns); + } + else { + mpack_write_cstr(writer, "cmetrics-metrics"); + } + + mpack_write_cstr(writer, "Dimensions"); + + static_labels = cmt_labels_count(cmt->static_labels); + labels += static_labels; + labels += map->label_count; + mpack_start_array(writer, 1); + mpack_start_array(writer, labels); + cfl_list_foreach(head, &map->label_keys) { + label_k = cfl_list_entry(head, struct cmt_map_label, _head); + mpack_write_cstr(writer, label_k->name); + } + + cfl_list_foreach(head, &cmt->static_labels->list) { + slabel = cfl_list_entry(head, struct cmt_label, _head); + mpack_write_cstr(writer, slabel->key); + } + mpack_finish_array(writer); /* Dimensions (inner) */ + mpack_finish_array(writer); /* Dimensions (outer) */ +} + +static void pack_basic_header_finish(mpack_writer_t *writer) +{ + mpack_finish_map(writer); /* CloudWatchMetrics (inner map) */ + mpack_finish_array(writer); /* CloudWatchMetrics (outer array) */ + mpack_finish_map(writer); /* _aws */ +} + +static void pack_cmetrics_type(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map) +{ + mpack_write_cstr(writer, "prom_metric_type"); + if (map->type == CMT_COUNTER) { + mpack_write_cstr(writer, "counter"); + } + else if (map->type == CMT_GAUGE) { + mpack_write_cstr(writer, "gauge"); + } + else if (map->type == CMT_UNTYPED) { + mpack_write_cstr(writer, "untyped"); + } + else if (map->type == CMT_SUMMARY) { + mpack_write_cstr(writer, "summary"); + } + else if (map->type == CMT_HISTOGRAM) { + mpack_write_cstr(writer, "histogram"); + } + else { + mpack_write_cstr(writer, ""); + } +} + +static void pack_histogram_metric(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map, struct cmt_metric *metric) +{ + int i; + int k; + int index = 0; + double val = 0.0; + double tmp; + uint64_t *hist_metrics; + struct cmt_opts *opts = map->opts; + struct cmt_histogram *histogram = NULL; + struct cmt_histogram_buckets *buckets = NULL; + + histogram = (struct cmt_histogram *) map->parent; + buckets = histogram->buckets; + hist_metrics = calloc(buckets->count + 1, sizeof(uint64_t)); + + for (i = 0; i <= buckets->count; i++) { + hist_metrics[i] = cmt_metric_hist_get_value(metric, i); + } + + for (i = 0; i <= buckets->count; i++) { + index = i; + + for (k = i + 1; k <= buckets->count; k++) { + if (hist_metrics[k] < hist_metrics[index]) { + index = k; + } + } + + tmp = hist_metrics[i]; + hist_metrics[i] = hist_metrics[index]; + hist_metrics[index] = tmp; + } + mpack_write_cstr(writer, opts->fqname); + mpack_start_map(writer, 4); + mpack_write_cstr(writer, "Min"); + mpack_write_double(writer, hist_metrics[0]); + mpack_write_cstr(writer, "Max"); + mpack_write_double(writer, hist_metrics[buckets->count - 1]); + mpack_write_cstr(writer, "Sum"); + val = cmt_metric_hist_get_sum_value(metric); + mpack_write_double(writer, val); + mpack_write_cstr(writer, "Count"); + val = cmt_metric_hist_get_count_value(metric); + mpack_write_double(writer, val); + mpack_finish_map(writer); + + free(hist_metrics); +} + +static void pack_summary_metric(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map, struct cmt_metric *metric) +{ + double val = 0.0; + struct cmt_opts *opts = map->opts; + struct cmt_summary *summary = NULL; + + summary = (struct cmt_summary *) map->parent; + + mpack_write_cstr(writer, opts->fqname); + mpack_start_map(writer, 4); + mpack_write_cstr(writer, "Min"); + val = cmt_summary_quantile_get_value(metric, 0); + mpack_write_double(writer, val); + mpack_write_cstr(writer, "Max"); + val = cmt_summary_quantile_get_value(metric, summary->quantiles_count - 1); + mpack_write_double(writer, val); + mpack_write_cstr(writer, "Sum"); + val = cmt_summary_get_sum_value(metric); + mpack_write_double(writer, val); + mpack_write_cstr(writer, "Count"); + val = cmt_summary_get_count_value(metric); + mpack_write_double(writer, val); + mpack_finish_map(writer); +} + +static int pack_metric(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map, struct cmt_metric *metric) +{ + int s = 0; + double val = 0.0; + int c_labels = 0; + int static_labels = 0; + struct cfl_list *head; + struct cmt_map_label *label_k; + struct cmt_map_label *label_v; + struct cmt_label *slabel; + struct cmt_opts *opts = map->opts; + + c_labels = cfl_list_size(&metric->labels); + s = 3; + + if (c_labels > 0) { + s += c_labels; + } + + static_labels = cmt_labels_count(cmt->static_labels); + if (static_labels > 0) { + s += static_labels; + } + + mpack_start_map(writer, s); + + pack_basic_header(writer, cmt, map, metric); + + /* Pack the actual metrics */ + mpack_write_cstr(writer, "Metrics"); + mpack_start_array(writer, 1); + if (map->type == CMT_COUNTER) { + mpack_start_map(writer, 3); + mpack_write_cstr(writer, "Name"); + mpack_write_cstr(writer, opts->fqname); + mpack_write_cstr(writer, "Unit"); + mpack_write_cstr(writer, CMT_EMF_UNIT_COUNTER); + mpack_write_cstr(writer, "StorageResolution"); + mpack_write_int(writer, 60); + mpack_finish_map(writer); + } + else { + mpack_start_map(writer, 2); + mpack_write_cstr(writer, "Name"); + mpack_write_cstr(writer, opts->fqname); + mpack_write_cstr(writer, "StorageResolution"); + mpack_write_int(writer, 60); + mpack_finish_map(writer); + } + mpack_finish_array(writer); /* Metrics */ + + pack_basic_header_finish(writer); + + /* dimensions */ + if (c_labels > 0) { + label_k = cfl_list_entry_first(&map->label_keys, struct cmt_map_label, _head); + + cfl_list_foreach(head, &metric->labels) { + label_v = cfl_list_entry(head, struct cmt_map_label, _head); + mpack_write_cstr(writer, label_k->name); + mpack_write_cstr(writer, label_v->name); + + label_k = cfl_list_entry_next(&label_k->_head, struct cmt_map_label, + _head, &map->label_keys); + } + } + + if (static_labels > 0) { + cfl_list_foreach(head, &cmt->static_labels->list) { + slabel = cfl_list_entry(head, struct cmt_label, _head); + mpack_write_cstr(writer, slabel->key); + mpack_write_cstr(writer, slabel->val); + } + } + + /* metric type */ + pack_cmetrics_type(writer, cmt, map); + + /* metrics */ + if (map->type == CMT_SUMMARY) { + pack_summary_metric(writer, cmt, map, metric); + } + else if (map->type == CMT_HISTOGRAM) { + pack_histogram_metric(writer, cmt, map, metric); + } + else { + mpack_write_cstr(writer, opts->fqname); + val = cmt_metric_get_value(metric); + mpack_write_double(writer, val); + } + + /* Finish creating up the EMF format for a metrics */ + mpack_finish_map(writer); + + return 0; +} + +static void pack_metrics(mpack_writer_t *writer, struct cmt *cmt, + struct cmt_map *map) +{ + struct cfl_list *head; + struct cmt_metric *metric = NULL; + + /* Simple metric, no labels */ + if (map->metric_static_set == 1) { + pack_metric(writer, cmt, map, &map->metric); + } + + cfl_list_foreach(head, &map->metrics) { + metric = cfl_list_entry(head, struct cmt_metric, _head); + pack_metric(writer, cmt, map, metric); + } +} + +static size_t count_metrics(struct cmt *cmt) +{ + size_t metric_count; + struct cmt_histogram *histogram; + struct cmt_summary *summary; + struct cmt_untyped *untyped; + struct cmt_counter *counter; + struct cmt_gauge *gauge; + struct cfl_list *head; + struct cmt_map *map; + + metric_count = 0; + /* Counters */ + cfl_list_foreach(head, &cmt->counters) { + counter = cfl_list_entry(head, struct cmt_counter, _head); + map = counter->map; + if (map->metric_static_set == 1) { + metric_count++; + } + metric_count += cfl_list_size(&map->metrics); + } + + /* Gauges */ + cfl_list_foreach(head, &cmt->gauges) { + gauge = cfl_list_entry(head, struct cmt_gauge, _head); + map = gauge->map; + if (map->metric_static_set == 1) { + metric_count++; + } + metric_count += cfl_list_size(&map->metrics); + } + + /* Untyped */ + cfl_list_foreach(head, &cmt->untypeds) { + untyped = cfl_list_entry(head, struct cmt_untyped, _head); + map = untyped->map; + if (map->metric_static_set == 1) { + metric_count++; + } + metric_count += cfl_list_size(&map->metrics); + } + + /* Summary */ + cfl_list_foreach(head, &cmt->summaries) { + summary = cfl_list_entry(head, struct cmt_summary, _head); + map = summary->map; + if (map->metric_static_set == 1) { + metric_count++; + } + metric_count += cfl_list_size(&map->metrics); + } + + /* Histogram */ + cfl_list_foreach(head, &cmt->histograms) { + histogram = cfl_list_entry(head, struct cmt_histogram, _head); + map = histogram->map; + if (map->metric_static_set == 1) { + metric_count++; + } + metric_count += cfl_list_size(&map->metrics); + } + + return metric_count; +} + +static int pack_context_metrics(mpack_writer_t *writer, struct cmt *cmt, int wrap_array) +{ + size_t metric_count; + struct cmt_histogram *histogram; + struct cmt_summary *summary; + struct cmt_untyped *untyped; + struct cmt_counter *counter; + struct cmt_gauge *gauge; + struct cfl_list *head; + + if (wrap_array == CMT_TRUE) { + metric_count = count_metrics(cmt); + mpack_start_array(writer, metric_count); + } + + /* Counters */ + cfl_list_foreach(head, &cmt->counters) { + counter = cfl_list_entry(head, struct cmt_counter, _head); + pack_metrics(writer, cmt, counter->map); + } + + /* Gauges */ + cfl_list_foreach(head, &cmt->gauges) { + gauge = cfl_list_entry(head, struct cmt_gauge, _head); + pack_metrics(writer, cmt, gauge->map); + } + + /* Untyped */ + cfl_list_foreach(head, &cmt->untypeds) { + untyped = cfl_list_entry(head, struct cmt_untyped, _head); + pack_metrics(writer, cmt, untyped->map); + } + + /* Summary */ + cfl_list_foreach(head, &cmt->summaries) { + summary = cfl_list_entry(head, struct cmt_summary, _head); + pack_metrics(writer, cmt, summary->map); + } + + /* Histogram */ + cfl_list_foreach(head, &cmt->histograms) { + histogram = cfl_list_entry(head, struct cmt_histogram, _head); + pack_metrics(writer, cmt, histogram->map); + } + + if (wrap_array == CMT_TRUE) { + mpack_finish_array(writer); /* outermost context scope */ + } + + return CMT_ENCODE_CLOUDWATCH_EMF_SUCCESS; +} + +static int pack_emf_payload(mpack_writer_t *writer, struct cmt *cmt, int wrap_array) +{ + int result; + + result = pack_context_metrics(writer, cmt, wrap_array); + + if (result != CMT_ENCODE_CLOUDWATCH_EMF_SUCCESS) { + return CMT_ENCODE_CLOUDWATCH_EMF_CREATION_FAILED; + } + + return 0; +} + +int cmt_encode_cloudwatch_emf_create(struct cmt *cmt, + char **out_buf, size_t *out_size, + int wrap_array) +{ + char *data; + size_t size; + mpack_writer_t writer; + int result; + + if (cmt == NULL) { + return CMT_ENCODE_CLOUDWATCH_EMF_INVALID_ARGUMENT_ERROR; + } + + mpack_writer_init_growable(&writer, &data, &size); + + result = pack_emf_payload(&writer, cmt, wrap_array); + + if (mpack_writer_destroy(&writer) != mpack_ok) { + fprintf(stderr, "An error occurred encoding the data!\n"); + + return CMT_ENCODE_CLOUDWATCH_EMF_INVALID_DATA_ERROR; + } + + if (result != 0) { + return result; + } + + *out_buf = data; + *out_size = size; + + return 0; +} + +void cmt_encode_cloudwatch_emf_destroy(char *out_buf) +{ + if (out_buf != NULL) { + MPACK_FREE(out_buf); + } +} diff --git a/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c b/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c index 2aede25e7e2..b5421de34d4 100644 --- a/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c +++ b/lib/cmetrics/src/cmt_encode_prometheus_remote_write.c @@ -644,6 +644,17 @@ int pack_basic_metric_sample(struct cmt_prometheus_remote_write_context *context return append_metric_to_timeseries(time_series, metric); } +static int check_staled_timestamp(struct cmt_metric *metric, uint64_t now, uint64_t cutoff) +{ + uint64_t ts; + uint64_t diff; + + ts = cmt_metric_get_timestamp(metric); + diff = now - ts; + + return diff > cutoff; +} + int pack_basic_type(struct cmt_prometheus_remote_write_context *context, struct cmt_map *map) { @@ -651,11 +662,20 @@ int pack_basic_type(struct cmt_prometheus_remote_write_context *context, struct cmt_metric *metric; int result; struct cfl_list *head; + uint64_t now; context->sequence_number++; add_metadata = CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_ADD_METADATA; + now = cfl_time_now(); + if (map->metric_static_set == CMT_TRUE) { + if (check_staled_timestamp(&map->metric, now, + CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_THRESHOLD)) { + /* Skip processing metrics which are staled over the threshold */ + return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR; + } + result = pack_basic_metric_sample(context, map, &map->metric, add_metadata); if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { @@ -666,6 +686,12 @@ int pack_basic_type(struct cmt_prometheus_remote_write_context *context, cfl_list_foreach(head, &map->metrics) { metric = cfl_list_entry(head, struct cmt_metric, _head); + if (check_staled_timestamp(metric, now, + CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_THRESHOLD)) { + /* Skip processing metrics which are staled over over the threshold */ + return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR; + } + result = pack_basic_metric_sample(context, map, metric, add_metadata); if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { @@ -699,6 +725,15 @@ int pack_complex_metric_sample(struct cmt_prometheus_remote_write_context *conte struct cmt_summary *summary; int result; size_t index; + uint64_t now; + + now = cfl_time_now(); + + if (check_staled_timestamp(metric, now, + CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_THRESHOLD)) { + /* Skip processing metrics which are staled over the threshold */ + return CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR; + } additional_label_caption = cfl_sds_create_len(NULL, 128); @@ -1067,6 +1102,10 @@ cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt) counter = cfl_list_entry(head, struct cmt_counter, _head); result = pack_basic_type(&context, counter->map); + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { + continue; + } + if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { break; } @@ -1078,6 +1117,10 @@ cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt) gauge = cfl_list_entry(head, struct cmt_gauge, _head); result = pack_basic_type(&context, gauge->map); + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { + continue; + } + if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { break; } @@ -1089,6 +1132,10 @@ cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt) cfl_list_foreach(head, &cmt->untypeds) { untyped = cfl_list_entry(head, struct cmt_untyped, _head); pack_basic_type(&context, untyped->map); + + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { + continue; + } } } @@ -1098,6 +1145,10 @@ cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt) summary = cfl_list_entry(head, struct cmt_summary, _head); result = pack_complex_type(&context, summary->map); + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { + continue; + } + if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { break; } @@ -1110,13 +1161,18 @@ cfl_sds_t cmt_encode_prometheus_remote_write_create(struct cmt *cmt) histogram = cfl_list_entry(head, struct cmt_histogram, _head); result = pack_complex_type(&context, histogram->map); + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { + continue; + } + if (result != CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { break; } } } - if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS) { + if (result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_SUCCESS || + result == CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_ERROR) { buf = render_remote_write_context_to_sds(&context); } diff --git a/lib/cmetrics/src/cmt_filter.c b/lib/cmetrics/src/cmt_filter.c new file mode 100644 index 00000000000..55bfc07098c --- /dev/null +++ b/lib/cmetrics/src/cmt_filter.c @@ -0,0 +1,329 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2021-2024 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int compare_label_keys(struct cmt_map *src, const char *label_key, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags) +{ + struct cfl_list *head; + struct cmt_map_label *label; + size_t label_key_size = 0; + + if (flags & CMT_FILTER_PREFIX) { + if (label_key == NULL) { + return CMT_FALSE; + } + + label_key_size = strlen(label_key); + } + + cfl_list_foreach(head, &src->label_keys) { + label = cfl_list_entry(head, struct cmt_map_label, _head); + /* compare label_keys for prefix */ + if (flags & CMT_FILTER_PREFIX) { + if (strncmp(label->name, label_key, label_key_size) == 0) { + return (flags & CMT_FILTER_EXCLUDE) ? CMT_FALSE : CMT_TRUE; + } + + return (flags & CMT_FILTER_EXCLUDE) ? CMT_TRUE : CMT_FALSE; + } + + /* compare label_keys for substring */ + if (flags & CMT_FILTER_SUBSTRING) { + if (strstr(label->name, label_key) != NULL) { + return (flags & CMT_FILTER_EXCLUDE) ? CMT_FALSE : CMT_TRUE; + } + + return (flags & CMT_FILTER_EXCLUDE) ? CMT_TRUE : CMT_FALSE; + } + + /* Compare with an external context (e.g. Onigmo). + * flb_regex_match should take three arguments that are + * flb_regex context, string and its string length. + * The length of string is changed by the callback and not determined by label_key. + */ + if (compare_ctx != NULL && compare != NULL) { + return compare(compare_ctx, label->name, strlen(label->name)); + } + } + + return CMT_FALSE; +} + +static int filter_context_label_key(struct cmt *dst, struct cmt *src, + const char *label_key, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags) +{ + int ret; + struct cfl_list *head; + struct cmt_counter *counter; + struct cmt_gauge *gauge; + struct cmt_untyped *untyped; + struct cmt_histogram *histogram; + struct cmt_summary *summary; + + /* Counters */ + cfl_list_foreach(head, &src->counters) { + counter = cfl_list_entry(head, struct cmt_counter, _head); + + if (compare_label_keys(counter->map, label_key, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_counter(dst, counter); + if (ret == -1) { + return -1; + } + } + + /* Gauges */ + cfl_list_foreach(head, &src->gauges) { + gauge = cfl_list_entry(head, struct cmt_gauge, _head); + + if (compare_label_keys(gauge->map, label_key, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_gauge(dst, gauge); + if (ret == -1) { + return -1; + } + } + + /* Untyped */ + cfl_list_foreach(head, &src->untypeds) { + untyped = cfl_list_entry(head, struct cmt_untyped, _head); + + if (compare_label_keys(untyped->map, label_key, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_untyped(dst, untyped); + if (ret == -1) { + return -1; + } + } + + /* Histogram */ + cfl_list_foreach(head, &src->histograms) { + histogram = cfl_list_entry(head, struct cmt_histogram, _head); + + if (compare_label_keys(histogram->map, label_key, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_histogram(dst, histogram); + if (ret == -1) { + return -1; + } + } + + /* Summary */ + cfl_list_foreach(head, &src->summaries) { + summary = cfl_list_entry(head, struct cmt_summary, _head); + + if (compare_label_keys(summary->map, label_key, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_summary(dst, summary); + if (ret == -1) { + return -1; + } + } + + return CMT_FILTER_SUCCESS; +} + +static int compare_fqname(struct cmt_opts *src, const char *fqname, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags) +{ + size_t fqname_size; + + if (flags & CMT_FILTER_PREFIX) { + if (fqname == NULL) { + return CMT_FALSE; + } + + fqname_size = strlen(fqname); + } + else if (compare_ctx != NULL && compare != NULL) { + fqname_size = strlen(src->fqname); + } + + /* compare fqname for prefix */ + if (flags & CMT_FILTER_PREFIX) { + if (strncmp(src->fqname, fqname, fqname_size) == 0) { + return (flags & CMT_FILTER_EXCLUDE) ? CMT_FALSE : CMT_TRUE; + } + + return (flags & CMT_FILTER_EXCLUDE) ? CMT_TRUE : CMT_FALSE; + } + + /* compare fqname for substring */ + if (flags & CMT_FILTER_SUBSTRING) { + if (strstr(src->fqname, fqname) != NULL) { + return (flags & CMT_FILTER_EXCLUDE) ? CMT_FALSE : CMT_TRUE; + } + + return (flags & CMT_FILTER_EXCLUDE) ? CMT_TRUE : CMT_FALSE; + } + + /* Compare with an external context (e.g. Onigmo). */ + if (compare_ctx != NULL && compare != NULL) { + return compare(compare_ctx, src->fqname, fqname_size); + } + + return CMT_FALSE; +} + +static int filter_context_fqname(struct cmt *dst, struct cmt *src, + const char *fqname, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags) +{ + int ret; + struct cfl_list *head; + struct cmt_counter *counter; + struct cmt_gauge *gauge; + struct cmt_untyped *untyped; + struct cmt_histogram *histogram; + struct cmt_summary *summary; + + /* Counters */ + cfl_list_foreach(head, &src->counters) { + counter = cfl_list_entry(head, struct cmt_counter, _head); + + if (compare_fqname(counter->map->opts, fqname, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_counter(dst, counter); + if (ret == -1) { + return -1; + } + } + + /* Gauges */ + cfl_list_foreach(head, &src->gauges) { + gauge = cfl_list_entry(head, struct cmt_gauge, _head); + if (compare_fqname(gauge->map->opts, fqname, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_gauge(dst, gauge); + if (ret == -1) { + return -1; + } + } + + /* Untyped */ + cfl_list_foreach(head, &src->untypeds) { + untyped = cfl_list_entry(head, struct cmt_untyped, _head); + if (compare_fqname(untyped->map->opts, fqname, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_untyped(dst, untyped); + if (ret == -1) { + return -1; + } + } + + /* Histogram */ + cfl_list_foreach(head, &src->histograms) { + histogram = cfl_list_entry(head, struct cmt_histogram, _head); + if (compare_fqname(histogram->map->opts, fqname, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_histogram(dst, histogram); + if (ret == -1) { + return -1; + } + } + + /* Summary */ + cfl_list_foreach(head, &src->summaries) { + summary = cfl_list_entry(head, struct cmt_summary, _head); + if (compare_fqname(summary->map->opts, fqname, compare_ctx, compare, flags) == CMT_FALSE) { + continue; + } + + ret = cmt_cat_summary(dst, summary); + if (ret == -1) { + return -1; + } + } + + return CMT_FILTER_SUCCESS; +} + +int cmt_filter(struct cmt *dst, struct cmt *src, + const char *fqname, const char *label_key, + void *compare_ctx, int (*compare)(void *compare_ctx, const char *str, size_t slen), + int flags) +{ + int ret = CMT_FILTER_SUCCESS; + + if (!dst) { + return CMT_FILTER_INVALID_ARGUMENT; + } + + if (!src) { + return CMT_FILTER_INVALID_ARGUMENT; + } + + if ((flags & CMT_FILTER_PREFIX) && + (flags & CMT_FILTER_SUBSTRING)) { + return CMT_FILTER_INVALID_FLAGS; + } + + if (fqname != NULL || (compare_ctx != NULL && compare != NULL)) { + ret = filter_context_fqname(dst, src, fqname, compare_ctx, compare, flags); + } + + if (ret != CMT_FILTER_SUCCESS) { + return CMT_FILTER_FAILED_OPERATION; + } + + /* On callback mode, labels are not searched by default. */ + if (label_key != NULL || + (compare_ctx != NULL && compare != NULL && flags & CMT_FILTER_REGEX_SEARCH_LABELS)) { + ret = filter_context_label_key(dst, src, label_key, compare_ctx, compare, flags); + } + + if (ret != CMT_FILTER_SUCCESS) { + return CMT_FILTER_FAILED_OPERATION; + } + + return ret; +} diff --git a/lib/cmetrics/tests/CMakeLists.txt b/lib/cmetrics/tests/CMakeLists.txt index d7ff19b3569..669b30baf0a 100644 --- a/lib/cmetrics/tests/CMakeLists.txt +++ b/lib/cmetrics/tests/CMakeLists.txt @@ -11,6 +11,7 @@ set(UNIT_TESTS_FILES cat.c issues.c null_label.c + filter.c ) if (CMT_BUILD_PROMETHEUS_DECODER) diff --git a/lib/cmetrics/tests/encoding.c b/lib/cmetrics/tests/encoding.c index 77a58027384..9d4478e4016 100644 --- a/lib/cmetrics/tests/encoding.c +++ b/lib/cmetrics/tests/encoding.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "cmt_tests.h" @@ -65,19 +66,17 @@ static struct cmt *generate_simple_encoder_test_data() return cmt; } -static struct cmt *generate_encoder_test_data() +static struct cmt *generate_encoder_test_data_with_timestamp(uint64_t ts) { double quantiles[5]; struct cmt_histogram_buckets *buckets; double val; struct cmt *cmt; - uint64_t ts; struct cmt_gauge *g1; struct cmt_counter *c1; struct cmt_summary *s1; struct cmt_histogram *h1; - ts = 0; cmt = cmt_create(); c1 = cmt_counter_create(cmt, "kubernetes", "network", "load_counter", "Network load counter", @@ -157,6 +156,11 @@ static struct cmt *generate_encoder_test_data() return cmt; } +static struct cmt *generate_encoder_test_data() +{ + return generate_encoder_test_data_with_timestamp(0); +} + /* * perform the following data encoding and compare msgpack buffsers * @@ -510,10 +514,13 @@ void test_prometheus_remote_write() struct cmt *cmt; cfl_sds_t payload; FILE *sample_file; + uint64_t ts; + + ts = cfl_time_now(); cmt_initialize(); - cmt = generate_encoder_test_data(); + cmt = generate_encoder_test_data_with_timestamp(ts); payload = cmt_encode_prometheus_remote_write_create(cmt); TEST_CHECK(NULL != payload); @@ -543,6 +550,34 @@ curl -v 'http://localhost:9090/receive' -H 'Content-Type: application/x-protobuf cmt_destroy(cmt); } +void test_prometheus_remote_write_with_outdated_timestamps() +{ + struct cmt *cmt; + cfl_sds_t payload; + uint64_t ts; + + ts = cfl_time_now() - CMT_ENCODE_PROMETHEUS_REMOTE_WRITE_CUTOFF_THRESHOLD * 1.5; + + cmt_initialize(); + + cmt = generate_encoder_test_data_with_timestamp(ts); + + payload = cmt_encode_prometheus_remote_write_create(cmt); + TEST_CHECK(NULL != payload); + + if (payload == NULL) { + cmt_destroy(cmt); + + return; + } + + TEST_CHECK(0 == cfl_sds_len(payload)); + + cmt_encode_prometheus_remote_write_destroy(payload); + + cmt_destroy(cmt); +} + void test_opentelemetry() { cfl_sds_t payload; @@ -579,6 +614,46 @@ curl -v 'http://localhost:9090/v1/metrics' -H 'Content-Type: application/x-proto cmt_destroy(cmt); } +void test_cloudwatch_emf() +{ + int ret; + struct cmt *cmt; + FILE *sample_file; + char *mp_buf = NULL; + size_t mp_size = 0; + int wrap_array = CMT_TRUE; + + cmt_initialize(); + + cmt = generate_encoder_test_data(); + + cmt_label_add(cmt, "format", "EMF"); + cmt_label_add(cmt, "dev", "CMetrics Authors"); + + ret = cmt_encode_cloudwatch_emf_create(cmt, &mp_buf, &mp_size, wrap_array); + TEST_CHECK(0 == ret); + + if (ret != 0) { + cmt_destroy(cmt); + + return; + } + + printf("\n\nDumping cloudwatch EMF payload to cloudwatch_emf_payload.bin, in order to test it \ +we need to encode it as JSON and to send AWS Cloudwatch with out_cloudwatch plugin on \ +fluent-bit\n\n"); + + sample_file = fopen("cloudwatch_emf_payload.bin", "wb+"); + + fwrite(mp_buf, 1, mp_size, sample_file); + + fclose(sample_file); + + cmt_encode_cloudwatch_emf_destroy(mp_buf); + + cmt_destroy(cmt); +} + void test_prometheus() { uint64_t ts; @@ -1040,11 +1115,13 @@ TEST_LIST = { {"cmt_msgpack_cleanup_on_error", test_cmt_to_msgpack_cleanup_on_error}, {"cmt_msgpack_partial_processing", test_cmt_msgpack_partial_processing}, {"prometheus_remote_write", test_prometheus_remote_write}, + {"prometheus_remote_write_old_cmt",test_prometheus_remote_write_with_outdated_timestamps}, {"cmt_msgpack_stability", test_cmt_to_msgpack_stability}, {"cmt_msgpack_integrity", test_cmt_to_msgpack_integrity}, {"cmt_msgpack_labels", test_cmt_to_msgpack_labels}, {"cmt_msgpack", test_cmt_to_msgpack}, {"opentelemetry", test_opentelemetry}, + {"cloudwatch_emf", test_cloudwatch_emf}, {"prometheus", test_prometheus}, {"text", test_text}, {"influx", test_influx}, diff --git a/lib/cmetrics/tests/filter.c b/lib/cmetrics/tests/filter.c new file mode 100644 index 00000000000..aa727884add --- /dev/null +++ b/lib/cmetrics/tests/filter.c @@ -0,0 +1,314 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CMetrics + * ======== + * Copyright 2021-2024 The CMetrics Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmt_tests.h" + +/* values to observe in a histogram */ +double hist_observe_values[10] = { + 0.0 , 1.02, 2.04, 3.06, + 4.08, 5.10, 6.12, 7.14, + 8.16, 9.18 + }; + +/* + * histogram bucket values: the values computed in the buckets, + * all of them are uint64_t. + * + * Note that on all examples we use the default buckets values, created manually + * and through the API: + * + * - 11 bucket values + * - 1 +Inf bucket value + */ +uint64_t hist_buckets_values[12] = {1, 1, 1, 1, 1, 1, 1, 1, + 3, 5, 10, 10}; +/* histogram _count value */ +uint64_t hist_count = 10; + +/* histogram _sum value */ +double hist_sum = 45.9; + +struct cmt *generate_filter_test_data() +{ + int i; + struct cmt *cmt; + uint64_t val; + uint64_t ts; + double sum; + uint64_t count; + double q[6]; + double r[6]; + + struct cmt_counter *c; + struct cmt_gauge *g; + struct cmt_untyped *u; + struct cmt_histogram *h; + struct cmt_histogram_buckets *buckets; + struct cmt_summary *s; + + /* cmetrics */ + cmt = cmt_create(); + TEST_CHECK(cmt != NULL); + + c = cmt_counter_create(cmt, "cmetrics", "test", "cat_counter", "first counter", + 2, (char *[]) {"label1", "label2"}); + TEST_CHECK(c != NULL); + + g = cmt_gauge_create(cmt, "cmetrics", "test", "cat_gauge", "first gauge", + 2, (char *[]) {"label3", "label4"}); + TEST_CHECK(g != NULL); + + u = cmt_untyped_create(cmt, "cmetrics", "test", "cat_untyped", "first untyped", + 2, (char *[]) {"hostname", "net"}); + TEST_CHECK(u != NULL); + + + ts = cfl_time_now(); + cmt_counter_set(c, ts, 1.1, 2, (char *[]) {"aaa", "bbb"}); + + ts = cfl_time_now(); + cmt_gauge_set(g, ts, 1.2, 2, (char *[]) {"yyy", "xxx"}); + + ts = cfl_time_now(); + cmt_untyped_set(u, ts, 1.3, 2, (char *[]) {"localhost", "eth0"}); + + c = cmt_counter_create(cmt, "cmetrics", "test", "cat_counter", "second counter", + 2, (char *[]) {"label1", "label2"}); + TEST_CHECK(c != NULL); + + g = cmt_gauge_create(cmt, "cmetrics", "test", "cat_gauge", "second gauge", + 2, (char *[]) {"label3", "label4"}); + TEST_CHECK(g != NULL); + + ts = cfl_time_now(); + cmt_counter_set(c, ts, 2.1, 2, (char *[]) {"ccc", "ddd"}); + + /* no labels */ + cmt_counter_set(c, ts, 5, 0, NULL); + + ts = cfl_time_now(); + cmt_gauge_add(g, ts, 10, 2, (char *[]) {"tyu", "iop"}); + + /* Create buckets */ + buckets = cmt_histogram_buckets_create(11, + 0.005, 0.01, 0.025, 0.05, + 0.1, 0.25, 0.5, 1.0, 2.5, + 5.0, 10.0); + TEST_CHECK(buckets != NULL); + + /* Create a histogram metric type */ + h = cmt_histogram_create(cmt, + "k8s", "network", "load", "Network load", + buckets, + 1, (char *[]) {"my_label"}); + TEST_CHECK(h != NULL); + + ts = cfl_time_now(); + for (i = 0; i < sizeof(hist_observe_values)/(sizeof(double)); i++) { + val = hist_observe_values[i]; + cmt_histogram_observe(h, ts, val, 1, (char *[]) {"my_label"}); + } + + ts = cfl_time_now(); + + /* set quantiles */ + q[0] = 0.1; + q[1] = 0.2; + q[2] = 0.3; + q[3] = 0.4; + q[4] = 0.5; + q[5] = 1.0; + + r[0] = 1; + r[1] = 2; + r[2] = 3; + r[3] = 4; + r[4] = 5; + r[5] = 6; + + /* Create a gauge metric type */ + s = cmt_summary_create(cmt, + "spring", "kafka_listener", "seconds", "Kafka Listener Timer", + 6, q, + 3, (char *[]) {"exception", "name", "result"}); + TEST_CHECK(s != NULL); + + /* no quantiles, labels */ + sum = 0.0; + count = 1; + + cmt_summary_set_default(s, ts, NULL, sum, count, + 3, (char *[]) {"ListenerExecutionFailedException", + "org.springframework.kafka.KafkaListenerEndpointContainer#0-0", + "failure"}); + + /* no quantiles, labels */ + sum = 0.1; + count = 2; + cmt_summary_set_default(s, ts, NULL, sum, count, + 3, (char *[]) {"none", + "org.springframework.kafka.KafkaListenerEndpointContainer#0-0", + "success"}); + + /* quantiles, labels */ + sum = 0.2; + count = 3; + cmt_summary_set_default(s, ts, r, sum, count, + 3, (char *[]) {"extra test", + "org.springframework.kafka.KafkaListenerEndpointContainer#0-0", + "success"}); + + return cmt; +} + +void test_filter() +{ + int ret; + cfl_sds_t text; + struct cmt *cmt; + struct cmt *cmt2; + struct cmt *cmt3; + struct cmt *cmt4; + struct cmt *cmt5; + struct cmt *cmt6; + int flags = 0; + char *fqname; + char *label_key; + + cmt = generate_filter_test_data(); + + text = cmt_encode_text_create(cmt); + printf("[Not filtered] ====>\n%s\n", text); + + cmt_encode_text_destroy(text); + + cmt2 = cmt_create(); + TEST_CHECK(cmt2 != NULL); + + /* filter with fqname (SUBSTRING) */ + fqname = "counter"; + flags |= CMT_FILTER_SUBSTRING; + ret = cmt_filter(cmt2, cmt, fqname, NULL, + NULL, NULL, flags); + TEST_CHECK(ret == 0); + /* check output (fqname) */ + text = cmt_encode_text_create(cmt2); + printf("[substring matched with \"%s\" in fqname] ====>\n%s\n", fqname, text); + + cmt_encode_text_destroy(text); + + /* reset flags */ + flags = 0; + + cmt3 = cmt_create(); + TEST_CHECK(cmt3 != NULL); + + /* filter with fqname (INCLUDE & PREFIX) */ + fqname = "spring"; + flags |= CMT_FILTER_PREFIX; + ret = cmt_filter(cmt3, cmt, fqname, NULL, + NULL, NULL, flags); + TEST_CHECK(ret == 0); + + /* check output (fqname) */ + text = cmt_encode_text_create(cmt3); + printf("[prefix matched with \"%s\" in fqname] ====>\n%s\n", fqname, text); + + cmt_encode_text_destroy(text); + + /* reset flags */ + flags = 0; + + cmt4 = cmt_create(); + TEST_CHECK(cmt4 != NULL); + + /* filter with fqname (INCLUDE & SUBSTRING) */ + fqname = "load"; + flags |= CMT_FILTER_SUBSTRING; + ret = cmt_filter(cmt4, cmt, fqname, NULL, + NULL, NULL, flags); + TEST_CHECK(ret == 0); + + /* check output (fqname) */ + text = cmt_encode_text_create(cmt4); + printf("[substring matched with \"%s\" in fqname] ====>\n%s\n", fqname, text); + + cmt_encode_text_destroy(text); + + cmt5 = cmt_create(); + TEST_CHECK(cmt5 != NULL); + + /* reset flags */ + flags = 0; + + /* filter with label_key (EXCLUDE & PREFIX) */ + label_key = "host"; + flags |= CMT_FILTER_PREFIX; + ret = cmt_filter(cmt5, cmt, NULL, label_key, + NULL, NULL, flags); + TEST_CHECK(ret == 0); + + /* check output (label_key) */ + text = cmt_encode_text_create(cmt5); + printf("[prefix matched with \"%s\" in label key] ====>\n%s\n", label_key, text); + + cmt_encode_text_destroy(text); + + cmt6 = cmt_create(); + TEST_CHECK(cmt6 != NULL); + + /* reset flags */ + flags = 0; + + /* filter with label_key (EXCLUDE & SUBSTRING) */ + label_key = "label"; + flags |= CMT_FILTER_EXCLUDE; + flags |= CMT_FILTER_SUBSTRING; + ret = cmt_filter(cmt6, cmt, NULL, label_key, + NULL, NULL, flags); + TEST_CHECK(ret == 0); + + /* check output (label_key) */ + text = cmt_encode_text_create(cmt6); + printf("[exclude with \"%s\" in label key] ====>\n%s\n", label_key, text); + + cmt_encode_text_destroy(text); + + /* destroy contexts */ + cmt_destroy(cmt); + cmt_destroy(cmt2); + cmt_destroy(cmt3); + cmt_destroy(cmt4); + cmt_destroy(cmt5); + cmt_destroy(cmt6); +} + +TEST_LIST = { + {"filter", test_filter}, + { 0 } +};