mirror of
https://github.com/netdata/netdata.git
synced 2025-04-13 09:11:50 +00:00

* cleanup of logging - wip
* first working iteration
* add errno annotator
* replace old logging functions with netdata_logger()
* cleanup
* update error_limit
* fix remanining error_limit references
* work on fatal()
* started working on structured logs
* full cleanup
* default logging to files; fix all plugins initialization
* fix formatting of numbers
* cleanup and reorg
* fix coverity issues
* cleanup obsolete code
* fix formatting of numbers
* fix log rotation
* fix for older systems
* add detection of systemd journal via stderr
* finished on access.log
* remove left-over transport
* do not add empty fields to the logs
* journal get compact uuids; X-Transaction-ID header is added in web responses
* allow compiling on systems without memfd sealing
* added libnetdata/uuid directory
* move datetime formatters to libnetdata
* add missing files
* link the makefiles in libnetdata
* added uuid_parse_flexi() to parse UUIDs with and without hyphens; the web server now read X-Transaction-ID and uses it for functions and web responses
* added stream receiver, sender, proc plugin and pluginsd log stack
* iso8601 advanced usage; line_splitter module in libnetdata; code cleanup
* add message ids to streaming inbound and outbound connections
* cleanup line_splitter between lines to avoid logging garbage; when killing children, kill them with SIGABRT if internal checks is enabled
* send SIGABRT to external plugins only if we are not shutting down
* fix cross cleanup in pluginsd parser
* fatal when there is a stack error in logs
* compile netdata with -fexceptions
* do not kill external plugins with SIGABRT
* metasync info logs to debug level
* added severity to logs
* added json output; added options per log output; added documentation; fixed issues mentioned
* allow memfd only on linux
* moved journal low level functions to journal.c/h
* move health logs to daemon.log with proper priorities
* fixed a couple of bugs; health log in journal
* updated docs
* systemd-cat-native command to push structured logs to journal from the command line
* fix makefiles
* restored NETDATA_LOG_SEVERITY_LEVEL
* fix makefiles
* systemd-cat-native can also work as the logger of Netdata scripts
* do not require a socket to systemd-journal to log-as-netdata
* alarm notify logs in native format
* properly compare log ids
* fatals log alerts; alarm-notify.sh working
* fix overflow warning
* alarm-notify.sh now logs the request (command line)
* anotate external plugins logs with the function cmd they run
* added context, component and type to alarm-notify.sh; shell sanitization removes control character and characters that may be expanded by bash
* reformatted alarm-notify logs
* unify cgroup-network-helper.sh
* added quotes around params
* charts.d.plugin switched logging to journal native
* quotes for logfmt
* unify the status codes of streaming receivers and senders
* alarm-notify: dont log anything, if there is nothing to do
* all external plugins log to stderr when running outside netdata; alarm-notify now shows an error when notifications menthod are needed but are not available
* migrate cgroup-name.sh to new logging
* systemd-cat-native now supports messages with newlines
* socket.c logs use priority
* cleanup log field types
* inherit the systemd set INVOCATION_ID if found
* allow systemd-cat-native to send messages to a systemd-journal-remote URL
* log2journal command that can convert structured logs to journal export format
* various fixes and documentation of log2journal
* updated log2journal docs
* updated log2journal docs
* updated documentation of fields
* allow compiling without libcurl
* do not use socket as format string
* added version information to newly added tools
* updated documentation and help messages
* fix the namespace socket path
* print errno with error
* do not timeout
* updated docs
* updated docs
* updated docs
* log2journal updated docs and params
* when talking to a remote journal, systemd-cat-native batches the messages
* enable lz4 compression for systemd-cat-native when sending messages to a systemd-journal-remote
* Revert "enable lz4 compression for systemd-cat-native when sending messages to a systemd-journal-remote"
This reverts commit b079d53c11
.
* note about uncompressed traffic
* log2journal: code reorg and cleanup to make modular
* finished rewriting log2journal
* more comments
* rewriting rules support
* increased limits
* updated docs
* updated docs
* fix old log call
* use journal only when stderr is connected to journal
* update netdata.spec for libcurl, libpcre2 and log2journal
* pcre2-devel
* do not require pcre2 in centos < 8, amazonlinux < 2023, open suse
* log2journal only on systems pcre2 is available
* ignore log2journal in .gitignore
* avoid log2journal on centos 7, amazonlinux 2 and opensuse
* add pcre2-8 to static build
* undo last commit
* Bundle to static
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
* Add build deps for deb packages
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
* Add dependencies; build from source
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
* Test build for amazon linux and centos expect to fail for suse
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
* fix minor oversight
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
* Reorg code
* Add the install from source (deps) as a TODO
* Not enable the build on suse ecosystem
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
---------
Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud>
Co-authored-by: Tasos Katsoulas <tasos@netdata.cloud>
1228 lines
38 KiB
C
1228 lines
38 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#ifndef NETDATA_WEB_BUFFER_H
|
|
#define NETDATA_WEB_BUFFER_H 1
|
|
|
|
#include "../string/utf8.h"
|
|
#include "../libnetdata.h"
|
|
|
|
#ifdef ENABLE_H2O
|
|
#include "h2o/memory.h"
|
|
#endif
|
|
|
|
#define WEB_DATA_LENGTH_INCREASE_STEP 1024
|
|
|
|
#define BUFFER_JSON_MAX_DEPTH 32 // max is 255
|
|
|
|
extern const char hex_digits[16];
|
|
extern const char base64_digits[64];
|
|
extern unsigned char hex_value_from_ascii[256];
|
|
extern unsigned char base64_value_from_ascii[256];
|
|
|
|
typedef enum __attribute__ ((__packed__)) {
|
|
BUFFER_JSON_EMPTY = 0,
|
|
BUFFER_JSON_OBJECT,
|
|
BUFFER_JSON_ARRAY,
|
|
} BUFFER_JSON_NODE_TYPE;
|
|
|
|
typedef struct web_buffer_json_node {
|
|
BUFFER_JSON_NODE_TYPE type;
|
|
uint32_t count:24;
|
|
} BUFFER_JSON_NODE;
|
|
|
|
#define BUFFER_QUOTE_MAX_SIZE 7
|
|
|
|
typedef enum __attribute__ ((__packed__)) {
|
|
WB_CONTENT_CACHEABLE = (1 << 0),
|
|
WB_CONTENT_NO_CACHEABLE = (1 << 1),
|
|
} BUFFER_OPTIONS;
|
|
|
|
typedef enum __attribute__ ((__packed__)) {
|
|
CT_NONE = 0,
|
|
CT_APPLICATION_JSON,
|
|
CT_TEXT_PLAIN,
|
|
CT_TEXT_HTML,
|
|
CT_APPLICATION_X_JAVASCRIPT,
|
|
CT_TEXT_CSS,
|
|
CT_TEXT_XML,
|
|
CT_APPLICATION_XML,
|
|
CT_TEXT_XSL,
|
|
CT_APPLICATION_OCTET_STREAM,
|
|
CT_APPLICATION_X_FONT_TRUETYPE,
|
|
CT_APPLICATION_X_FONT_OPENTYPE,
|
|
CT_APPLICATION_FONT_WOFF,
|
|
CT_APPLICATION_FONT_WOFF2,
|
|
CT_APPLICATION_VND_MS_FONTOBJ,
|
|
CT_IMAGE_SVG_XML,
|
|
CT_IMAGE_PNG,
|
|
CT_IMAGE_JPG,
|
|
CT_IMAGE_GIF,
|
|
CT_IMAGE_XICON,
|
|
CT_IMAGE_ICNS,
|
|
CT_IMAGE_BMP,
|
|
CT_PROMETHEUS,
|
|
CT_AUDIO_MPEG,
|
|
CT_AUDIO_OGG,
|
|
CT_VIDEO_MP4,
|
|
CT_APPLICATION_PDF,
|
|
CT_APPLICATION_ZIP,
|
|
} HTTP_CONTENT_TYPE;
|
|
|
|
typedef enum __attribute__ ((__packed__)) {
|
|
BUFFER_JSON_OPTIONS_DEFAULT = 0,
|
|
BUFFER_JSON_OPTIONS_MINIFY = (1 << 0),
|
|
BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS = (1 << 1),
|
|
BUFFER_JSON_OPTIONS_NON_ANONYMOUS = (1 << 2),
|
|
} BUFFER_JSON_OPTIONS;
|
|
|
|
typedef struct web_buffer {
|
|
size_t size; // allocation size of buffer, in bytes
|
|
size_t len; // current data length in buffer, in bytes
|
|
char *buffer; // the buffer itself
|
|
HTTP_CONTENT_TYPE content_type; // the content type of the data in the buffer
|
|
BUFFER_OPTIONS options; // options related to the content
|
|
time_t date; // the timestamp this content has been generated
|
|
time_t expires; // the timestamp this content expires
|
|
size_t *statistics;
|
|
|
|
struct {
|
|
char key_quote[BUFFER_QUOTE_MAX_SIZE + 1];
|
|
char value_quote[BUFFER_QUOTE_MAX_SIZE + 1];
|
|
int8_t depth;
|
|
BUFFER_JSON_OPTIONS options;
|
|
BUFFER_JSON_NODE stack[BUFFER_JSON_MAX_DEPTH];
|
|
} json;
|
|
} BUFFER;
|
|
|
|
#define CLEAN_BUFFER _cleanup_(buffer_freep) BUFFER
|
|
|
|
#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
|
|
#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0)
|
|
|
|
#define buffer_strlen(wb) ((wb)->len)
|
|
|
|
#define BUFFER_OVERFLOW_EOF "EOF"
|
|
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
#define buffer_overflow_check(b) _buffer_overflow_check(b)
|
|
#else
|
|
#define buffer_overflow_check(b)
|
|
#endif
|
|
|
|
static inline void _buffer_overflow_check(BUFFER *b) {
|
|
assert(b->len <= b->size &&
|
|
"BUFFER: length is above buffer size.");
|
|
|
|
assert(!(b->buffer && (b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF) != 0)) &&
|
|
"BUFFER: detected overflow.");
|
|
}
|
|
|
|
static inline void buffer_flush(BUFFER *wb) {
|
|
wb->len = 0;
|
|
|
|
wb->json.depth = 0;
|
|
wb->json.stack[0].type = BUFFER_JSON_EMPTY;
|
|
wb->json.stack[0].count = 0;
|
|
|
|
if(wb->buffer)
|
|
wb->buffer[0] = '\0';
|
|
}
|
|
|
|
void buffer_reset(BUFFER *wb);
|
|
|
|
void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
|
|
void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
|
|
|
|
BUFFER *buffer_create(size_t size, size_t *statistics);
|
|
void buffer_free(BUFFER *b);
|
|
void buffer_increase(BUFFER *b, size_t free_size_required);
|
|
|
|
static inline void buffer_freep(BUFFER **bp) {
|
|
if(bp) buffer_free(*bp);
|
|
}
|
|
|
|
void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4);
|
|
void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
|
|
void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3);
|
|
void buffer_strcat_htmlescape(BUFFER *wb, const char *txt);
|
|
|
|
void buffer_char_replace(BUFFER *wb, char from, char to);
|
|
|
|
void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit);
|
|
|
|
#ifdef ENABLE_H2O
|
|
h2o_iovec_t buffer_to_h2o_iovec(BUFFER *wb);
|
|
#endif
|
|
|
|
static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) {
|
|
if(unlikely(buffer->len + needed_free_size >= buffer->size))
|
|
buffer_increase(buffer, needed_free_size + 1);
|
|
}
|
|
|
|
void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value_quote, int depth,
|
|
bool add_anonymous_object, BUFFER_JSON_OPTIONS options);
|
|
|
|
void buffer_json_finalize(BUFFER *wb);
|
|
|
|
static const char *buffer_tostring(BUFFER *wb)
|
|
{
|
|
buffer_need_bytes(wb, 1);
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
|
|
return(wb->buffer);
|
|
}
|
|
|
|
static inline void _buffer_json_depth_push(BUFFER *wb, BUFFER_JSON_NODE_TYPE type) {
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
assert(wb->json.depth <= BUFFER_JSON_MAX_DEPTH && "BUFFER JSON: max nesting reached");
|
|
#endif
|
|
wb->json.depth++;
|
|
wb->json.stack[wb->json.depth].count = 0;
|
|
wb->json.stack[wb->json.depth].type = type;
|
|
}
|
|
|
|
static inline void _buffer_json_depth_pop(BUFFER *wb) {
|
|
wb->json.depth--;
|
|
}
|
|
|
|
static inline void buffer_fast_charcat(BUFFER *wb, const char c) {
|
|
|
|
buffer_need_bytes(wb, 2);
|
|
*(&wb->buffer[wb->len]) = c;
|
|
wb->len += 1;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
|
|
if(unlikely(!txt || !*txt || !len)) return;
|
|
|
|
buffer_need_bytes(wb, len + 1);
|
|
|
|
const char *t = txt;
|
|
const char *e = &txt[len];
|
|
|
|
char *d = &wb->buffer[wb->len];
|
|
|
|
while(t != e)
|
|
*d++ = *t++;
|
|
|
|
wb->len += len;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_putc(BUFFER *wb, char c) {
|
|
buffer_need_bytes(wb, 2);
|
|
wb->buffer[wb->len++] = c;
|
|
wb->buffer[wb->len] = '\0';
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
|
|
if(unlikely(!txt || !*txt || !len)) return;
|
|
|
|
buffer_need_bytes(wb, len + 1);
|
|
|
|
const char *t = txt;
|
|
const char *e = &txt[len];
|
|
|
|
char *d = &wb->buffer[wb->len];
|
|
|
|
while(t != e
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
&& *t
|
|
#endif
|
|
)
|
|
*d++ = *t++;
|
|
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
assert(!(t != e && !*t) && "BUFFER: source string is shorter than the length given.");
|
|
#endif
|
|
|
|
wb->len += len;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_strcat(BUFFER *wb, const char *txt) {
|
|
if(unlikely(!txt || !*txt)) return;
|
|
|
|
const char *t = txt;
|
|
while(*t) {
|
|
buffer_need_bytes(wb, 100);
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = s;
|
|
const char *e = &wb->buffer[wb->size];
|
|
|
|
while(*t && d < e)
|
|
*d++ = *t++;
|
|
|
|
wb->len += d - s;
|
|
}
|
|
|
|
buffer_need_bytes(wb, 1);
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_contents_replace(BUFFER *wb, const char *txt, size_t len) {
|
|
wb->len = 0;
|
|
buffer_need_bytes(wb, len + 1);
|
|
|
|
memcpy(wb->buffer, txt, len);
|
|
wb->len = len;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_strncat(BUFFER *wb, const char *txt, size_t len) {
|
|
if(unlikely(!txt || !*txt)) return;
|
|
|
|
buffer_need_bytes(wb, len + 1);
|
|
|
|
memcpy(&wb->buffer[wb->len], txt, len);
|
|
|
|
wb->len += len;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_memcat(BUFFER *wb, const void *mem, size_t bytes) {
|
|
if(unlikely(!mem)) return;
|
|
|
|
buffer_need_bytes(wb, bytes + 1);
|
|
|
|
memcpy(&wb->buffer[wb->len], mem, bytes);
|
|
|
|
wb->len += bytes;
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_json_strcat(BUFFER *wb, const char *txt) {
|
|
if(unlikely(!txt || !*txt)) return;
|
|
|
|
const unsigned char *t = (const unsigned char *)txt;
|
|
while(*t) {
|
|
buffer_need_bytes(wb, 110);
|
|
unsigned char *s = (unsigned char *)&wb->buffer[wb->len];
|
|
unsigned char *d = s;
|
|
const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence
|
|
|
|
while(*t && d < e) {
|
|
#ifdef BUFFER_JSON_ESCAPE_UTF
|
|
if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) {
|
|
// UTF-8 multi-byte encoded character
|
|
|
|
// find how big this character is (2-4 bytes)
|
|
size_t utf_character_size = 2;
|
|
while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size]))
|
|
utf_character_size++;
|
|
|
|
uint32_t code_point = 0;
|
|
for (size_t i = 0; i < utf_character_size; i++) {
|
|
code_point <<= 6;
|
|
code_point |= (t[i] & 0x3F);
|
|
}
|
|
|
|
t += utf_character_size;
|
|
|
|
// encode as \u escape sequence
|
|
*d++ = '\\';
|
|
*d++ = 'u';
|
|
*d++ = hex_digits[(code_point >> 12) & 0xf];
|
|
*d++ = hex_digits[(code_point >> 8) & 0xf];
|
|
*d++ = hex_digits[(code_point >> 4) & 0xf];
|
|
*d++ = hex_digits[code_point & 0xf];
|
|
}
|
|
else
|
|
#endif
|
|
if(unlikely(*t < ' ')) {
|
|
uint32_t v = *t++;
|
|
*d++ = '\\';
|
|
*d++ = 'u';
|
|
*d++ = hex_digits[(v >> 12) & 0xf];
|
|
*d++ = hex_digits[(v >> 8) & 0xf];
|
|
*d++ = hex_digits[(v >> 4) & 0xf];
|
|
*d++ = hex_digits[v & 0xf];
|
|
}
|
|
else {
|
|
if (unlikely(*t == '\\' || *t == '\"'))
|
|
*d++ = '\\';
|
|
|
|
*d++ = *t++;
|
|
}
|
|
}
|
|
|
|
wb->len += d - s;
|
|
}
|
|
|
|
buffer_need_bytes(wb, 1);
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_json_quoted_strcat(BUFFER *wb, const char *txt) {
|
|
if(unlikely(!txt || !*txt)) return;
|
|
|
|
if(*txt == '"')
|
|
txt++;
|
|
|
|
const char *t = txt;
|
|
while(*t) {
|
|
buffer_need_bytes(wb, 100);
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = s;
|
|
const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character
|
|
|
|
while(*t && d < e) {
|
|
if(unlikely(*t == '"' && !t[1])) {
|
|
t++;
|
|
continue;
|
|
}
|
|
|
|
if(unlikely(*t == '\\' || *t == '"'))
|
|
*d++ = '\\';
|
|
|
|
*d++ = *t++;
|
|
}
|
|
|
|
wb->len += d - s;
|
|
}
|
|
|
|
buffer_need_bytes(wb, 1);
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
// This trick seems to give an 80% speed increase in 32bit systems
|
|
// print_number_llu_r() will just print the digits up to the
|
|
// point the remaining value fits in 32 bits, and then calls
|
|
// print_number_lu_r() to print the rest with 32 bit arithmetic.
|
|
|
|
static inline char *print_uint32_reversed(char *dst, uint32_t value) {
|
|
char *d = dst;
|
|
do *d++ = (char)('0' + (value % 10)); while((value /= 10));
|
|
return d;
|
|
}
|
|
|
|
static inline char *print_uint64_reversed(char *dst, uint64_t value) {
|
|
#ifdef ENV32BIT
|
|
if(value <= (uint64_t)0xffffffff)
|
|
return print_uint32_reversed(dst, value);
|
|
|
|
char *d = dst;
|
|
do *d++ = (char)('0' + (value % 10)); while((value /= 10) && value > (uint64_t)0xffffffff);
|
|
if(value) return print_uint32_reversed(d, value);
|
|
return d;
|
|
#else
|
|
char *d = dst;
|
|
do *d++ = (char)('0' + (value % 10)); while((value /= 10));
|
|
return d;
|
|
#endif
|
|
}
|
|
|
|
static inline char *print_uint32_hex_reversed(char *dst, uint32_t value) {
|
|
static const char *digits = "0123456789ABCDEF";
|
|
char *d = dst;
|
|
do *d++ = digits[value & 0xf]; while((value >>= 4));
|
|
return d;
|
|
}
|
|
|
|
static inline char *print_uint64_hex_reversed(char *dst, uint64_t value) {
|
|
#ifdef ENV32BIT
|
|
if(value <= (uint64_t)0xffffffff)
|
|
return print_uint32_hex_reversed(dst, value);
|
|
|
|
char *d = dst;
|
|
do *d++ = hex_digits[value & 0xf]; while((value >>= 4) && value > (uint64_t)0xffffffff);
|
|
if(value) return print_uint32_hex_reversed(d, value);
|
|
return d;
|
|
#else
|
|
char *d = dst;
|
|
do *d++ = hex_digits[value & 0xf]; while((value >>= 4));
|
|
return d;
|
|
#endif
|
|
}
|
|
|
|
static inline char *print_uint64_base64_reversed(char *dst, uint64_t value) {
|
|
char *d = dst;
|
|
do *d++ = base64_digits[value & 63]; while ((value >>= 6));
|
|
return d;
|
|
}
|
|
|
|
static inline void char_array_reverse(char *from, char *to) {
|
|
// from and to are inclusive
|
|
char *begin = from, *end = to, aux;
|
|
while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
|
|
}
|
|
|
|
static inline int print_netdata_double(char *dst, NETDATA_DOUBLE value) {
|
|
char *s = dst;
|
|
|
|
if(unlikely(value < 0)) {
|
|
*s++ = '-';
|
|
value = fabsndd(value);
|
|
}
|
|
|
|
uint64_t fractional_precision = 10000000ULL; // fractional part 7 digits
|
|
int fractional_wanted_digits = 7;
|
|
int exponent = 0;
|
|
if(unlikely(value >= (NETDATA_DOUBLE)(UINT64_MAX / 10))) {
|
|
// the number is too big to print using 64bit numbers
|
|
// so, let's convert it to exponential notation
|
|
exponent = (int)(floorndd(log10ndd(value)));
|
|
value /= powndd(10, exponent);
|
|
|
|
// the max precision we can support is 18 digits
|
|
// (UINT64_MAX is 20, but the first is 1)
|
|
fractional_precision = 1000000000000000000ULL; // fractional part 18 digits
|
|
fractional_wanted_digits = 18;
|
|
}
|
|
|
|
char *d = s;
|
|
NETDATA_DOUBLE integral_d, fractional_d;
|
|
fractional_d = modfndd(value, &integral_d);
|
|
|
|
// get the integral and the fractional parts as 64-bit integers
|
|
uint64_t integral = (uint64_t)integral_d;
|
|
uint64_t fractional = (uint64_t)llrintndd(fractional_d * (NETDATA_DOUBLE)fractional_precision);
|
|
if(unlikely(fractional >= fractional_precision)) {
|
|
integral++;
|
|
fractional -= fractional_precision;
|
|
}
|
|
|
|
// convert the integral part to string (reversed)
|
|
d = print_uint64_reversed(d, integral);
|
|
char_array_reverse(s, d - 1); // copy reversed the integral string
|
|
|
|
if(likely(fractional != 0)) {
|
|
*d++ = '.'; // add the dot
|
|
|
|
// convert the fractional part to string (reversed)
|
|
d = print_uint64_reversed(s = d, fractional);
|
|
|
|
while(d - s < fractional_wanted_digits) *d++ = '0'; // prepend zeros to reach precision
|
|
char_array_reverse(s, d - 1); // copy reversed the fractional string
|
|
|
|
// remove trailing zeros from the fractional part
|
|
while(*(d - 1) == '0') d--;
|
|
}
|
|
|
|
if(unlikely(exponent != 0)) {
|
|
*d++ = 'e';
|
|
*d++ = '+';
|
|
d = print_uint32_reversed(s = d, exponent);
|
|
char_array_reverse(s, d - 1);
|
|
}
|
|
|
|
*d = '\0';
|
|
return (int)(d - dst);
|
|
}
|
|
|
|
static inline void buffer_print_uint64(BUFFER *wb, uint64_t value) {
|
|
buffer_need_bytes(wb, 50);
|
|
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = print_uint64_reversed(s, value);
|
|
char_array_reverse(s, d - 1);
|
|
*d = '\0';
|
|
wb->len += d - s;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_int64(BUFFER *wb, int64_t value) {
|
|
buffer_need_bytes(wb, 50);
|
|
|
|
if(value < 0) {
|
|
buffer_fast_strcat(wb, "-", 1);
|
|
value = -value;
|
|
}
|
|
|
|
buffer_print_uint64(wb, (uint64_t)value);
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_uint64_hex(BUFFER *wb, uint64_t value) {
|
|
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1);
|
|
|
|
buffer_fast_strcat(wb, HEX_PREFIX, sizeof(HEX_PREFIX) - 1);
|
|
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = print_uint64_hex_reversed(s, value);
|
|
char_array_reverse(s, d - 1);
|
|
*d = '\0';
|
|
wb->len += d - s;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_uint64_base64(BUFFER *wb, uint64_t value) {
|
|
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1);
|
|
|
|
buffer_fast_strcat(wb, IEEE754_UINT64_B64_PREFIX, sizeof(IEEE754_UINT64_B64_PREFIX) - 1);
|
|
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = print_uint64_base64_reversed(s, value);
|
|
char_array_reverse(s, d - 1);
|
|
*d = '\0';
|
|
wb->len += d - s;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_int64_hex(BUFFER *wb, int64_t value) {
|
|
buffer_need_bytes(wb, 2);
|
|
|
|
if(value < 0) {
|
|
buffer_fast_strcat(wb, "-", 1);
|
|
value = -value;
|
|
}
|
|
|
|
buffer_print_uint64_hex(wb, (uint64_t)value);
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_int64_base64(BUFFER *wb, int64_t value) {
|
|
buffer_need_bytes(wb, 2);
|
|
|
|
if(value < 0) {
|
|
buffer_fast_strcat(wb, "-", 1);
|
|
value = -value;
|
|
}
|
|
|
|
buffer_print_uint64_base64(wb, (uint64_t)value);
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_netdata_double(BUFFER *wb, NETDATA_DOUBLE value) {
|
|
buffer_need_bytes(wb, 512 + 2);
|
|
|
|
if(isnan(value) || isinf(value)) {
|
|
buffer_fast_strcat(wb, "null", 4);
|
|
return;
|
|
}
|
|
else
|
|
wb->len += print_netdata_double(&wb->buffer[wb->len], value);
|
|
|
|
// terminate it
|
|
buffer_need_bytes(wb, 1);
|
|
wb->buffer[wb->len] = '\0';
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_netdata_double_hex(BUFFER *wb, NETDATA_DOUBLE value) {
|
|
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
|
|
|
|
uint64_t *ptr = (uint64_t *) (&value);
|
|
buffer_fast_strcat(wb, IEEE754_DOUBLE_HEX_PREFIX, sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1);
|
|
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = print_uint64_hex_reversed(s, *ptr);
|
|
char_array_reverse(s, d - 1);
|
|
*d = '\0';
|
|
wb->len += d - s;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_netdata_double_base64(BUFFER *wb, NETDATA_DOUBLE value) {
|
|
buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1);
|
|
|
|
uint64_t *ptr = (uint64_t *) (&value);
|
|
buffer_fast_strcat(wb, IEEE754_DOUBLE_B64_PREFIX, sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1);
|
|
|
|
char *s = &wb->buffer[wb->len];
|
|
char *d = print_uint64_base64_reversed(s, *ptr);
|
|
char_array_reverse(s, d - 1);
|
|
*d = '\0';
|
|
wb->len += d - s;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
typedef enum {
|
|
NUMBER_ENCODING_DECIMAL,
|
|
NUMBER_ENCODING_HEX,
|
|
NUMBER_ENCODING_BASE64,
|
|
} NUMBER_ENCODING;
|
|
|
|
static inline void buffer_print_int64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, int64_t value) {
|
|
if(encoding == NUMBER_ENCODING_BASE64)
|
|
return buffer_print_int64_base64(wb, value);
|
|
|
|
if(encoding == NUMBER_ENCODING_HEX)
|
|
return buffer_print_int64_hex(wb, value);
|
|
|
|
return buffer_print_int64(wb, value);
|
|
}
|
|
|
|
static inline void buffer_print_uint64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, uint64_t value) {
|
|
if(encoding == NUMBER_ENCODING_BASE64)
|
|
return buffer_print_uint64_base64(wb, value);
|
|
|
|
if(encoding == NUMBER_ENCODING_HEX)
|
|
return buffer_print_uint64_hex(wb, value);
|
|
|
|
return buffer_print_uint64(wb, value);
|
|
}
|
|
|
|
static inline void buffer_print_netdata_double_encoded(BUFFER *wb, NUMBER_ENCODING encoding, NETDATA_DOUBLE value) {
|
|
if(encoding == NUMBER_ENCODING_BASE64)
|
|
return buffer_print_netdata_double_base64(wb, value);
|
|
|
|
if(encoding == NUMBER_ENCODING_HEX)
|
|
return buffer_print_netdata_double_hex(wb, value);
|
|
|
|
return buffer_print_netdata_double(wb, value);
|
|
}
|
|
|
|
static inline void buffer_print_spaces(BUFFER *wb, size_t spaces) {
|
|
buffer_need_bytes(wb, spaces * 4 + 1);
|
|
|
|
char *d = &wb->buffer[wb->len];
|
|
for(size_t i = 0; i < spaces; i++) {
|
|
*d++ = ' ';
|
|
*d++ = ' ';
|
|
*d++ = ' ';
|
|
*d++ = ' ';
|
|
}
|
|
|
|
*d = '\0';
|
|
wb->len += spaces * 4;
|
|
|
|
buffer_overflow_check(wb);
|
|
}
|
|
|
|
static inline void buffer_print_json_comma(BUFFER *wb) {
|
|
if(wb->json.stack[wb->json.depth].count)
|
|
buffer_fast_strcat(wb, ",", 1);
|
|
}
|
|
|
|
static inline void buffer_print_json_comma_newline_spacing(BUFFER *wb) {
|
|
buffer_print_json_comma(wb);
|
|
|
|
if((wb->json.options & BUFFER_JSON_OPTIONS_MINIFY) ||
|
|
(wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY && !(wb->json.options & BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS)))
|
|
return;
|
|
|
|
buffer_fast_strcat(wb, "\n", 1);
|
|
buffer_print_spaces(wb, wb->json.depth + 1);
|
|
}
|
|
|
|
static inline void buffer_print_json_key(BUFFER *wb, const char *key) {
|
|
buffer_strcat(wb, wb->json.key_quote);
|
|
buffer_json_strcat(wb, key);
|
|
buffer_strcat(wb, wb->json.key_quote);
|
|
}
|
|
|
|
static inline void buffer_json_add_string_value(BUFFER *wb, const char *value) {
|
|
if(value) {
|
|
buffer_strcat(wb, wb->json.value_quote);
|
|
buffer_json_strcat(wb, value);
|
|
buffer_strcat(wb, wb->json.value_quote);
|
|
}
|
|
else
|
|
buffer_fast_strcat(wb, "null", 4);
|
|
}
|
|
|
|
static inline void buffer_json_add_quoted_string_value(BUFFER *wb, const char *value) {
|
|
if(value) {
|
|
buffer_strcat(wb, wb->json.value_quote);
|
|
buffer_json_quoted_strcat(wb, value);
|
|
buffer_strcat(wb, wb->json.value_quote);
|
|
}
|
|
else
|
|
buffer_fast_strcat(wb, "null", 4);
|
|
}
|
|
|
|
static inline void buffer_json_member_add_object(BUFFER *wb, const char *key) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":{", 2);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
|
|
_buffer_json_depth_push(wb, BUFFER_JSON_OBJECT);
|
|
}
|
|
|
|
static inline void buffer_json_object_close(BUFFER *wb) {
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it");
|
|
assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_OBJECT && "BUFFER JSON: an object is not open to close it");
|
|
#endif
|
|
if(!(wb->json.options & BUFFER_JSON_OPTIONS_MINIFY)) {
|
|
buffer_fast_strcat(wb, "\n", 1);
|
|
buffer_print_spaces(wb, wb->json.depth);
|
|
}
|
|
buffer_fast_strcat(wb, "}", 1);
|
|
_buffer_json_depth_pop(wb);
|
|
}
|
|
|
|
static inline void buffer_json_member_add_string(BUFFER *wb, const char *key, const char *value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_json_add_string_value(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_string_or_omit(BUFFER *wb, const char *key, const char *value) {
|
|
if(value && *value)
|
|
buffer_json_member_add_string(wb, key, value);
|
|
}
|
|
|
|
static inline void buffer_json_member_add_string_or_empty(BUFFER *wb, const char *key, const char *value) {
|
|
if(!value)
|
|
value = "";
|
|
|
|
buffer_json_member_add_string(wb, key, value);
|
|
}
|
|
|
|
static inline void buffer_json_member_add_quoted_string(BUFFER *wb, const char *key, const char *value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
|
|
if(!value || strcmp(value, "null") == 0)
|
|
buffer_fast_strcat(wb, "null", 4);
|
|
else
|
|
buffer_json_add_quoted_string_value(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_uuid(BUFFER *wb, const char *key, uuid_t *value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
|
|
if(value && !uuid_is_null(*value)) {
|
|
char uuid[GUID_LEN + 1];
|
|
uuid_unparse_lower(*value, uuid);
|
|
buffer_json_add_string_value(wb, uuid);
|
|
}
|
|
else
|
|
buffer_json_add_string_value(wb, NULL);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_boolean(BUFFER *wb, const char *key, bool value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_strcat(wb, value?"true":"false");
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_array(BUFFER *wb, const char *key) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
if (key) {
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":[", 2);
|
|
}
|
|
else
|
|
buffer_fast_strcat(wb, "[", 1);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
|
|
_buffer_json_depth_push(wb, BUFFER_JSON_ARRAY);
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_array(BUFFER *wb) {
|
|
if(!(wb->json.options & BUFFER_JSON_OPTIONS_MINIFY) && wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY) {
|
|
// an array inside another array
|
|
buffer_print_json_comma(wb);
|
|
buffer_fast_strcat(wb, "\n", 1);
|
|
buffer_print_spaces(wb, wb->json.depth + 1);
|
|
}
|
|
else
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_fast_strcat(wb, "[", 1);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
|
|
_buffer_json_depth_push(wb, BUFFER_JSON_ARRAY);
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_string(BUFFER *wb, const char *value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_json_add_string_value(wb, value);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_double(BUFFER *wb, NETDATA_DOUBLE value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_netdata_double(wb, value);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_int64(BUFFER *wb, int64_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_int64(wb, value);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_uint64(wb, value);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_boolean(BUFFER *wb, bool value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_strcat(wb, value ? "true" : "false");
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_time_t(BUFFER *wb, time_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_int64(wb, value);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_time_ms(BUFFER *wb, time_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_int64(wb, value);
|
|
buffer_fast_strcat(wb, "000", 3);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_time_t2ms(BUFFER *wb, time_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_print_int64(wb, value);
|
|
buffer_fast_strcat(wb, "000", 3);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_add_array_item_object(BUFFER *wb) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
|
|
buffer_fast_strcat(wb, "{", 1);
|
|
wb->json.stack[wb->json.depth].count++;
|
|
|
|
_buffer_json_depth_push(wb, BUFFER_JSON_OBJECT);
|
|
}
|
|
|
|
static inline void buffer_json_member_add_time_t(BUFFER *wb, const char *key, time_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_print_int64(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_time_t2ms(BUFFER *wb, const char *key, time_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_print_int64(wb, value);
|
|
buffer_fast_strcat(wb, "000", 3);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_uint64(BUFFER *wb, const char *key, uint64_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_print_uint64(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_int64(BUFFER *wb, const char *key, int64_t value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_print_int64(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_member_add_double(BUFFER *wb, const char *key, NETDATA_DOUBLE value) {
|
|
buffer_print_json_comma_newline_spacing(wb);
|
|
buffer_print_json_key(wb, key);
|
|
buffer_fast_strcat(wb, ":", 1);
|
|
buffer_print_netdata_double(wb, value);
|
|
|
|
wb->json.stack[wb->json.depth].count++;
|
|
}
|
|
|
|
static inline void buffer_json_array_close(BUFFER *wb) {
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it");
|
|
assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY && "BUFFER JSON: an array is not open to close it");
|
|
#endif
|
|
if(wb->json.options & BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS) {
|
|
buffer_fast_strcat(wb, "\n", 1);
|
|
buffer_print_spaces(wb, wb->json.depth);
|
|
}
|
|
|
|
buffer_fast_strcat(wb, "]", 1);
|
|
_buffer_json_depth_pop(wb);
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_OPTS_NONE = 0,
|
|
RRDF_FIELD_OPTS_UNIQUE_KEY = (1 << 0), // the field is the unique key of the row
|
|
RRDF_FIELD_OPTS_VISIBLE = (1 << 1), // the field should be visible by default
|
|
RRDF_FIELD_OPTS_STICKY = (1 << 2), // the field should be sticky
|
|
RRDF_FIELD_OPTS_FULL_WIDTH = (1 << 3), // the field should get full width
|
|
RRDF_FIELD_OPTS_WRAP = (1 << 4), // the field should wrap
|
|
RRDF_FIELD_OPTS_DUMMY = (1 << 5), // not a presentable field
|
|
RRDF_FIELD_OPTS_EXPANDED_FILTER = (1 << 6), // show the filter expanded
|
|
} RRDF_FIELD_OPTIONS;
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_TYPE_NONE,
|
|
RRDF_FIELD_TYPE_INTEGER,
|
|
RRDF_FIELD_TYPE_BOOLEAN,
|
|
RRDF_FIELD_TYPE_STRING,
|
|
RRDF_FIELD_TYPE_DETAIL_STRING,
|
|
RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
|
|
RRDF_FIELD_TYPE_DURATION,
|
|
RRDF_FIELD_TYPE_TIMESTAMP,
|
|
RRDF_FIELD_TYPE_ARRAY,
|
|
} RRDF_FIELD_TYPE;
|
|
|
|
static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) {
|
|
switch(type) {
|
|
default:
|
|
case RRDF_FIELD_TYPE_NONE:
|
|
return "none";
|
|
|
|
case RRDF_FIELD_TYPE_INTEGER:
|
|
return "integer";
|
|
|
|
case RRDF_FIELD_TYPE_BOOLEAN:
|
|
return "boolean";
|
|
|
|
case RRDF_FIELD_TYPE_STRING:
|
|
return "string";
|
|
|
|
case RRDF_FIELD_TYPE_DETAIL_STRING:
|
|
return "detail-string";
|
|
|
|
case RRDF_FIELD_TYPE_BAR_WITH_INTEGER:
|
|
return "bar-with-integer";
|
|
|
|
case RRDF_FIELD_TYPE_DURATION:
|
|
return "duration";
|
|
|
|
case RRDF_FIELD_TYPE_TIMESTAMP:
|
|
return "timestamp";
|
|
|
|
case RRDF_FIELD_TYPE_ARRAY:
|
|
return "array";
|
|
}
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_VISUAL_VALUE, // show the value, possibly applying a transformation
|
|
RRDF_FIELD_VISUAL_BAR, // show the value and a bar, respecting the max field to fill the bar at 100%
|
|
RRDF_FIELD_VISUAL_PILL, //
|
|
RRDF_FIELD_VISUAL_RICH, //
|
|
RRDR_FIELD_VISUAL_ROW_OPTIONS, // this is a dummy column that is used for row options
|
|
} RRDF_FIELD_VISUAL;
|
|
|
|
static inline const char *rrdf_field_visual_to_string(RRDF_FIELD_VISUAL visual) {
|
|
switch(visual) {
|
|
default:
|
|
case RRDF_FIELD_VISUAL_VALUE:
|
|
return "value";
|
|
|
|
case RRDF_FIELD_VISUAL_BAR:
|
|
return "bar";
|
|
|
|
case RRDF_FIELD_VISUAL_PILL:
|
|
return "pill";
|
|
|
|
case RRDF_FIELD_VISUAL_RICH:
|
|
return "richValue";
|
|
|
|
case RRDR_FIELD_VISUAL_ROW_OPTIONS:
|
|
return "rowOptions";
|
|
}
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_TRANSFORM_NONE, // show the value as-is
|
|
RRDF_FIELD_TRANSFORM_NUMBER, // show the value respecting the decimal_points
|
|
RRDF_FIELD_TRANSFORM_DURATION_S, // transform as duration in second to a human-readable duration
|
|
RRDF_FIELD_TRANSFORM_DATETIME_MS, // UNIX epoch timestamp in ms
|
|
RRDF_FIELD_TRANSFORM_DATETIME_USEC, // UNIX epoch timestamp in usec
|
|
} RRDF_FIELD_TRANSFORM;
|
|
|
|
static inline const char *rrdf_field_transform_to_string(RRDF_FIELD_TRANSFORM transform) {
|
|
switch(transform) {
|
|
default:
|
|
case RRDF_FIELD_TRANSFORM_NONE:
|
|
return "none";
|
|
|
|
case RRDF_FIELD_TRANSFORM_NUMBER:
|
|
return "number";
|
|
|
|
case RRDF_FIELD_TRANSFORM_DURATION_S:
|
|
return "duration";
|
|
|
|
case RRDF_FIELD_TRANSFORM_DATETIME_MS:
|
|
return "datetime";
|
|
|
|
case RRDF_FIELD_TRANSFORM_DATETIME_USEC:
|
|
return "datetime_usec";
|
|
}
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_SORT_ASCENDING = (1 << 0),
|
|
RRDF_FIELD_SORT_DESCENDING = (1 << 1),
|
|
|
|
RRDF_FIELD_SORT_FIXED = (1 << 7),
|
|
} RRDF_FIELD_SORT;
|
|
|
|
static inline const char *rrdf_field_sort_to_string(RRDF_FIELD_SORT sort) {
|
|
if(sort & RRDF_FIELD_SORT_DESCENDING)
|
|
return "descending";
|
|
|
|
else
|
|
return "ascending";
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_SUMMARY_UNIQUECOUNT, // Finds the number of unique values of a group of rows
|
|
RRDF_FIELD_SUMMARY_SUM, // Sums the values of a group of rows
|
|
RRDF_FIELD_SUMMARY_MIN, // Finds the minimum value of a group of rows
|
|
RRDF_FIELD_SUMMARY_MAX, // Finds the maximum value of a group of rows
|
|
// RRDF_FIELD_SUMMARY_EXTENT, // Finds the minimum and maximum values of a group of rows
|
|
RRDF_FIELD_SUMMARY_MEAN, // Finds the mean/average value of a group of rows
|
|
RRDF_FIELD_SUMMARY_MEDIAN, // Finds the median value of a group of rows
|
|
// RRDF_FIELD_SUMMARY_UNIQUE, // Finds the unique values of a group of rows
|
|
RRDF_FIELD_SUMMARY_COUNT, // Calculates the number of rows in a group
|
|
} RRDF_FIELD_SUMMARY;
|
|
|
|
static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summary) {
|
|
switch(summary) {
|
|
default:
|
|
case RRDF_FIELD_SUMMARY_COUNT:
|
|
return "count";
|
|
|
|
case RRDF_FIELD_SUMMARY_UNIQUECOUNT:
|
|
return "uniqueCount";
|
|
|
|
case RRDF_FIELD_SUMMARY_SUM:
|
|
return "sum";
|
|
|
|
case RRDF_FIELD_SUMMARY_MIN:
|
|
return "min";
|
|
|
|
case RRDF_FIELD_SUMMARY_MEAN:
|
|
return "mean";
|
|
|
|
case RRDF_FIELD_SUMMARY_MEDIAN:
|
|
return "median";
|
|
|
|
case RRDF_FIELD_SUMMARY_MAX:
|
|
return "max";
|
|
}
|
|
}
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
RRDF_FIELD_FILTER_NONE = 0,
|
|
RRDF_FIELD_FILTER_RANGE,
|
|
RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_FILTER_FACET,
|
|
} RRDF_FIELD_FILTER;
|
|
|
|
static inline const char *rrdf_field_filter_to_string(RRDF_FIELD_FILTER filter) {
|
|
switch(filter) {
|
|
case RRDF_FIELD_FILTER_RANGE:
|
|
return "range";
|
|
|
|
case RRDF_FIELD_FILTER_MULTISELECT:
|
|
return "multiselect";
|
|
|
|
case RRDF_FIELD_FILTER_FACET:
|
|
return "facet";
|
|
|
|
default:
|
|
case RRDF_FIELD_FILTER_NONE:
|
|
return "none";
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
buffer_rrdf_table_add_field(BUFFER *wb, size_t field_id, const char *key, const char *name, RRDF_FIELD_TYPE type,
|
|
RRDF_FIELD_VISUAL visual, RRDF_FIELD_TRANSFORM transform, size_t decimal_points,
|
|
const char *units, NETDATA_DOUBLE max, RRDF_FIELD_SORT sort, const char *pointer_to,
|
|
RRDF_FIELD_SUMMARY summary, RRDF_FIELD_FILTER filter, RRDF_FIELD_OPTIONS options,
|
|
const char *default_value) {
|
|
|
|
buffer_json_member_add_object(wb, key);
|
|
{
|
|
buffer_json_member_add_uint64(wb, "index", field_id);
|
|
buffer_json_member_add_boolean(wb, "unique_key", options & RRDF_FIELD_OPTS_UNIQUE_KEY);
|
|
buffer_json_member_add_string(wb, "name", name);
|
|
buffer_json_member_add_boolean(wb, "visible", options & RRDF_FIELD_OPTS_VISIBLE);
|
|
buffer_json_member_add_string(wb, "type", rrdf_field_type_to_string(type));
|
|
buffer_json_member_add_string_or_omit(wb, "units", units);
|
|
buffer_json_member_add_string(wb, "visualization", rrdf_field_visual_to_string(visual));
|
|
|
|
buffer_json_member_add_object(wb, "value_options");
|
|
{
|
|
buffer_json_member_add_string_or_omit(wb, "units", units);
|
|
buffer_json_member_add_string(wb, "transform", rrdf_field_transform_to_string(transform));
|
|
buffer_json_member_add_uint64(wb, "decimal_points", decimal_points);
|
|
buffer_json_member_add_string(wb, "default_value", default_value);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
if (!isnan((NETDATA_DOUBLE) (max)))
|
|
buffer_json_member_add_double(wb, "max", (NETDATA_DOUBLE) (max));
|
|
|
|
buffer_json_member_add_string_or_omit(wb, "pointer_to", pointer_to);
|
|
buffer_json_member_add_string(wb, "sort", rrdf_field_sort_to_string(sort));
|
|
buffer_json_member_add_boolean(wb, "sortable", !(sort & RRDF_FIELD_SORT_FIXED));
|
|
buffer_json_member_add_boolean(wb, "sticky", options & RRDF_FIELD_OPTS_STICKY);
|
|
buffer_json_member_add_string(wb, "summary", rrdf_field_summary_to_string(summary));
|
|
buffer_json_member_add_string(wb, "filter", rrdf_field_filter_to_string(filter));
|
|
|
|
buffer_json_member_add_boolean(wb, "full_width", options & RRDF_FIELD_OPTS_FULL_WIDTH);
|
|
buffer_json_member_add_boolean(wb, "wrap", options & RRDF_FIELD_OPTS_WRAP);
|
|
buffer_json_member_add_boolean(wb, "default_expanded_filter", options & RRDF_FIELD_OPTS_EXPANDED_FILTER);
|
|
|
|
if(options & RRDF_FIELD_OPTS_DUMMY)
|
|
buffer_json_member_add_boolean(wb, "dummy", true);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
}
|
|
|
|
#endif /* NETDATA_WEB_BUFFER_H */
|