summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-09-12 18:19:47 +0300
committerGitHub <noreply@github.com>2023-09-12 18:19:47 +0300
commitbb85c57a39674be5fa7d815734aead46fb47a08c (patch)
tree9f99f5a9b990b4d4e3f9fa5adc5877e84d8335b5
parent21539122d57536bd30bf92d9e62fa6cb25142c85 (diff)
facets optimizations (#15940)
-rw-r--r--collectors/systemd-journal.plugin/systemd-journal.c88
-rw-r--r--libnetdata/clocks/clocks.h8
-rw-r--r--libnetdata/facets/facets.c183
-rw-r--r--libnetdata/facets/facets.h18
4 files changed, 220 insertions, 77 deletions
diff --git a/collectors/systemd-journal.plugin/systemd-journal.c b/collectors/systemd-journal.plugin/systemd-journal.c
index 1931998681..d58763c2ee 100644
--- a/collectors/systemd-journal.plugin/systemd-journal.c
+++ b/collectors/systemd-journal.plugin/systemd-journal.c
@@ -90,52 +90,71 @@ int systemd_journal_query(BUFFER *wb, FACETS *facets, usec_t after_ut, usec_t be
uint64_t first_msg_ut = 0;
bool timed_out = false;
size_t row_counter = 0;
- sd_journal_seek_realtime_usec(j, before_ut);
- SD_JOURNAL_FOREACH_BACKWARDS(j) {
- row_counter++;
- uint64_t msg_ut;
- sd_journal_get_realtime_usec(j, &msg_ut);
+ // the entries are not guaranteed to be sorted, so we process up to 100 entries beyond
+ // the end of the query to find possibly useful logs for our time-frame
+ size_t excess_rows_allowed = 100;
- if(unlikely(!first_msg_ut)) {
- if(msg_ut == if_modified_since) {
- sd_journal_close(j);
- return HTTP_RESP_NOT_MODIFIED;
- }
+ if(sd_journal_seek_realtime_usec(j, before_ut) < 0) {
+ netdata_log_error("SYSTEMD-JOURNAL: Failed to seek to %llu", before_ut);
+ if(sd_journal_seek_tail(j) < 0) {
+ netdata_log_error("SYSTEMD-JOURNAL: Failed to seek to journal's tail");
+ goto finalize;
+ }
+ }
+ while (sd_journal_previous(j) > 0) {
+ row_counter++;
+
+ uint64_t msg_ut;
+ sd_journal_get_realtime_usec(j, &msg_ut);
- first_msg_ut = msg_ut;
+ if(unlikely(!first_msg_ut)) {
+ if(msg_ut == if_modified_since) {
+ sd_journal_close(j);
+ return HTTP_RESP_NOT_MODIFIED;
}
- if (msg_ut < after_ut)
+ first_msg_ut = msg_ut;
+ }
+
+ if (msg_ut > before_ut)
+ continue;
+
+ if (msg_ut < after_ut) {
+ if(--excess_rows_allowed == 0)
break;
- const void *data;
- size_t length;
- SD_JOURNAL_FOREACH_DATA(j, data, length) {
- const char *key = data;
- const char *equal = strchr(key, '=');
- if(unlikely(!equal))
- continue;
+ continue;
+ }
- const char *value = ++equal;
- size_t key_length = value - key; // including '\0'
+ const void *data;
+ size_t length;
+ SD_JOURNAL_FOREACH_DATA(j, data, length) {
+ const char *key = data;
+ const char *equal = strchr(key, '=');
+ if(unlikely(!equal))
+ continue;
- char key_copy[key_length];
- memcpy(key_copy, key, key_length - 1);
- key_copy[key_length - 1] = '\0';
+ const char *value = ++equal;
+ size_t key_length = value - key; // including '\0'
- size_t value_length = length - key_length; // without '\0'
- facets_add_key_value_length(facets, key_copy, value, value_length <= FACET_MAX_VALUE_LENGTH ? value_length : FACET_MAX_VALUE_LENGTH);
- }
+ char key_copy[key_length];
+ memcpy(key_copy, key, key_length - 1);
+ key_copy[key_length - 1] = '\0';
- facets_row_finished(facets, msg_ut);
+ size_t value_length = length - key_length; // without '\0'
+ facets_add_key_value_length(facets, key_copy, key_length - 1, value, value_length <= FACET_MAX_VALUE_LENGTH ? value_length : FACET_MAX_VALUE_LENGTH);
+ }
- if((row_counter % 100) == 0 && now_monotonic_usec() > stop_monotonic_ut) {
- timed_out = true;
- break;
- }
+ facets_row_finished(facets, msg_ut);
+
+ if((row_counter % 100) == 0 && now_monotonic_usec() > stop_monotonic_ut) {
+ timed_out = true;
+ break;
}
+ }
+finalize:
sd_journal_close(j);
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
@@ -369,11 +388,11 @@ static void function_systemd_journal(const char *transaction, char *function, ch
// register the fields in the order you want them on the dashboard
facets_register_dynamic_key_name(facets, "ND_JOURNAL_PROCESS",
- FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS,
+ FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_VISIBLE | FACET_KEY_OPTION_FTS,
systemd_journal_dynamic_row_id, NULL);
facets_register_key_name(facets, "MESSAGE",
- FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE |
+ FACET_KEY_OPTION_NEVER_FACET | FACET_KEY_OPTION_MAIN_TEXT | FACET_KEY_OPTION_VISIBLE |
FACET_KEY_OPTION_FTS);
facets_register_key_name_transformation(facets, "PRIORITY", FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_FTS,
@@ -654,6 +673,7 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
if(argc == 2 && strcmp(argv[1], "debug") == 0) {
char buf[] = "systemd-journal after:-864000 before:0 last:500";
+ // char buf[] = "systemd-journal after:1694511062 before:1694514662 anchor:1694514122024403";
function_systemd_journal("123", buf, "", 0, 30);
exit(1);
}
diff --git a/libnetdata/clocks/clocks.h b/libnetdata/clocks/clocks.h
index b050b62548..5b88a45798 100644
--- a/libnetdata/clocks/clocks.h
+++ b/libnetdata/clocks/clocks.h
@@ -16,10 +16,10 @@ struct timespec {
typedef int clockid_t;
#endif
-typedef unsigned long long nsec_t;
-typedef unsigned long long msec_t;
-typedef unsigned long long usec_t;
-typedef long long susec_t;
+typedef uint64_t nsec_t;
+typedef uint64_t msec_t;
+typedef uint64_t usec_t;
+typedef int64_t susec_t;
typedef struct heartbeat {
usec_t realtime;
diff --git a/libnetdata/facets/facets.c b/libnetdata/facets/facets.c
index e9be71417f..7496072c78 100644
--- a/libnetdata/facets/facets.c
+++ b/libnetdata/facets/facets.c
@@ -31,12 +31,20 @@ inline void facets_string_hash(const char *src, size_t len, char *out) {
out[FACET_STRING_HASH_SIZE - 1] = '\0';
}
+static inline void facets_zero_hash(char *out) {
+ for(int i = 0; i < FACET_STRING_HASH_SIZE ;i++)
+ out[i] = '0';
+
+ out[FACET_STRING_HASH_SIZE - 1] = '\0';
+}
+
// ----------------------------------------------------------------------------
typedef struct facet_value {
const char *name;
bool selected;
+ bool empty;
uint32_t rows_matching_facet_value;
uint32_t final_facet_value_counter;
@@ -46,6 +54,8 @@ typedef struct facet_value {
} FACET_VALUE;
struct facet_key {
+ FACETS *facets;
+
const char *name;
DICTIONARY *values;
@@ -61,9 +71,14 @@ struct facet_key {
struct {
char hash[FACET_STRING_HASH_SIZE];
bool updated;
+ bool empty;
BUFFER *b;
} current_value;
+ struct {
+ FACET_VALUE *empty_value;
+ } empty_value;
+
uint32_t order;
struct {
@@ -117,9 +132,6 @@ struct facets {
struct {
FACET_ROW *last_added;
- size_t evaluated;
- size_t matched;
-
size_t first;
size_t forwards;
size_t backwards;
@@ -128,6 +140,32 @@ struct facets {
size_t prepends;
size_t appends;
size_t shifts;
+
+ struct {
+ size_t evaluated;
+ size_t matched;
+ size_t created;
+ size_t reused;
+ } rows;
+
+ struct {
+ size_t registered;
+ size_t unique;
+ } keys;
+
+ struct {
+ size_t registered;
+ size_t transformed;
+ size_t dynamic;
+ size_t empty;
+ size_t indexed;
+ size_t inserts;
+ size_t conflicts;
+ } values;
+
+ struct {
+ size_t searches;
+ } fts;
} operations;
};
@@ -673,12 +711,12 @@ static inline void facet_value_is_used(FACET_KEY *k, FACET_VALUE *v) {
static inline bool facets_key_is_facet(FACETS *facets, FACET_KEY *k) {
bool included = true, excluded = false;
- if(k->options & (FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_NO_FACET)) {
+ if(k->options & (FACET_KEY_OPTION_FACET | FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_NEVER_FACET)) {
if(k->options & FACET_KEY_OPTION_FACET) {
included = true;
excluded = false;
}
- else if(k->options & FACET_KEY_OPTION_NO_FACET) {
+ else if(k->options & (FACET_KEY_OPTION_NO_FACET | FACET_KEY_OPTION_NEVER_FACET)) {
included = false;
excluded = true;
}
@@ -721,6 +759,8 @@ static void facet_value_insert_callback(const DICTIONARY_ITEM *item __maybe_unus
v->name = strdupz(v->name);
facet_value_is_used(k, v);
}
+
+ k->facets->operations.values.inserts++;
}
static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -738,6 +778,8 @@ static bool facet_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_un
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));
+ k->facets->operations.values.conflicts++;
+
return false;
}
@@ -768,6 +810,8 @@ static void facet_key_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
FACET_KEY *k = value;
FACETS *facets = data;
+ k->facets = facets;
+
if(!(k->options & FACET_KEY_OPTION_REORDER))
k->order = facets->order++;
@@ -783,6 +827,9 @@ static void facet_key_insert_callback(const DICTIONARY_ITEM *item __maybe_unused
k->current_value.b = buffer_create(0, NULL);
DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(facets->keys_ll, k, prev, next);
+
+ facets->operations.keys.registered++;
+ facets->operations.keys.unique++;
}
static bool facet_key_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -804,6 +851,8 @@ static bool facet_key_conflict_callback(const DICTIONARY_ITEM *item __maybe_unus
k->options &= ~FACET_KEY_OPTION_REORDER;
}
+ facets->operations.keys.registered++;
+
return false;
}
@@ -870,17 +919,21 @@ void facets_accepted_param(FACETS *facets, const char *param) {
dictionary_set(facets->accepted_params, param, NULL, 0);
}
-inline FACET_KEY *facets_register_key_name(FACETS *facets, const char *key, FACET_KEY_OPTIONS options) {
+static inline FACET_KEY *facets_register_key_name_length(FACETS *facets, const char *key, size_t key_length, FACET_KEY_OPTIONS options) {
FACET_KEY tk = {
.name = key,
.options = options,
.default_selected_for_values = true,
};
char hash[FACET_STRING_HASH_SIZE];
- facets_string_hash(tk.name, strlen(key), hash);
+ facets_string_hash(tk.name, key_length, hash);
return dictionary_set(facets->keys, hash, &tk, sizeof(tk));
}
+inline FACET_KEY *facets_register_key_name(FACETS *facets, const char *key, FACET_KEY_OPTIONS options) {
+ return facets_register_key_name_length(facets, key, strlen(key), options);
+}
+
inline FACET_KEY *facets_register_key_name_transformation(FACETS *facets, const char *key, FACET_KEY_OPTIONS options, facets_key_transformer_t cb, void *data) {
FACET_KEY *k = facets_register_key_name(facets, key, options);
k->transform.cb = cb;
@@ -938,32 +991,60 @@ void facets_register_facet_id_filter(FACETS *facets, const char *key_id, char *v
// ----------------------------------------------------------------------------
static inline void facets_check_value(FACETS *facets __maybe_unused, FACET_KEY *k) {
+ facets->operations.values.registered++;
+
if(!k->current_value.updated)
buffer_flush(k->current_value.b);
- if(k->transform.cb)
+ if(k->transform.cb) {
+ facets->operations.values.transformed++;
k->transform.cb(facets, k->current_value.b, k->transform.data);
+ }
if(!k->current_value.updated) {
buffer_fast_strcat(k->current_value.b, FACET_VALUE_UNSET, sizeof(FACET_VALUE_UNSET) - 1);
k->current_value.updated = true;
+ k->current_value.empty = true;
+ facets->operations.values.empty++;
}
+ else
+ k->current_value.empty = false;
// bool found = false;
// if(strstr(buffer_tostring(k->current_value), "fprintd") != NULL)
// found = true;
- if(facets->query && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
+ if(facets->query && !k->current_value.empty && ((k->options & FACET_KEY_OPTION_FTS) || facets->options & FACETS_OPTION_ALL_KEYS_FTS)) {
+ facets->operations.fts.searches++;
if(simple_pattern_matches(facets->query, buffer_tostring(k->current_value.b)))
facets->keys_matched_by_query++;
}
if(k->values) {
- FACET_VALUE tk = {
- .name = buffer_tostring(k->current_value.b),
- };
- 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));
+ if(k->current_value.empty) {
+ facets_zero_hash(k->current_value.hash);
+
+ FACET_VALUE tk = {
+ .name = FACET_VALUE_UNSET,
+ };
+
+ if(k->empty_value.empty_value) {
+ facet_value_conflict_callback(NULL, k->empty_value.empty_value, &tk, k);
+ }
+ else {
+ FACET_VALUE *tkv = dictionary_set(k->values, k->current_value.hash, &tk, sizeof(tk));
+ tkv->empty = true;
+ k->empty_value.empty_value = tkv;
+ }
+ }
+ else {
+ FACET_VALUE tk = {
+ .name = buffer_tostring(k->current_value.b),
+ };
+ 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));
+ facets->operations.values.indexed++;
+ }
}
else {
k->key_found_in_row++;
@@ -980,8 +1061,8 @@ void facets_add_key_value(FACETS *facets, const char *key, const char *value) {
facets_check_value(facets, k);
}
-void facets_add_key_value_length(FACETS *facets, const char *key, const char *value, size_t value_len) {
- FACET_KEY *k = facets_register_key_name(facets, key, 0);
+void facets_add_key_value_length(FACETS *facets, const char *key, size_t key_len, const char *value, size_t value_len) {
+ FACET_KEY *k = facets_register_key_name_length(facets, key, key_len, 0);
buffer_flush(k->current_value.b);
buffer_strncat(k->current_value.b, value, value_len);
k->current_value.updated = true;
@@ -997,7 +1078,7 @@ static void facet_row_key_value_insert_callback(const DICTIONARY_ITEM *item __ma
FACET_ROW *row = data; (void)row;
rkv->wb = buffer_create(0, NULL);
- buffer_strcat(rkv->wb, rkv->tmp && *rkv->tmp ? rkv->tmp : FACET_VALUE_UNSET);
+ buffer_strcat(rkv->wb, rkv->tmp);
}
static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data) {
@@ -1006,7 +1087,7 @@ static bool facet_row_key_value_conflict_callback(const DICTIONARY_ITEM *item __
FACET_ROW *row = data; (void)row;
buffer_flush(rkv->wb);
- buffer_strcat(rkv->wb, n_rkv->tmp && *n_rkv->tmp ? n_rkv->tmp : FACET_VALUE_UNSET);
+ buffer_strcat(rkv->wb, n_rkv->tmp);
return false;
}
@@ -1029,14 +1110,17 @@ static void facets_row_free(FACETS *facets __maybe_unused, FACET_ROW *row) {
static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into) {
FACET_ROW *row;
- if(into)
+ if(into) {
row = into;
+ facets->operations.rows.reused++;
+ }
else {
row = callocz(1, sizeof(FACET_ROW));
row->dict = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE|DICT_OPTION_FIXED_SIZE, NULL, sizeof(FACET_ROW_KEY_VALUE));
dictionary_register_insert_callback(row->dict, facet_row_key_value_insert_callback, row);
dictionary_register_conflict_callback(row->dict, facet_row_key_value_conflict_callback, row);
dictionary_register_delete_callback(row->dict, facet_row_key_value_delete_callback, row);
+ facets->operations.rows.created++;
}
row->usec = usec;
@@ -1044,9 +1128,9 @@ static FACET_ROW *facets_row_create(FACETS *facets, usec_t usec, FACET_ROW *into
FACET_KEY *k;
dfe_start_read(facets->keys, k) {
FACET_ROW_KEY_VALUE t = {
- .tmp = (k->current_value.updated && buffer_strlen(k->current_value.b)) ?
- buffer_tostring(k->current_value.b) : FACET_VALUE_UNSET,
+ .tmp = (k->current_value.updated) ? buffer_tostring(k->current_value.b) : FACET_VALUE_UNSET,
.wb = NULL,
+ .empty = k->current_value.empty,
};
dictionary_set(row->dict, k->name, &t, sizeof(t));
}
@@ -1086,7 +1170,7 @@ static void facets_row_keep_first_entry(FACETS *facets, usec_t usec) {
}
static void facets_row_keep(FACETS *facets, usec_t usec) {
- facets->operations.matched++;
+ facets->operations.rows.matched++;
if(facets->anchor.key) {
// we have an anchor key
@@ -1095,14 +1179,16 @@ static void facets_row_keep(FACETS *facets, usec_t usec) {
switch (facets->anchor.direction) {
default:
case FACETS_ANCHOR_DIRECTION_BACKWARD:
- if (usec < facets->anchor.key) {
+ // we need to keep only the smaller timestamps
+ if (usec >= facets->anchor.key) {
facets->operations.skips_before++;
return;
}
break;
case FACETS_ANCHOR_DIRECTION_FORWARD:
- if (usec > facets->anchor.key) {
+ // we need to keep only the bigger timestamps
+ if (usec <= facets->anchor.key) {
facets->operations.skips_after++;
return;
}
@@ -1182,6 +1268,7 @@ void facets_rows_begin(FACETS *facets) {
k->key_found_in_row = 0;
k->key_values_selected_in_row = 0;
k->current_value.updated = false;
+ k->current_value.empty = false;
k->current_value.hash[0] = '\0';
}
// dfe_done(k);
@@ -1193,7 +1280,7 @@ void facets_row_finished(FACETS *facets, usec_t usec) {
if(facets->query && facets->keys_filtered_by_query && !facets->keys_matched_by_query)
goto cleanup;
- facets->operations.evaluated++;
+ facets->operations.rows.evaluated++;
uint32_t total_keys = 0;
uint32_t selected_by = 0;
@@ -1375,7 +1462,7 @@ void facets_report(FACETS *facets, BUFFER *wb) {
RRDF_FIELD_SORT_ASCENDING,
NULL,
RRDF_FIELD_SUMMARY_COUNT,
- k->values ? RRDF_FIELD_FILTER_FACET : RRDF_FIELD_FILTER_NONE,
+ (k->options & FACET_KEY_OPTION_NEVER_FACET) ? RRDF_FIELD_FILTER_NONE : RRDF_FIELD_FILTER_FACET,
options,
FACET_VALUE_UNSET);
}
@@ -1399,9 +1486,14 @@ void facets_report(FACETS *facets, BUFFER *wb) {
rkv = dictionary_set(row->dict, k->name, NULL, sizeof(*rkv));
k->dynamic.cb(facets, wb, rkv, row, k->dynamic.data);
+ facets->operations.values.dynamic++;
+ }
+ else {
+ if(!rkv || rkv->empty)
+ buffer_json_add_array_item_string(wb, FACET_VALUE_UNSET);
+ else
+ buffer_json_add_array_item_string(wb, buffer_tostring(rkv->wb));
}
- else
- buffer_json_add_array_item_string(wb, rkv ? buffer_tostring(rkv->wb) : FACET_VALUE_UNSET);
}
dfe_done(k);
buffer_json_array_close(wb); // each row
@@ -1458,8 +1550,8 @@ void facets_report(FACETS *facets, BUFFER *wb) {
buffer_json_member_add_object(wb, "items");
{
- buffer_json_member_add_uint64(wb, "evaluated", facets->operations.evaluated);
- buffer_json_member_add_uint64(wb, "matched", facets->operations.matched);
+ buffer_json_member_add_uint64(wb, "evaluated", facets->operations.rows.evaluated);
+ buffer_json_member_add_uint64(wb, "matched", facets->operations.rows.matched);
buffer_json_member_add_uint64(wb, "returned", facets->items_to_return);
buffer_json_member_add_uint64(wb, "max_to_return", facets->max_items_to_return);
buffer_json_member_add_uint64(wb, "before", facets->operations.skips_before);
@@ -1477,7 +1569,36 @@ void facets_report(FACETS *facets, BUFFER *wb) {
buffer_json_member_add_uint64(wb, "prepends", facets->operations.prepends);
buffer_json_member_add_uint64(wb, "appends", facets->operations.appends);
buffer_json_member_add_uint64(wb, "shifts", facets->operations.shifts);
+ buffer_json_member_add_object(wb, "rows");
+ {
+ buffer_json_member_add_uint64(wb, "created", facets->operations.rows.created);
+ buffer_json_member_add_uint64(wb, "reused", facets->operations.rows.reused);
+ buffer_json_member_add_uint64(wb, "evaluated", facets->operations.rows.evaluated);
+ buffer_json_member_add_uint64(wb, "matched", facets->operations.rows.matched);
+ }
+ buffer_json_object_close(wb); // rows
+ buffer_json_member_add_object(wb, "keys");
+ {
+ buffer_json_member_add_uint64(wb, "registered", facets->operations.keys.registered);
+ buffer_json_member_add_uint64(wb, "unique", facets->operations.keys.unique);
+ }
+ buffer_json_object_close(wb); // keys
+ buffer_json_member_add_object(wb, "values");
+ {
+ buffer_json_member_add_uint64(wb, "registered", facets->operations.values.registered);
+ buffer_json_member_add_uint64(wb, "transformed", facets->operations.values.transformed);
+ buffer_json_member_add_uint64(wb, "dynamic", facets->operations.values.dynamic);
+ buffer_json_member_add_uint64(wb, "empty", facets->operations.values.empty);
+ buffer_json_member_add_uint64(wb, "indexed", facets->operations.values.indexed);
+ buffer_json_member_add_uint64(wb, "inserts", facets->operations.values.inserts);
+ buffer_json_member_add_uint64(wb, "conflicts", facets->operations.values.conflicts);
+ }
+ buffer_json_object_close(wb); // values
+ buffer_json_member_add_object(wb, "fts");
+ {
+ buffer_json_member_add_uint64(wb, "searches", facets->operations.fts.searches);
+ }
+ buffer_json_object_close(wb); // fts
}
buffer_json_object_close(wb); // items
-
}
diff --git a/libnetdata/facets/facets.h b/libnetdata/facets/facets.h
index 961be703b4..c34d92d0ef 100644
--- a/libnetdata/facets/facets.h
+++ b/libnetdata/facets/facets.h
@@ -11,18 +11,20 @@ typedef enum __attribute__((packed)) {
} FACETS_ANCHOR_DIRECTION;
typedef enum __attribute__((packed)) {
- FACET_KEY_OPTION_FACET = (1 << 0), // filterable values
- FACET_KEY_OPTION_NO_FACET = (1 << 1), // non-filterable value
- FACET_KEY_OPTION_STICKY = (1 << 2), // should be sticky in the table
- FACET_KEY_OPTION_VISIBLE = (1 << 3), // should be in the default table
- FACET_KEY_OPTION_FTS = (1 << 4), // the key is filterable by full text search (FTS)
- FACET_KEY_OPTION_MAIN_TEXT = (1 << 5), // full width and wrap
- FACET_KEY_OPTION_REORDER = (1 << 6), // give the key a new order id on first encounter
+ FACET_KEY_OPTION_FACET = (1 << 0), // filterable values
+ FACET_KEY_OPTION_NO_FACET = (1 << 1), // non-filterable value
+ FACET_KEY_OPTION_NEVER_FACET = (1 << 2), // never enable this field as facet
+ FACET_KEY_OPTION_STICKY = (1 << 3), // should be sticky in the table
+ FACET_KEY_OPTION_VISIBLE = (1 << 4), // should be in the default table
+ FACET_KEY_OPTION_FTS = (1 << 5), // the key is filterable by full text search (FTS)
+ FACET_KEY_OPTION_MAIN_TEXT = (1 << 6), // full width and wrap
+ FACET_KEY_OPTION_REORDER = (1 << 7), // give the key a new order id on first encounter
} FACET_KEY_OPTIONS;
typedef struct facet_row_key_value {
const char *tmp;
BUFFER *wb;
+ bool empty;
} FACET_ROW_KEY_VALUE;
typedef struct facet_row {
@@ -64,7 +66,7 @@ void facets_register_facet_id_filter(FACETS *facets, const char *key_id, char *v
void facets_set_histogram(FACETS *facets, const char *chart, usec_t after_ut, usec_t before_ut);
void facets_add_key_value(FACETS *facets, const char *key, const char *value);
-void facets_add_key_value_length(FACETS *facets, const char *key, const char *value, size_t value_len);
+void facets_add_key_value_length(FACETS *facets, const char *key, size_t key_len, const char *value, size_t value_len);
void facets_report(FACETS *facets, BUFFER *wb);
void facets_accepted_parameters_to_json_array(FACETS *facets, BUFFER *wb, bool with_keys);