log2journal.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "log2journal.h"
  3. // ----------------------------------------------------------------------------
  4. const char journal_key_characters_map[256] = {
  5. // control characters
  6. [0] = '\0', [1] = '_', [2] = '_', [3] = '_', [4] = '_', [5] = '_', [6] = '_', [7] = '_',
  7. [8] = '_', [9] = '_', [10] = '_', [11] = '_', [12] = '_', [13] = '_', [14] = '_', [15] = '_',
  8. [16] = '_', [17] = '_', [18] = '_', [19] = '_', [20] = '_', [21] = '_', [22] = '_', [23] = '_',
  9. [24] = '_', [25] = '_', [26] = '_', [27] = '_', [28] = '_', [29] = '_', [30] = '_', [31] = '_',
  10. // symbols
  11. [' '] = '_', ['!'] = '_', ['"'] = '_', ['#'] = '_', ['$'] = '_', ['%'] = '_', ['&'] = '_', ['\''] = '_',
  12. ['('] = '_', [')'] = '_', ['*'] = '_', ['+'] = '_', [','] = '_', ['-'] = '_', ['.'] = '_', ['/'] = '_',
  13. // numbers
  14. ['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7',
  15. ['8'] = '8', ['9'] = '9',
  16. // symbols
  17. [':'] = '_', [';'] = '_', ['<'] = '_', ['='] = '_', ['>'] = '_', ['?'] = '_', ['@'] = '_',
  18. // capitals
  19. ['A'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H',
  20. ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', ['M'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P',
  21. ['Q'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['W'] = 'W', ['X'] = 'X',
  22. ['Y'] = 'Y', ['Z'] = 'Z',
  23. // symbols
  24. ['['] = '_', ['\\'] = '_', [']'] = '_', ['^'] = '_', ['_'] = '_', ['`'] = '_',
  25. // lower to upper
  26. ['a'] = 'A', ['b'] = 'B', ['c'] = 'C', ['d'] = 'D', ['e'] = 'E', ['f'] = 'F', ['g'] = 'G', ['h'] = 'H',
  27. ['i'] = 'I', ['j'] = 'J', ['k'] = 'K', ['l'] = 'L', ['m'] = 'M', ['n'] = 'N', ['o'] = 'O', ['p'] = 'P',
  28. ['q'] = 'Q', ['r'] = 'R', ['s'] = 'S', ['t'] = 'T', ['u'] = 'U', ['v'] = 'V', ['w'] = 'W', ['x'] = 'X',
  29. ['y'] = 'Y', ['z'] = 'Z',
  30. // symbols
  31. ['{'] = '_', ['|'] = '_', ['}'] = '_', ['~'] = '_', [127] = '_', // Delete (DEL)
  32. // Extended ASCII characters (128-255) set to underscore
  33. [128] = '_', [129] = '_', [130] = '_', [131] = '_', [132] = '_', [133] = '_', [134] = '_', [135] = '_',
  34. [136] = '_', [137] = '_', [138] = '_', [139] = '_', [140] = '_', [141] = '_', [142] = '_', [143] = '_',
  35. [144] = '_', [145] = '_', [146] = '_', [147] = '_', [148] = '_', [149] = '_', [150] = '_', [151] = '_',
  36. [152] = '_', [153] = '_', [154] = '_', [155] = '_', [156] = '_', [157] = '_', [158] = '_', [159] = '_',
  37. [160] = '_', [161] = '_', [162] = '_', [163] = '_', [164] = '_', [165] = '_', [166] = '_', [167] = '_',
  38. [168] = '_', [169] = '_', [170] = '_', [171] = '_', [172] = '_', [173] = '_', [174] = '_', [175] = '_',
  39. [176] = '_', [177] = '_', [178] = '_', [179] = '_', [180] = '_', [181] = '_', [182] = '_', [183] = '_',
  40. [184] = '_', [185] = '_', [186] = '_', [187] = '_', [188] = '_', [189] = '_', [190] = '_', [191] = '_',
  41. [192] = '_', [193] = '_', [194] = '_', [195] = '_', [196] = '_', [197] = '_', [198] = '_', [199] = '_',
  42. [200] = '_', [201] = '_', [202] = '_', [203] = '_', [204] = '_', [205] = '_', [206] = '_', [207] = '_',
  43. [208] = '_', [209] = '_', [210] = '_', [211] = '_', [212] = '_', [213] = '_', [214] = '_', [215] = '_',
  44. [216] = '_', [217] = '_', [218] = '_', [219] = '_', [220] = '_', [221] = '_', [222] = '_', [223] = '_',
  45. [224] = '_', [225] = '_', [226] = '_', [227] = '_', [228] = '_', [229] = '_', [230] = '_', [231] = '_',
  46. [232] = '_', [233] = '_', [234] = '_', [235] = '_', [236] = '_', [237] = '_', [238] = '_', [239] = '_',
  47. [240] = '_', [241] = '_', [242] = '_', [243] = '_', [244] = '_', [245] = '_', [246] = '_', [247] = '_',
  48. [248] = '_', [249] = '_', [250] = '_', [251] = '_', [252] = '_', [253] = '_', [254] = '_', [255] = '_',
  49. };
  50. // ----------------------------------------------------------------------------
  51. static inline HASHED_KEY *get_key_from_hashtable(LOG_JOB *jb, HASHED_KEY *k) {
  52. if(k->flags & HK_HASHTABLE_ALLOCATED)
  53. return k;
  54. if(!k->hashtable_ptr) {
  55. HASHED_KEY *ht_key;
  56. SIMPLE_HASHTABLE_SLOT_KEY *slot = simple_hashtable_get_slot_KEY(&jb->hashtable, k->hash, true);
  57. if((ht_key = SIMPLE_HASHTABLE_SLOT_DATA(slot))) {
  58. if(!(ht_key->flags & HK_COLLISION_CHECKED)) {
  59. ht_key->flags |= HK_COLLISION_CHECKED;
  60. if(strcmp(ht_key->key, k->key) != 0)
  61. log2stderr("Hashtable collision detected on key '%s' (hash %lx) and '%s' (hash %lx). "
  62. "Please file a bug report.", ht_key->key, (unsigned long) ht_key->hash, k->key
  63. , (unsigned long) k->hash
  64. );
  65. }
  66. }
  67. else {
  68. ht_key = callocz(1, sizeof(HASHED_KEY));
  69. ht_key->key = strdupz(k->key);
  70. ht_key->len = k->len;
  71. ht_key->hash = k->hash;
  72. ht_key->flags = HK_HASHTABLE_ALLOCATED;
  73. simple_hashtable_set_slot_KEY(&jb->hashtable, slot, ht_key->hash, ht_key);
  74. }
  75. k->hashtable_ptr = ht_key;
  76. }
  77. return k->hashtable_ptr;
  78. }
  79. static inline HASHED_KEY *get_key_from_hashtable_with_char_ptr(LOG_JOB *jb, const char *key) {
  80. HASHED_KEY find = {
  81. .key = key,
  82. .len = strlen(key),
  83. };
  84. find.hash = XXH3_64bits(key, find.len);
  85. return get_key_from_hashtable(jb, &find);
  86. }
  87. // ----------------------------------------------------------------------------
  88. static inline void validate_key(LOG_JOB *jb __maybe_unused, HASHED_KEY *k) {
  89. if(k->len > JOURNAL_MAX_KEY_LEN)
  90. log2stderr("WARNING: key '%s' has length %zu, which is more than %zu, the max systemd-journal allows",
  91. k->key, (size_t)k->len, (size_t)JOURNAL_MAX_KEY_LEN);
  92. for(size_t i = 0; i < k->len ;i++) {
  93. char c = k->key[i];
  94. if((c < 'A' || c > 'Z') && !isdigit(c) && c != '_') {
  95. log2stderr("WARNING: key '%s' contains characters that are not allowed by systemd-journal.", k->key);
  96. break;
  97. }
  98. }
  99. if(isdigit(k->key[0]))
  100. log2stderr("WARNING: key '%s' starts with a digit and may not be accepted by systemd-journal.", k->key);
  101. if(k->key[0] == '_')
  102. log2stderr("WARNING: key '%s' starts with an underscore, which makes it a systemd-journal trusted field. "
  103. "Such fields are accepted by systemd-journal-remote, but not by systemd-journald.", k->key);
  104. }
  105. // ----------------------------------------------------------------------------
  106. static inline size_t replace_evaluate_to_buffer(LOG_JOB *jb, HASHED_KEY *k __maybe_unused, REPLACE_PATTERN *rp, char *dst, size_t dst_size) {
  107. size_t remaining = dst_size;
  108. char *copy_to = dst;
  109. for(REPLACE_NODE *node = rp->nodes; node != NULL && remaining > 1; node = node->next) {
  110. if(node->is_variable) {
  111. if(hashed_keys_match(&node->name, &jb->line.key)) {
  112. size_t copied = copy_to_buffer(copy_to, remaining, jb->line.trimmed, jb->line.trimmed_len);
  113. copy_to += copied;
  114. remaining -= copied;
  115. }
  116. else {
  117. HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
  118. if(ktmp->value.len) {
  119. size_t copied = copy_to_buffer(copy_to, remaining, ktmp->value.txt, ktmp->value.len);
  120. copy_to += copied;
  121. remaining -= copied;
  122. }
  123. }
  124. }
  125. else {
  126. size_t copied = copy_to_buffer(copy_to, remaining, node->name.key, node->name.len);
  127. copy_to += copied;
  128. remaining -= copied;
  129. }
  130. }
  131. return copy_to - dst;
  132. }
  133. static inline void replace_evaluate(LOG_JOB *jb, HASHED_KEY *k, REPLACE_PATTERN *rp) {
  134. HASHED_KEY *ht_key = get_key_from_hashtable(jb, k);
  135. // set it to empty value
  136. k->value.len = 0;
  137. for(REPLACE_NODE *node = rp->nodes; node != NULL; node = node->next) {
  138. if(node->is_variable) {
  139. if(hashed_keys_match(&node->name, &jb->line.key))
  140. txt_expand_and_append(&ht_key->value, jb->line.trimmed, jb->line.trimmed_len);
  141. else {
  142. HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
  143. if(ktmp->value.len)
  144. txt_expand_and_append(&ht_key->value, ktmp->value.txt, ktmp->value.len);
  145. }
  146. }
  147. else
  148. txt_expand_and_append(&ht_key->value, node->name.key, node->name.len);
  149. }
  150. }
  151. static inline void replace_evaluate_from_pcre2(LOG_JOB *jb, HASHED_KEY *k, REPLACE_PATTERN *rp, SEARCH_PATTERN *sp) {
  152. assert(k->flags & HK_HASHTABLE_ALLOCATED);
  153. // set the temporary TEXT to zero length
  154. jb->rewrites.tmp.len = 0;
  155. PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(sp->match_data);
  156. // Iterate through the linked list of replacement nodes
  157. for(REPLACE_NODE *node = rp->nodes; node != NULL; node = node->next) {
  158. if(node->is_variable) {
  159. int group_number = pcre2_substring_number_from_name(
  160. sp->re, (PCRE2_SPTR) node->name.key);
  161. if(group_number >= 0) {
  162. PCRE2_SIZE start_offset = ovector[2 * group_number];
  163. PCRE2_SIZE end_offset = ovector[2 * group_number + 1];
  164. PCRE2_SIZE length = end_offset - start_offset;
  165. txt_expand_and_append(&jb->rewrites.tmp, k->value.txt + start_offset, length);
  166. }
  167. else {
  168. if(hashed_keys_match(&node->name, &jb->line.key))
  169. txt_expand_and_append(&jb->rewrites.tmp, jb->line.trimmed, jb->line.trimmed_len);
  170. else {
  171. HASHED_KEY *ktmp = get_key_from_hashtable_with_char_ptr(jb, node->name.key);
  172. if(ktmp->value.len)
  173. txt_expand_and_append(&jb->rewrites.tmp, ktmp->value.txt, ktmp->value.len);
  174. }
  175. }
  176. }
  177. else {
  178. txt_expand_and_append(&jb->rewrites.tmp, node->name.key, node->name.len);
  179. }
  180. }
  181. // swap the values of the temporary TEXT and the key value
  182. TEXT tmp = k->value;
  183. k->value = jb->rewrites.tmp;
  184. jb->rewrites.tmp = tmp;
  185. }
  186. static inline bool rewrite_conditions_satisfied(LOG_JOB *jb, HASHED_KEY *k, REWRITE *rw) {
  187. assert(k->flags & HK_HASHTABLE_ALLOCATED);
  188. if(rw->flags & RW_MATCH_PCRE2) {
  189. return search_pattern_matches(&rw->match_pcre2, k->value.txt, k->value.len);
  190. }
  191. else if(rw->flags & RW_MATCH_NON_EMPTY) {
  192. char buffer[2]; // we don't need a big buffer - we just check if anything is written
  193. if(replace_evaluate_to_buffer(jb, k, &rw->match_non_empty, buffer, sizeof(buffer)))
  194. // it copied something
  195. return true;
  196. else
  197. // it copied nothing
  198. return false;
  199. }
  200. else
  201. // no conditions
  202. return true;
  203. }
  204. // ----------------------------------------------------------------------------
  205. static inline HASHED_KEY *rename_key(LOG_JOB *jb, HASHED_KEY *k) {
  206. if(!(k->flags & HK_RENAMES_CHECKED) || k->flags & HK_HAS_RENAMES) {
  207. k->flags |= HK_RENAMES_CHECKED;
  208. for(size_t i = 0; i < jb->renames.used; i++) {
  209. RENAME *rn = &jb->renames.array[i];
  210. if(hashed_keys_match(&rn->old_key, k)) {
  211. k->flags |= HK_HAS_RENAMES;
  212. return get_key_from_hashtable(jb, &rn->new_key);
  213. }
  214. }
  215. }
  216. return k;
  217. }
  218. // ----------------------------------------------------------------------------
  219. static inline void send_key_value_constant(LOG_JOB *jb __maybe_unused, HASHED_KEY *key, const char *value, size_t len) {
  220. HASHED_KEY *ht_key = get_key_from_hashtable(jb, key);
  221. txt_replace(&ht_key->value, value, len);
  222. ht_key->flags |= HK_VALUE_FROM_LOG;
  223. // fprintf(stderr, "SET %s=%.*s\n", ht_key->key, (int)ht_key->value.len, ht_key->value.txt);
  224. }
  225. static inline void send_key_value_error(LOG_JOB *jb, HASHED_KEY *key, const char *format, ...) __attribute__ ((format(__printf__, 3, 4)));
  226. static inline void send_key_value_error(LOG_JOB *jb, HASHED_KEY *key, const char *format, ...) {
  227. HASHED_KEY *ht_key = get_key_from_hashtable(jb, key);
  228. printf("%s=", ht_key->key);
  229. va_list args;
  230. va_start(args, format);
  231. vprintf(format, args);
  232. va_end(args);
  233. printf("\n");
  234. }
  235. inline void log_job_send_extracted_key_value(LOG_JOB *jb, const char *key, const char *value, size_t len) {
  236. HASHED_KEY *ht_key = get_key_from_hashtable_with_char_ptr(jb, key);
  237. HASHED_KEY *nk = rename_key(jb, ht_key);
  238. txt_replace(&nk->value, value, len);
  239. ht_key->flags |= HK_VALUE_FROM_LOG;
  240. // fprintf(stderr, "SET %s=%.*s\n", ht_key->key, (int)ht_key->value.len, ht_key->value.txt);
  241. }
  242. static inline void log_job_process_rewrites(LOG_JOB *jb) {
  243. for(size_t i = 0; i < jb->rewrites.used ;i++) {
  244. REWRITE *rw = &jb->rewrites.array[i];
  245. HASHED_KEY *k = get_key_from_hashtable(jb, &rw->key);
  246. if(!(rw->flags & RW_INJECT) && !(k->flags & HK_VALUE_FROM_LOG) && !k->value.len)
  247. continue;
  248. if(!(k->flags & HK_VALUE_REWRITTEN) && rewrite_conditions_satisfied(jb, k, rw)) {
  249. if(rw->flags & RW_MATCH_PCRE2)
  250. replace_evaluate_from_pcre2(jb, k, &rw->value, &rw->match_pcre2);
  251. else
  252. replace_evaluate(jb, k, &rw->value);
  253. if(!(rw->flags & RW_DONT_STOP))
  254. k->flags |= HK_VALUE_REWRITTEN;
  255. // fprintf(stderr, "REWRITE %s=%.*s\n", k->key, (int)k->value.len, k->value.txt);
  256. }
  257. }
  258. }
  259. static inline void send_all_fields(LOG_JOB *jb) {
  260. SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY(&jb->hashtable, kptr, HASHED_KEY, _KEY) {
  261. HASHED_KEY *k = SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY_VALUE(kptr);
  262. if(k->value.len) {
  263. // the key exists and has some value
  264. if(!(k->flags & HK_FILTERED)) {
  265. k->flags |= HK_FILTERED;
  266. bool included = jb->filter.include.re ? search_pattern_matches(&jb->filter.include, k->key, k->len) : true;
  267. bool excluded = jb->filter.exclude.re ? search_pattern_matches(&jb->filter.exclude, k->key, k->len) : false;
  268. if(included && !excluded)
  269. k->flags |= HK_FILTERED_INCLUDED;
  270. else
  271. k->flags &= ~HK_FILTERED_INCLUDED;
  272. // log some error if the key does not comply to journal standards
  273. validate_key(jb, k);
  274. }
  275. if(k->flags & HK_FILTERED_INCLUDED)
  276. printf("%s=%.*s\n", k->key, (int)k->value.len, k->value.txt);
  277. // reset it for the next round
  278. k->value.txt[0] = '\0';
  279. k->value.len = 0;
  280. }
  281. k->flags &= ~(HK_VALUE_REWRITTEN | HK_VALUE_FROM_LOG);
  282. }
  283. }
  284. // ----------------------------------------------------------------------------
  285. // injection of constant fields
  286. static void select_which_injections_should_be_injected_on_unmatched(LOG_JOB *jb) {
  287. // mark all injections to be added to unmatched logs
  288. for(size_t i = 0; i < jb->injections.used ; i++)
  289. jb->injections.keys[i].on_unmatched = true;
  290. if(jb->injections.used && jb->unmatched.injections.used) {
  291. // we have both injections and injections on unmatched
  292. // we find all the injections that are also configured as injections on unmatched,
  293. // and we disable them, so that the output will not have the same key twice
  294. for(size_t i = 0; i < jb->injections.used ;i++) {
  295. for(size_t u = 0; u < jb->unmatched.injections.used ; u++) {
  296. if(strcmp(jb->injections.keys[i].key.key, jb->unmatched.injections.keys[u].key.key) == 0)
  297. jb->injections.keys[i].on_unmatched = false;
  298. }
  299. }
  300. }
  301. }
  302. static inline void jb_finalize_injections(LOG_JOB *jb, bool line_is_matched) {
  303. for (size_t j = 0; j < jb->injections.used; j++) {
  304. if(!line_is_matched && !jb->injections.keys[j].on_unmatched)
  305. continue;
  306. INJECTION *inj = &jb->injections.keys[j];
  307. replace_evaluate(jb, &inj->key, &inj->value);
  308. }
  309. }
  310. // ----------------------------------------------------------------------------
  311. // filename injection
  312. static inline void jb_inject_filename(LOG_JOB *jb) {
  313. if (jb->filename.key.key && jb->filename.current.len)
  314. send_key_value_constant(jb, &jb->filename.key, jb->filename.current.txt, jb->filename.current.len);
  315. }
  316. static inline bool jb_switched_filename(LOG_JOB *jb, const char *line, size_t len) {
  317. // IMPORTANT:
  318. // Return TRUE when the caller should skip this line (because it is ours).
  319. // Unfortunately, we have to consume empty lines too.
  320. // IMPORTANT:
  321. // filename may not be NULL terminated and have more data than the filename.
  322. if (!len) {
  323. jb->filename.last_line_was_empty = true;
  324. return true;
  325. }
  326. // Check if it's a log file change line
  327. if (jb->filename.last_line_was_empty && line[0] == '=' && strncmp(line, "==> ", 4) == 0) {
  328. const char *start = line + 4;
  329. const char *end = strstr(line, " <==");
  330. while (*start == ' ') start++;
  331. if (*start != '\n' && *start != '\0' && end) {
  332. txt_replace(&jb->filename.current, start, end - start);
  333. return true;
  334. }
  335. }
  336. jb->filename.last_line_was_empty = false;
  337. return false;
  338. }
  339. static inline bool jb_send_unmatched_line(LOG_JOB *jb, const char *line) {
  340. if (!jb->unmatched.key.key)
  341. return false;
  342. // we are sending errors to systemd-journal
  343. send_key_value_error(jb, &jb->unmatched.key, "Parsing error on: %s", line);
  344. for (size_t j = 0; j < jb->unmatched.injections.used; j++) {
  345. INJECTION *inj = &jb->unmatched.injections.keys[j];
  346. replace_evaluate(jb, &inj->key, &inj->value);
  347. }
  348. return true;
  349. }
  350. // ----------------------------------------------------------------------------
  351. // running a job
  352. static char *get_next_line(LOG_JOB *jb __maybe_unused, char *buffer, size_t size, size_t *line_length) {
  353. if(!fgets(buffer, (int)size, stdin)) {
  354. *line_length = 0;
  355. return NULL;
  356. }
  357. char *line = buffer;
  358. size_t len = strlen(line);
  359. // remove trailing newlines and spaces
  360. while(len > 1 && (line[len - 1] == '\n' || isspace(line[len - 1])))
  361. line[--len] = '\0';
  362. // skip leading spaces
  363. while(isspace(*line)) {
  364. line++;
  365. len--;
  366. }
  367. *line_length = len;
  368. return line;
  369. }
  370. int log_job_run(LOG_JOB *jb) {
  371. select_which_injections_should_be_injected_on_unmatched(jb);
  372. PCRE2_STATE *pcre2 = NULL;
  373. LOG_JSON_STATE *json = NULL;
  374. LOGFMT_STATE *logfmt = NULL;
  375. if(strcmp(jb->pattern, "json") == 0) {
  376. json = json_parser_create(jb);
  377. // never fails
  378. }
  379. else if(strcmp(jb->pattern, "logfmt") == 0) {
  380. logfmt = logfmt_parser_create(jb);
  381. // never fails
  382. }
  383. else if(strcmp(jb->pattern, "none") != 0) {
  384. pcre2 = pcre2_parser_create(jb);
  385. if(pcre2_has_error(pcre2)) {
  386. log2stderr("%s", pcre2_parser_error(pcre2));
  387. pcre2_parser_destroy(pcre2);
  388. return 1;
  389. }
  390. }
  391. jb->line.buffer = mallocz(MAX_LINE_LENGTH + 1);
  392. jb->line.size = MAX_LINE_LENGTH + 1;
  393. jb->line.trimmed_len = 0;
  394. jb->line.trimmed = jb->line.buffer;
  395. while ((jb->line.trimmed = get_next_line(jb, (char *)jb->line.buffer, jb->line.size, &jb->line.trimmed_len))) {
  396. const char *line = jb->line.trimmed;
  397. size_t len = jb->line.trimmed_len;
  398. if(jb_switched_filename(jb, line, len))
  399. continue;
  400. bool line_is_matched = true;
  401. if(json)
  402. line_is_matched = json_parse_document(json, line);
  403. else if(logfmt)
  404. line_is_matched = logfmt_parse_document(logfmt, line);
  405. else if(pcre2)
  406. line_is_matched = pcre2_parse_document(pcre2, line, len);
  407. if(!line_is_matched) {
  408. if(json)
  409. log2stderr("%s", json_parser_error(json));
  410. else if(logfmt)
  411. log2stderr("%s", logfmt_parser_error(logfmt));
  412. else if(pcre2)
  413. log2stderr("%s", pcre2_parser_error(pcre2));
  414. if(!jb_send_unmatched_line(jb, line))
  415. // just logging to stderr, not sending unmatched lines
  416. continue;
  417. }
  418. jb_inject_filename(jb);
  419. jb_finalize_injections(jb, line_is_matched);
  420. log_job_process_rewrites(jb);
  421. send_all_fields(jb);
  422. printf("\n");
  423. fflush(stdout);
  424. }
  425. if(json)
  426. json_parser_destroy(json);
  427. else if(logfmt)
  428. logfmt_parser_destroy(logfmt);
  429. else if(pcre2)
  430. pcre2_parser_destroy(pcre2);
  431. freez((void *)jb->line.buffer);
  432. return 0;
  433. }
  434. // ----------------------------------------------------------------------------
  435. int main(int argc, char *argv[]) {
  436. LOG_JOB log_job;
  437. log_job_init(&log_job);
  438. if(!log_job_command_line_parse_parameters(&log_job, argc, argv))
  439. exit(1);
  440. if(log_job.show_config)
  441. log_job_configuration_to_yaml(&log_job);
  442. int ret = log_job_run(&log_job);
  443. log_job_cleanup(&log_job);
  444. return ret;
  445. }