master
c 239 lines 5.62 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "log2journal.h"
4
5 #define LOGFMT_ERROR_LINE_MAX 1024
6 #define LOGFMT_KEY_MAX 1024
7
8 struct logfmt_state {
9 LOG_JOB *jb;
10
11 const char *line;
12 uint32_t pos;
13 uint32_t key_start;
14
15 char key[LOGFMT_KEY_MAX];
16 char msg[LOGFMT_ERROR_LINE_MAX];
17 };
18
19 #define logfmt_current_pos(lfs) &(lfs)->line[(lfs)->pos]
20 #define logfmt_consume_char(lfs) ++(lfs)->pos
21
22 static inline void logfmt_process_key_value(LOGFMT_STATE *lfs, const char *value, size_t len) {
23 log_job_send_extracted_key_value(lfs->jb, lfs->key, value, len);
24 }
25
26 static inline void logfmt_skip_spaces(LOGFMT_STATE *lfs) {
27 const char *s = logfmt_current_pos(lfs);
28 const char *start = s;
29
30 while(isspace(*s)) s++;
31
32 lfs->pos += s - start;
33 }
34
35 static inline void copy_newline(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
36 if(*remaining > 3) {
37 *(*d)++ = '\\';
38 *(*d)++ = 'n';
39 (*remaining) -= 2;
40 }
41 }
42
43 static inline void copy_tab(LOGFMT_STATE *lfs __maybe_unused, char **d, size_t *remaining) {
44 if(*remaining > 3) {
45 *(*d)++ = '\\';
46 *(*d)++ = 't';
47 (*remaining) -= 2;
48 }
49 }
50
51 static inline bool logftm_parse_value(LOGFMT_STATE *lfs) {
52 static __thread char value[JOURNAL_MAX_VALUE_LEN];
53
54 char quote = '\0';
55 const char *s = logfmt_current_pos(lfs);
56 if(*s == '"' || *s == '\'') {
57 quote = *s;
58 logfmt_consume_char(lfs);
59 }
60
61 value[0] = '\0';
62 char *d = value;
63 s = logfmt_current_pos(lfs);
64 size_t remaining = sizeof(value);
65
66 char end_char = (char)(quote == '\0' ? ' ' : quote);
67 while (*s && *s != end_char) {
68 char c;
69
70 if (*s == '\\') {
71 s++;
72
73 if(!*s)
74 c = '\\';
75 else {
76 switch (*s) {
77 case 'n':
78 copy_newline(lfs, &d, &remaining);
79 s++;
80 continue;
81
82 case 't':
83 copy_tab(lfs, &d, &remaining);
84 s++;
85 continue;
86
87 case 'f':
88 case 'b':
89 case 'r':
90 c = ' ';
91 s++;
92 break;
93
94 default:
95 c = *s++;
96 break;
97 }
98 }
99 }
100 else
101 c = *s++;
102
103 if(remaining < 2) {
104 snprintf(lfs->msg, sizeof(lfs->msg),
105 "LOGFMT PARSER: truncated string value at position %u", lfs->pos);
106 return false;
107 }
108 else {
109 *d++ = c;
110 remaining--;
111 }
112 }
113 *d = '\0';
114 lfs->pos += s - logfmt_current_pos(lfs);
115
116 s = logfmt_current_pos(lfs);
117
118 if(quote != '\0') {
119 if (*s != quote) {
120 snprintf(lfs->msg, sizeof(lfs->msg),
121 "LOGFMT PARSER: missing quote at position %u: '%s'",
122 lfs->pos, s);
123 return false;
124 }
125 else
126 logfmt_consume_char(lfs);
127 }
128
129 if(d > value)
130 logfmt_process_key_value(lfs, value, d - value);
131
132 return true;
133 }
134
135 static inline bool logfmt_parse_key(LOGFMT_STATE *lfs) {
136 logfmt_skip_spaces(lfs);
137
138 char *d = &lfs->key[lfs->key_start];
139
140 size_t remaining = sizeof(lfs->key) - (d - lfs->key);
141
142 const char *s = logfmt_current_pos(lfs);
143 char last_c = '\0';
144 while(*s && *s != '=') {
145 char c;
146
147 if (*s == '\\') {
148 s++;
149
150 if(!*s)
151 c = journal_key_characters_map[(unsigned char)'\\'];
152 else
153 c = journal_key_characters_map[(unsigned char)*s++];
154 }
155 else
156 c = journal_key_characters_map[(unsigned char)*s++];
157
158 if(c == '_' && last_c == '_')
159 continue;
160 else {
161 if(remaining < 2) {
162 snprintf(lfs->msg, sizeof(lfs->msg),
163 "LOGFMT PARSER: key buffer full - keys are too long, at position %u", lfs->pos);
164 return false;
165 }
166 *d++ = c;
167 remaining--;
168 }
169
170 last_c = c;
171 }
172 *d = '\0';
173 lfs->pos += s - logfmt_current_pos(lfs);
174
175 s = logfmt_current_pos(lfs);
176 if(*s != '=') {
177 snprintf(lfs->msg, sizeof(lfs->msg),
178 "LOGFMT PARSER: key is missing the equal sign, at position %u", lfs->pos);
179 return false;
180 }
181
182 logfmt_consume_char(lfs);
183
184 return true;
185 }
186
187 LOGFMT_STATE *logfmt_parser_create(LOG_JOB *jb) {
188 LOGFMT_STATE *lfs = mallocz(sizeof(LOGFMT_STATE));
189 memset(lfs, 0, sizeof(LOGFMT_STATE));
190 lfs->jb = jb;
191
192 if(jb->prefix)
193 lfs->key_start = copy_to_buffer(lfs->key, sizeof(lfs->key), lfs->jb->prefix, strlen(lfs->jb->prefix));
194
195 return lfs;
196 }
197
198 void logfmt_parser_destroy(LOGFMT_STATE *lfs) {
199 if(lfs)
200 freez(lfs);
201 }
202
203 const char *logfmt_parser_error(LOGFMT_STATE *lfs) {
204 return lfs->msg;
205 }
206
207 bool logfmt_parse_document(LOGFMT_STATE *lfs, const char *txt) {
208 lfs->line = txt;
209 lfs->pos = 0;
210 lfs->msg[0] = '\0';
211
212 const char *s;
213 do {
214 if(!logfmt_parse_key(lfs))
215 return false;
216
217 if(!logftm_parse_value(lfs))
218 return false;
219
220 logfmt_skip_spaces(lfs);
221
222 s = logfmt_current_pos(lfs);
223 } while(*s);
224
225 return true;
226 }
227
228
229 void logfmt_test(void) {
230 LOG_JOB jb = { 0 };
231 log_job_init(&jb);
232 log_job_key_prefix_set(&jb, "NGINX_", 6);
233 LOGFMT_STATE *logfmt = logfmt_parser_create(&jb);
234
235 logfmt_parse_document(logfmt, "x=1 y=2 z=\"3 \\ 4\" 5 ");
236
237 logfmt_parser_destroy(logfmt);
238 log_job_cleanup(&jb);
239 }