log2journal-logfmt.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "log2journal.h"
  3. #define LOGFMT_ERROR_LINE_MAX 1024
  4. #define LOGFMT_KEY_MAX 1024
  5. struct logfmt_state {
  6. LOG_JOB *jb;
  7. const char *line;
  8. uint32_t pos;
  9. uint32_t key_start;
  10. char key[LOGFMT_KEY_MAX];
  11. char msg[LOGFMT_ERROR_LINE_MAX];
  12. };
  13. #define logfmt_current_pos(lfs) &(lfs)->line[(lfs)->pos]
  14. #define logfmt_consume_char(lfs) ++(lfs)->pos
  15. static inline void logfmt_process_key_value(LOGFMT_STATE *lfs, const char *value, size_t len) {
  16. log_job_send_extracted_key_value(lfs->jb, lfs->key, value, len);
  17. }
  18. static inline void logfmt_skip_spaces(LOGFMT_STATE *lfs) {
  19. const char *s = logfmt_current_pos(lfs);
  20. const char *start = s;
  21. while(isspace(*s)) s++;
  22. lfs->pos += s - start;
  23. }
  24. static inline void copy_newline(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
  25. if(*remaining > 3) {
  26. *(*d)++ = '\\';
  27. *(*d)++ = 'n';
  28. (*remaining) -= 2;
  29. }
  30. }
  31. static inline void copy_tab(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
  32. if(*remaining > 3) {
  33. *(*d)++ = '\\';
  34. *(*d)++ = 't';
  35. (*remaining) -= 2;
  36. }
  37. }
  38. static inline bool logftm_parse_value(LOGFMT_STATE *lfs) {
  39. static __thread char value[JOURNAL_MAX_VALUE_LEN];
  40. char quote = '\0';
  41. const char *s = logfmt_current_pos(lfs);
  42. if(*s == '\"' || *s == '\'') {
  43. quote = *s;
  44. logfmt_consume_char(lfs);
  45. }
  46. value[0] = '\0';
  47. char *d = value;
  48. s = logfmt_current_pos(lfs);
  49. size_t remaining = sizeof(value);
  50. char end_char = (char)(quote == '\0' ? ' ' : quote);
  51. while (*s && *s != end_char) {
  52. char c;
  53. if (*s == '\\') {
  54. s++;
  55. switch (*s) {
  56. case 'n':
  57. copy_newline(lfs, &d, &remaining);
  58. s++;
  59. continue;
  60. case 't':
  61. copy_tab(lfs, &d, &remaining);
  62. s++;
  63. continue;
  64. case 'f':
  65. case 'b':
  66. case 'r':
  67. c = ' ';
  68. s++;
  69. break;
  70. default:
  71. c = *s++;
  72. break;
  73. }
  74. }
  75. else
  76. c = *s++;
  77. if(remaining < 2) {
  78. snprintf(lfs->msg, sizeof(lfs->msg),
  79. "LOGFMT PARSER: truncated string value at pos %zu", lfs->pos);
  80. return false;
  81. }
  82. else {
  83. *d++ = c;
  84. remaining--;
  85. }
  86. }
  87. *d = '\0';
  88. lfs->pos += s - logfmt_current_pos(lfs);
  89. s = logfmt_current_pos(lfs);
  90. if(quote != '\0') {
  91. if (*s != quote) {
  92. snprintf(lfs->msg, sizeof(lfs->msg),
  93. "LOGFMT PARSER: missing quote at pos %zu: '%s'",
  94. lfs->pos, s);
  95. return false;
  96. }
  97. else
  98. logfmt_consume_char(lfs);
  99. }
  100. if(d > value)
  101. logfmt_process_key_value(lfs, value, d - value);
  102. return true;
  103. }
  104. static inline bool logfmt_parse_key(LOGFMT_STATE *lfs) {
  105. logfmt_skip_spaces(lfs);
  106. char *d = &lfs->key[lfs->key_start];
  107. size_t remaining = sizeof(lfs->key) - (d - lfs->key);
  108. const char *s = logfmt_current_pos(lfs);
  109. char last_c = '\0';
  110. while(*s && *s != '=') {
  111. char c;
  112. if (*s == '\\')
  113. s++;
  114. c = journal_key_characters_map[(unsigned char)*s++];
  115. if(c == '_' && last_c == '_')
  116. continue;
  117. else {
  118. if(remaining < 2) {
  119. snprintf(lfs->msg, sizeof(lfs->msg),
  120. "LOGFMT PARSER: key buffer full - keys are too long, at pos %zu", lfs->pos);
  121. return false;
  122. }
  123. *d++ = c;
  124. remaining--;
  125. }
  126. last_c = c;
  127. }
  128. *d = '\0';
  129. lfs->pos += s - logfmt_current_pos(lfs);
  130. s = logfmt_current_pos(lfs);
  131. if(*s != '=') {
  132. snprintf(lfs->msg, sizeof(lfs->msg),
  133. "LOGFMT PARSER: key is missing the equal sign, at pos %zu", lfs->pos);
  134. return false;
  135. }
  136. logfmt_consume_char(lfs);
  137. return true;
  138. }
  139. LOGFMT_STATE *logfmt_parser_create(LOG_JOB *jb) {
  140. LOGFMT_STATE *lfs = mallocz(sizeof(LOGFMT_STATE));
  141. memset(lfs, 0, sizeof(LOGFMT_STATE));
  142. lfs->jb = jb;
  143. if(jb->prefix)
  144. lfs->key_start = copy_to_buffer(lfs->key, sizeof(lfs->key), lfs->jb->prefix, strlen(lfs->jb->prefix));
  145. return lfs;
  146. }
  147. void logfmt_parser_destroy(LOGFMT_STATE *lfs) {
  148. if(lfs)
  149. freez(lfs);
  150. }
  151. const char *logfmt_parser_error(LOGFMT_STATE *lfs) {
  152. return lfs->msg;
  153. }
  154. bool logfmt_parse_document(LOGFMT_STATE *lfs, const char *txt) {
  155. lfs->line = txt;
  156. lfs->pos = 0;
  157. lfs->msg[0] = '\0';
  158. const char *s;
  159. do {
  160. if(!logfmt_parse_key(lfs))
  161. return false;
  162. if(!logftm_parse_value(lfs))
  163. return false;
  164. logfmt_skip_spaces(lfs);
  165. s = logfmt_current_pos(lfs);
  166. } while(*s);
  167. return true;
  168. }
  169. void logfmt_test(void) {
  170. LOG_JOB jb = { .prefix = "NIGNX_" };
  171. LOGFMT_STATE *logfmt = logfmt_parser_create(&jb);
  172. logfmt_parse_document(logfmt, "x=1 y=2 z=\"3 \\ 4\" 5 ");
  173. logfmt_parser_destroy(logfmt);
  174. }