|
@@ -6,55 +6,23 @@ static void facets_row_free(FACETS *facets __maybe_unused, FACET_ROW *row);
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
-time_t calculate_bar_width(time_t before, time_t after) {
|
|
|
- // Array of valid durations in seconds
|
|
|
- static time_t valid_durations[] = {
|
|
|
- 1,
|
|
|
- 15,
|
|
|
- 30,
|
|
|
- 1 * 60, 2 * 60, 3 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, // minutes
|
|
|
- 1 * 3600, 2 * 3600, 6 * 3600, 8 * 3600, 12 * 3600, // hours
|
|
|
- 1 * 86400, 2 * 86400, 3 * 86400, 5 * 86400, 7 * 86400, 14 * 86400, // days
|
|
|
- 1 * (30*86400) // months
|
|
|
- };
|
|
|
- static int array_size = sizeof(valid_durations) / sizeof(valid_durations[0]);
|
|
|
-
|
|
|
- time_t duration = before - after;
|
|
|
- time_t bar_width = 1;
|
|
|
-
|
|
|
- for (int i = array_size - 1; i >= 0; --i) {
|
|
|
- if (duration / valid_durations[i] >= HISTOGRAM_COLUMNS) {
|
|
|
- bar_width = valid_durations[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return bar_width;
|
|
|
-}
|
|
|
-
|
|
|
-// ----------------------------------------------------------------------------
|
|
|
-
|
|
|
-static inline void uint32_to_char(uint32_t num, char *out) {
|
|
|
- static char id_encoding_characters[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdefghijklmnopqrstuvwxyz_0123456789";
|
|
|
+static inline void uint64_to_char(uint64_t num, char *out) {
|
|
|
+ static const char id_encoding_characters[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdefghijklmnopqrstuvwxyz_0123456789";
|
|
|
|
|
|
int i;
|
|
|
- for(i = 5; i >= 0; --i) {
|
|
|
+ for(i = 10; i >= 0; --i) {
|
|
|
out[i] = id_encoding_characters[num & 63];
|
|
|
num >>= 6;
|
|
|
}
|
|
|
- out[6] = '\0';
|
|
|
}
|
|
|
|
|
|
-inline void facets_string_hash(const char *src, char *out) {
|
|
|
- uint32_t hash1 = fnv1a_hash32(src);
|
|
|
- uint32_t hash2 = djb2_hash32(src);
|
|
|
- uint32_t hash3 = larson_hash32(src);
|
|
|
+inline void facets_string_hash(const char *src, size_t len, char *out) {
|
|
|
+ XXH128_hash_t hash = XXH128(src, len, 0);
|
|
|
|
|
|
- uint32_to_char(hash1, out);
|
|
|
- uint32_to_char(hash2, &out[6]);
|
|
|
- uint32_to_char(hash3, &out[12]);
|
|
|
+ uint64_to_char(hash.high64, out);
|
|
|
+ uint64_to_char(hash.low64, &out[11]); // Starts right after the first 64-bit encoded string
|
|
|
|
|
|
- out[18] = '\0';
|
|
|
+ out[FACET_STRING_HASH_SIZE - 1] = '\0';
|
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
@@ -66,6 +34,9 @@ typedef struct facet_value {
|
|
|
|
|
|
uint32_t rows_matching_facet_value;
|
|
|
uint32_t final_facet_value_counter;
|
|
|
+
|
|
|
+ uint32_t *histogram;
|
|
|
+ uint32_t min, max, sum;
|
|
|
} FACET_VALUE;
|
|
|
|
|
|
struct facet_key {
|
|
@@ -124,6 +95,15 @@ struct facets {
|
|
|
uint32_t max_items_to_return;
|
|
|
uint32_t order;
|
|
|
|
|
|
+ struct {
|
|
|
+ char *chart;
|
|
|
+ bool enabled;
|
|
|
+ uint32_t slots;
|
|
|
+ usec_t slot_width;
|
|
|
+ usec_t after_ut;
|
|
|
+ usec_t before_ut;
|
|
|
+ } histogram;
|
|
|
+
|
|
|
struct {
|
|
|
FACET_ROW *last_added;
|
|
|
|
|
@@ -143,6 +123,515 @@ struct facets {
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
+static usec_t calculate_histogram_bar_width(usec_t after_ut, usec_t before_ut) {
|
|
|
+ // Array of valid durations in seconds
|
|
|
+ static time_t valid_durations[] = {
|
|
|
+ 1,
|
|
|
+ 15,
|
|
|
+ 30,
|
|
|
+ 1 * 60, 2 * 60, 3 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, // minutes
|
|
|
+ 1 * 3600, 2 * 3600, 6 * 3600, 8 * 3600, 12 * 3600, // hours
|
|
|
+ 1 * 86400, 2 * 86400, 3 * 86400, 5 * 86400, 7 * 86400, 14 * 86400, // days
|
|
|
+ 1 * (30*86400) // months
|
|
|
+ };
|
|
|
+ static int array_size = sizeof(valid_durations) / sizeof(valid_durations[0]);
|
|
|
+
|
|
|
+ usec_t duration = before_ut - after_ut;
|
|
|
+ usec_t bar_width = 1 * 60;
|
|
|
+
|
|
|
+ for (int i = array_size - 1; i >= 0; --i) {
|
|
|
+ if (duration / (valid_durations[i] * 60) >= HISTOGRAM_COLUMNS) {
|
|
|
+ bar_width = valid_durations[i] * 60;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return bar_width;
|
|
|
+}
|
|
|
+
|
|
|
+static inline usec_t facets_histogram_slot_baseline_ut(FACETS *facets, usec_t ut) {
|
|
|
+ usec_t delta = ut % facets->histogram.slot_width;
|
|
|
+ return ut - delta;
|
|
|
+}
|
|
|
+
|
|
|
+void facets_set_histogram(FACETS *facets, const char *chart, usec_t after_ut, usec_t before_ut) {
|
|
|
+ facets->histogram.enabled = true;
|
|
|
+ facets->histogram.chart = chart ? strdupz(chart) : NULL;
|
|
|
+ facets->histogram.slot_width = calculate_histogram_bar_width(after_ut, before_ut);
|
|
|
+ facets->histogram.after_ut = facets_histogram_slot_baseline_ut(facets, after_ut);
|
|
|
+ facets->histogram.before_ut = facets_histogram_slot_baseline_ut(facets, before_ut) + facets->histogram.slot_width;
|
|
|
+ facets->histogram.slots = (facets->histogram.before_ut - facets->histogram.after_ut) / facets->histogram.slot_width + 1;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_update_value(FACETS *facets, FACET_KEY *k, FACET_VALUE *v, usec_t usec) {
|
|
|
+ if(!facets->histogram.enabled)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ v->histogram = callocz(facets->histogram.slots, sizeof(*v->histogram));
|
|
|
+
|
|
|
+ usec_t base_ut = facets_histogram_slot_baseline_ut(facets, usec);
|
|
|
+
|
|
|
+ if(base_ut < facets->histogram.after_ut)
|
|
|
+ base_ut = facets->histogram.after_ut;
|
|
|
+
|
|
|
+ if(base_ut > facets->histogram.before_ut)
|
|
|
+ base_ut = facets->histogram.before_ut;
|
|
|
+
|
|
|
+ uint32_t slot = (base_ut - facets->histogram.after_ut) / facets->histogram.slot_width;
|
|
|
+
|
|
|
+ if(unlikely(slot >= facets->histogram.slots))
|
|
|
+ slot = facets->histogram.slots - 1;
|
|
|
+
|
|
|
+ v->histogram[slot]++;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_names(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_string(wb, v->name);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_units(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_string(wb, "events");
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_min(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_uint64(wb, v->min);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_max(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_uint64(wb, v->max);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_avg(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_double(wb, (double)v->sum / (double)facets->histogram.slots);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_arp(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_uint64(wb, 0);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static inline void facets_histogram_value_con(BUFFER *wb, FACETS *facets __maybe_unused, FACET_KEY *k, const char *key, uint32_t sum) {
|
|
|
+ buffer_json_member_add_array(wb, key);
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_double(wb, (double)v->sum * 100.0 / (double)sum);
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // key
|
|
|
+}
|
|
|
+
|
|
|
+static void facets_histogram_generate(FACETS *facets, FACET_KEY *k, BUFFER *wb) {
|
|
|
+ size_t dimensions = 0;
|
|
|
+ uint32_t min = UINT32_MAX, max = 0, sum = 0, count = 0;
|
|
|
+
|
|
|
+ {
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v){
|
|
|
+ if (unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dimensions++;
|
|
|
+
|
|
|
+ v->min = UINT32_MAX;
|
|
|
+ v->max = 0;
|
|
|
+ v->sum = 0;
|
|
|
+
|
|
|
+ for(uint32_t i = 0; i < facets->histogram.slots ;i++) {
|
|
|
+ uint32_t n = v->histogram[i];
|
|
|
+
|
|
|
+ if(n < min)
|
|
|
+ min = n;
|
|
|
+
|
|
|
+ if(n > max)
|
|
|
+ max = n;
|
|
|
+
|
|
|
+ sum += n;
|
|
|
+ count++;
|
|
|
+
|
|
|
+ if(n < v->min)
|
|
|
+ v->min = n;
|
|
|
+
|
|
|
+ if(n > v->max)
|
|
|
+ v->max = n;
|
|
|
+
|
|
|
+ v->sum += n;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!dimensions)
|
|
|
+ return;
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "summary");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_array(wb, "nodes");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_object(wb); // node
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "mg", "default");
|
|
|
+ buffer_json_member_add_string(wb, "nm", "facets.histogram");
|
|
|
+ buffer_json_member_add_uint64(wb, "ni", 0);
|
|
|
+ buffer_json_member_add_object(wb, "st");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "ai", 0);
|
|
|
+ buffer_json_member_add_uint64(wb, "code", 200);
|
|
|
+ buffer_json_member_add_string(wb, "msg", "");
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // st
|
|
|
+ buffer_json_member_add_object(wb, "is");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", 1);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // is
|
|
|
+ buffer_json_member_add_object(wb, "ds");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", dimensions);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", dimensions);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // ds
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "min", min);
|
|
|
+ buffer_json_member_add_uint64(wb, "max", max);
|
|
|
+ buffer_json_member_add_double(wb, "avg", (double)sum / (double)count);
|
|
|
+ buffer_json_member_add_double(wb, "con", 100.0);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // node
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // nodes
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "contexts");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_object(wb); // context
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "id", "facets.histogram");
|
|
|
+ buffer_json_member_add_object(wb, "is");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", 1);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // is
|
|
|
+ buffer_json_member_add_object(wb, "ds");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", dimensions);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", dimensions);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // ds
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "min", min);
|
|
|
+ buffer_json_member_add_uint64(wb, "max", max);
|
|
|
+ buffer_json_member_add_double(wb, "avg", (double)sum / (double)count);
|
|
|
+ buffer_json_member_add_double(wb, "con", 100.0);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // context
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // contexts
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "instances");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_object(wb); // instance
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "id", "facets.histogram");
|
|
|
+ buffer_json_member_add_uint64(wb, "ni", 0);
|
|
|
+ buffer_json_member_add_object(wb, "ds");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", dimensions);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", dimensions);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // ds
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "min", min);
|
|
|
+ buffer_json_member_add_uint64(wb, "max", max);
|
|
|
+ buffer_json_member_add_double(wb, "avg", (double)sum / (double)count);
|
|
|
+ buffer_json_member_add_double(wb, "con", 100.0);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // instance
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // instances
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "dimensions");
|
|
|
+ {
|
|
|
+ size_t pri = 0;
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_object(wb); // dimension
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "id", v->name);
|
|
|
+ buffer_json_member_add_object(wb, "ds");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", 1);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // ds
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "min", v->min);
|
|
|
+ buffer_json_member_add_uint64(wb, "max", v->max);
|
|
|
+ buffer_json_member_add_double(wb, "avg", (double)v->sum / (double)facets->histogram.slots);
|
|
|
+ buffer_json_member_add_double(wb, "con", (double)v->sum * 100.0 / (double)sum);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ buffer_json_member_add_uint64(wb, "pri", pri++);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // dimension
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // dimensions
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "labels");
|
|
|
+ buffer_json_array_close(wb); // labels
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "alerts");
|
|
|
+ buffer_json_array_close(wb); // alerts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // summary
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "totals");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_object(wb, "nodes");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", 1);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // nodes;
|
|
|
+ buffer_json_member_add_object(wb, "contexts");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", 1);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // contexts;
|
|
|
+ buffer_json_member_add_object(wb, "dimensions");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "sl", dimensions);
|
|
|
+ buffer_json_member_add_uint64(wb, "qr", dimensions);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // contexts;
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // totals
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "result");
|
|
|
+ {
|
|
|
+ facets_histogram_value_names(wb, facets, k, "labels");
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "point");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "value", 0);
|
|
|
+ buffer_json_member_add_uint64(wb, "arp", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "pa", 2);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // point
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "data");
|
|
|
+ {
|
|
|
+ usec_t t = facets->histogram.after_ut;
|
|
|
+ for(uint32_t i = 0; i < facets->histogram.slots ;i++) {
|
|
|
+ buffer_json_add_array_item_array(wb); // row
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_time_ms(wb, t / USEC_PER_SEC);
|
|
|
+
|
|
|
+ FACET_VALUE *v;
|
|
|
+ dfe_start_read(k->values, v) {
|
|
|
+ if(unlikely(!v->histogram))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_array(wb); // point
|
|
|
+
|
|
|
+ buffer_json_add_array_item_uint64(wb, v->histogram[i]);
|
|
|
+ buffer_json_add_array_item_uint64(wb, 0);
|
|
|
+ buffer_json_add_array_item_uint64(wb, 1);
|
|
|
+
|
|
|
+ buffer_json_array_close(wb); // point
|
|
|
+ }
|
|
|
+ dfe_done(v);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // row
|
|
|
+
|
|
|
+ t += facets->histogram.slot_width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); //data
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // result
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "db");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "tiers", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "update_every", 1);
|
|
|
+ buffer_json_member_add_time_t(wb, "first_entry", facets->histogram.after_ut / USEC_PER_SEC);
|
|
|
+ buffer_json_member_add_time_t(wb, "last_entry", facets->histogram.before_ut / USEC_PER_SEC);
|
|
|
+ buffer_json_member_add_string(wb, "units", "events");
|
|
|
+ buffer_json_member_add_object(wb, "dimensions");
|
|
|
+ {
|
|
|
+ facets_histogram_value_names(wb, facets, k, "ids");
|
|
|
+ facets_histogram_value_units(wb, facets, k, "units");
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ facets_histogram_value_min(wb, facets, k, "min");
|
|
|
+ facets_histogram_value_max(wb, facets, k, "max");
|
|
|
+ facets_histogram_value_avg(wb, facets, k, "avg");
|
|
|
+ facets_histogram_value_arp(wb, facets, k, "arp");
|
|
|
+ facets_histogram_value_con(wb, facets, k, "con", sum);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // dimensions
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "per_tier");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_object(wb); // tier0
|
|
|
+ {
|
|
|
+ buffer_json_member_add_uint64(wb, "tier", 0);
|
|
|
+ buffer_json_member_add_uint64(wb, "queries", 1);
|
|
|
+ buffer_json_member_add_uint64(wb, "points", count);
|
|
|
+ buffer_json_member_add_time_t(wb, "update_every", 1);
|
|
|
+ buffer_json_member_add_time_t(wb, "first_entry", facets->histogram.after_ut / USEC_PER_SEC);
|
|
|
+ buffer_json_member_add_time_t(wb, "last_entry", facets->histogram.before_ut / USEC_PER_SEC);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // tier0
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // per_tier
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // db
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "view");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "title", "Events Distribution");
|
|
|
+ buffer_json_member_add_time_t(wb, "update_every", 1);
|
|
|
+ buffer_json_member_add_time_t(wb, "after", facets->histogram.after_ut / USEC_PER_SEC);
|
|
|
+ buffer_json_member_add_time_t(wb, "before", facets->histogram.before_ut / USEC_PER_SEC);
|
|
|
+ buffer_json_member_add_string(wb, "units", "events");
|
|
|
+ buffer_json_member_add_string(wb, "chart_type", "stacked");
|
|
|
+ buffer_json_member_add_object(wb, "dimensions");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_array(wb, "grouped_by");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_string(wb, "dimension");
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // grouped_by
|
|
|
+
|
|
|
+ facets_histogram_value_names(wb, facets, k, "ids");
|
|
|
+ facets_histogram_value_names(wb, facets, k, "names");
|
|
|
+ facets_histogram_value_units(wb, facets, k, "units");
|
|
|
+
|
|
|
+ buffer_json_member_add_object(wb, "sts");
|
|
|
+ {
|
|
|
+ facets_histogram_value_min(wb, facets, k, "min");
|
|
|
+ facets_histogram_value_max(wb, facets, k, "max");
|
|
|
+ facets_histogram_value_avg(wb, facets, k, "avg");
|
|
|
+ facets_histogram_value_arp(wb, facets, k, "arp");
|
|
|
+ facets_histogram_value_con(wb, facets, k, "con", sum);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // sts
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // dimensions
|
|
|
+
|
|
|
+ buffer_json_member_add_uint64(wb, "min", min);
|
|
|
+ buffer_json_member_add_uint64(wb, "max", max);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // view
|
|
|
+
|
|
|
+ buffer_json_member_add_array(wb, "agents");
|
|
|
+ {
|
|
|
+ buffer_json_add_array_item_object(wb); // agent
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "mg", "default");
|
|
|
+ buffer_json_member_add_string(wb, "nm", "facets.histogram");
|
|
|
+ buffer_json_member_add_time_t(wb, "now", now_realtime_sec());
|
|
|
+ buffer_json_member_add_uint64(wb, "ai", 0);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // agent
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb); // agents
|
|
|
+}
|
|
|
+
|
|
|
+// ----------------------------------------------------------------------------
|
|
|
+
|
|
|
static inline void facet_value_is_used(FACET_KEY *k, FACET_VALUE *v) {
|
|
|
if(!k->key_found_in_row)
|
|
|
v->rows_matching_facet_value++;
|
|
@@ -218,7 +707,7 @@ static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
|
|
|
if(v->name)
|
|
|
facet_value_is_used(k, v);
|
|
|
|
|
|
- internal_fatal(v->name && strcmp(v->name, nv->name) != 0, "hash conflict: '%s' and '%s' have the same hash '%s'", v->name, nv->name,
|
|
|
+ internal_fatal(v->name && nv->name && strcmp(v->name, nv->name) != 0, "value hash conflict: '%s' and '%s' have the same hash '%s'", v->name, nv->name,
|
|
|
dictionary_acquired_item_name(item));
|
|
|
|
|
|
return false;
|
|
@@ -226,6 +715,7 @@ static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
|
|
|
|
|
|
static void facet_value_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
|
|
|
FACET_VALUE *v = value;
|
|
|
+ freez(v->histogram);
|
|
|
freez((char *)v->name);
|
|
|
}
|
|
|
|
|
@@ -278,6 +768,9 @@ static bool facet_key_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
|
|
|
facet_key_late_init(facets, k);
|
|
|
}
|
|
|
|
|
|
+ internal_fatal(k->name && nk->name && strcmp(k->name, nk->name) != 0, "key hash conflict: '%s' and '%s' have the same hash '%s'", k->name, nk->name,
|
|
|
+ dictionary_acquired_item_name(item));
|
|
|
+
|
|
|
if(k->options & FACET_KEY_OPTION_REORDER) {
|
|
|
k->order = facets->order++;
|
|
|
k->options &= ~FACET_KEY_OPTION_REORDER;
|
|
@@ -337,6 +830,7 @@ void facets_destroy(FACETS *facets) {
|
|
|
facets_row_free(facets, r);
|
|
|
}
|
|
|
|
|
|
+ freez(facets->histogram.chart);
|
|
|
freez(facets);
|
|
|
}
|
|
|
|
|
@@ -354,7 +848,7 @@ inline FACET_KEY *facets_register_key(FACETS *facets, const char *key, FACET_KEY
|
|
|
.default_selected_for_values = true,
|
|
|
};
|
|
|
char hash[FACET_STRING_HASH_SIZE];
|
|
|
- facets_string_hash(tk.name, hash);
|
|
|
+ facets_string_hash(tk.name, strlen(key), hash);
|
|
|
return dictionary_set(facets->keys, hash, &tk, sizeof(tk));
|
|
|
}
|
|
|
|
|
@@ -414,7 +908,7 @@ static inline void facets_check_value(FACETS *facets __maybe_unused, FACET_KEY *
|
|
|
k->transform.cb(facets, k->current_value.b, k->transform.data);
|
|
|
|
|
|
if(!k->current_value.updated) {
|
|
|
- buffer_strcat(k->current_value.b, FACET_VALUE_UNSET);
|
|
|
+ buffer_fast_strcat(k->current_value.b, FACET_VALUE_UNSET, sizeof(FACET_VALUE_UNSET) - 1);
|
|
|
k->current_value.updated = true;
|
|
|
}
|
|
|
|
|
@@ -431,7 +925,7 @@ static inline void facets_check_value(FACETS *facets __maybe_unused, FACET_KEY *
|
|
|
FACET_VALUE tk = {
|
|
|
.name = buffer_tostring(k->current_value.b),
|
|
|
};
|
|
|
- facets_string_hash(tk.name, k->current_value.hash);
|
|
|
+ facets_string_hash(tk.name, buffer_strlen(k->current_value.b), k->current_value.hash);
|
|
|
dictionary_set(k->values, k->current_value.hash, &tk, sizeof(tk));
|
|
|
}
|
|
|
else {
|
|
@@ -652,10 +1146,13 @@ void facets_row_finished(FACETS *facets, usec_t usec) {
|
|
|
if(counted_by == total_keys) {
|
|
|
if(k->values) {
|
|
|
if(!k->current_value.hash[0])
|
|
|
- facets_string_hash(buffer_tostring(k->current_value.b), k->current_value.hash);
|
|
|
+ facets_string_hash(buffer_tostring(k->current_value.b), buffer_strlen(k->current_value.b), k->current_value.hash);
|
|
|
|
|
|
FACET_VALUE *v = dictionary_get(k->values, k->current_value.hash);
|
|
|
v->final_facet_value_counter++;
|
|
|
+
|
|
|
+ if(selected_by == total_keys)
|
|
|
+ facets_histogram_update_value(facets, k, v, usec);
|
|
|
}
|
|
|
|
|
|
found++;
|
|
@@ -826,6 +1323,49 @@ void facets_report(FACETS *facets, BUFFER *wb) {
|
|
|
buffer_json_member_add_array(wb, "default_charts");
|
|
|
buffer_json_array_close(wb);
|
|
|
|
|
|
+ if(facets->histogram.enabled) {
|
|
|
+ const char *first_histogram = NULL;
|
|
|
+ buffer_json_member_add_array(wb, "available_histograms");
|
|
|
+ {
|
|
|
+ FACET_KEY *k;
|
|
|
+ dfe_start_read(facets->keys, k) {
|
|
|
+ if (!k->values)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if(unlikely(!first_histogram))
|
|
|
+ first_histogram = k_dfe.name;
|
|
|
+
|
|
|
+ buffer_json_add_array_item_object(wb);
|
|
|
+ buffer_json_member_add_string(wb, "id", k_dfe.name);
|
|
|
+ buffer_json_member_add_string(wb, "name", k->name);
|
|
|
+ buffer_json_object_close(wb);
|
|
|
+ }
|
|
|
+ dfe_done(k);
|
|
|
+ }
|
|
|
+ buffer_json_array_close(wb);
|
|
|
+
|
|
|
+ {
|
|
|
+ const char *id = facets->histogram.chart;
|
|
|
+ FACET_KEY *k = dictionary_get(facets->keys, id);
|
|
|
+ if(!k || !k->values) {
|
|
|
+ id = first_histogram;
|
|
|
+ k = dictionary_get(facets->keys, id);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(k && k->values) {
|
|
|
+ buffer_json_member_add_object(wb, "histogram");
|
|
|
+ {
|
|
|
+ buffer_json_member_add_string(wb, "id", id);
|
|
|
+ buffer_json_member_add_string(wb, "name", k->name);
|
|
|
+ buffer_json_member_add_object(wb, "chart");
|
|
|
+ facets_histogram_generate(facets, k, wb);
|
|
|
+ buffer_json_object_close(wb);
|
|
|
+ }
|
|
|
+ buffer_json_object_close(wb); // histogram
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
buffer_json_member_add_object(wb, "items");
|
|
|
{
|
|
|
buffer_json_member_add_uint64(wb, "evaluated", facets->operations.evaluated);
|