123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #include "log2journal.h"
- #define LOGFMT_ERROR_LINE_MAX 1024
- #define LOGFMT_KEY_MAX 1024
- struct logfmt_state {
- LOG_JOB *jb;
- const char *line;
- uint32_t pos;
- uint32_t key_start;
- char key[LOGFMT_KEY_MAX];
- char msg[LOGFMT_ERROR_LINE_MAX];
- };
- #define logfmt_current_pos(lfs) &(lfs)->line[(lfs)->pos]
- #define logfmt_consume_char(lfs) ++(lfs)->pos
- static inline void logfmt_process_key_value(LOGFMT_STATE *lfs, const char *value, size_t len) {
- log_job_send_extracted_key_value(lfs->jb, lfs->key, value, len);
- }
- static inline void logfmt_skip_spaces(LOGFMT_STATE *lfs) {
- const char *s = logfmt_current_pos(lfs);
- const char *start = s;
- while(isspace(*s)) s++;
- lfs->pos += s - start;
- }
- static inline void copy_newline(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
- if(*remaining > 3) {
- *(*d)++ = '\\';
- *(*d)++ = 'n';
- (*remaining) -= 2;
- }
- }
- static inline void copy_tab(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
- if(*remaining > 3) {
- *(*d)++ = '\\';
- *(*d)++ = 't';
- (*remaining) -= 2;
- }
- }
- static inline bool logftm_parse_value(LOGFMT_STATE *lfs) {
- static __thread char value[JOURNAL_MAX_VALUE_LEN];
- char quote = '\0';
- const char *s = logfmt_current_pos(lfs);
- if(*s == '\"' || *s == '\'') {
- quote = *s;
- logfmt_consume_char(lfs);
- }
- value[0] = '\0';
- char *d = value;
- s = logfmt_current_pos(lfs);
- size_t remaining = sizeof(value);
- char end_char = (char)(quote == '\0' ? ' ' : quote);
- while (*s && *s != end_char) {
- char c;
- if (*s == '\\') {
- s++;
- switch (*s) {
- case 'n':
- copy_newline(lfs, &d, &remaining);
- s++;
- continue;
- case 't':
- copy_tab(lfs, &d, &remaining);
- s++;
- continue;
- case 'f':
- case 'b':
- case 'r':
- c = ' ';
- s++;
- break;
- default:
- c = *s++;
- break;
- }
- }
- else
- c = *s++;
- if(remaining < 2) {
- snprintf(lfs->msg, sizeof(lfs->msg),
- "LOGFMT PARSER: truncated string value at pos %zu", lfs->pos);
- return false;
- }
- else {
- *d++ = c;
- remaining--;
- }
- }
- *d = '\0';
- lfs->pos += s - logfmt_current_pos(lfs);
- s = logfmt_current_pos(lfs);
- if(quote != '\0') {
- if (*s != quote) {
- snprintf(lfs->msg, sizeof(lfs->msg),
- "LOGFMT PARSER: missing quote at pos %zu: '%s'",
- lfs->pos, s);
- return false;
- }
- else
- logfmt_consume_char(lfs);
- }
- if(d > value)
- logfmt_process_key_value(lfs, value, d - value);
- return true;
- }
- static inline bool logfmt_parse_key(LOGFMT_STATE *lfs) {
- logfmt_skip_spaces(lfs);
- char *d = &lfs->key[lfs->key_start];
- size_t remaining = sizeof(lfs->key) - (d - lfs->key);
- const char *s = logfmt_current_pos(lfs);
- char last_c = '\0';
- while(*s && *s != '=') {
- char c;
- if (*s == '\\')
- s++;
- c = journal_key_characters_map[(unsigned char)*s++];
- if(c == '_' && last_c == '_')
- continue;
- else {
- if(remaining < 2) {
- snprintf(lfs->msg, sizeof(lfs->msg),
- "LOGFMT PARSER: key buffer full - keys are too long, at pos %zu", lfs->pos);
- return false;
- }
- *d++ = c;
- remaining--;
- }
- last_c = c;
- }
- *d = '\0';
- lfs->pos += s - logfmt_current_pos(lfs);
- s = logfmt_current_pos(lfs);
- if(*s != '=') {
- snprintf(lfs->msg, sizeof(lfs->msg),
- "LOGFMT PARSER: key is missing the equal sign, at pos %zu", lfs->pos);
- return false;
- }
- logfmt_consume_char(lfs);
- return true;
- }
- LOGFMT_STATE *logfmt_parser_create(LOG_JOB *jb) {
- LOGFMT_STATE *lfs = mallocz(sizeof(LOGFMT_STATE));
- memset(lfs, 0, sizeof(LOGFMT_STATE));
- lfs->jb = jb;
- if(jb->prefix)
- lfs->key_start = copy_to_buffer(lfs->key, sizeof(lfs->key), lfs->jb->prefix, strlen(lfs->jb->prefix));
- return lfs;
- }
- void logfmt_parser_destroy(LOGFMT_STATE *lfs) {
- if(lfs)
- freez(lfs);
- }
- const char *logfmt_parser_error(LOGFMT_STATE *lfs) {
- return lfs->msg;
- }
- bool logfmt_parse_document(LOGFMT_STATE *lfs, const char *txt) {
- lfs->line = txt;
- lfs->pos = 0;
- lfs->msg[0] = '\0';
- const char *s;
- do {
- if(!logfmt_parse_key(lfs))
- return false;
- if(!logftm_parse_value(lfs))
- return false;
- logfmt_skip_spaces(lfs);
- s = logfmt_current_pos(lfs);
- } while(*s);
- return true;
- }
- void logfmt_test(void) {
- LOG_JOB jb = { .prefix = "NIGNX_" };
- LOGFMT_STATE *logfmt = logfmt_parser_create(&jb);
- logfmt_parse_document(logfmt, "x=1 y=2 z=\"3 \\ 4\" 5 ");
- logfmt_parser_destroy(logfmt);
- }
|