Skip to content

Commit 92ef906

Browse files
Fujimoto Seijiedsiper
authored andcommitted
out_file: add support for arbitrary templating (#1480)
This adds a new format option "template" that allows us to use Python-like format specifiers. Here is a configuration example: [OUTPUT] Name file Format template Template {time} [{level}] {message} This produce an output like the following: 1564455346.000257 [INFO] Service is starting up. 1564455347.000254 [INFO] Waiting when the pool is empty. This helps us getting closer to Fluentd's formatting ability. Notably, this allows us to do single_value formatting that is currently missing in Fluent Bit. Signed-off-by: Fujimoto Seiji <[email protected]>
1 parent dc487dc commit 92ef906

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

plugins/out_file/file.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct flb_file_conf {
4242
const char *out_file;
4343
const char *delimiter;
4444
const char *label_delimiter;
45+
const char *template;
4546
int format;
4647
};
4748

@@ -84,6 +85,7 @@ static int cb_file_init(struct flb_output_instance *ins,
8485
conf->format = FLB_OUT_FILE_FMT_JSON; /* default */
8586
conf->delimiter = NULL;
8687
conf->label_delimiter = NULL;
88+
conf->template = NULL;
8789

8890
/* Optional output file name/path */
8991
tmp = flb_output_get_property("Path", ins);
@@ -113,6 +115,10 @@ static int cb_file_init(struct flb_output_instance *ins,
113115
conf->delimiter = NULL;
114116
conf->label_delimiter = NULL;
115117
}
118+
else if (!strcasecmp(tmp, "template")) {
119+
conf->format = FLB_OUT_FILE_FMT_TEMPLATE;
120+
conf->template = "{time} {message}";
121+
}
116122
}
117123

118124
tmp = flb_output_get_property("delimiter", ins);
@@ -127,6 +133,11 @@ static int cb_file_init(struct flb_output_instance *ins,
127133
conf->label_delimiter = ret_str;
128134
}
129135

136+
tmp = flb_output_get_property("template", ins);
137+
if (tmp != NULL) {
138+
conf->template = tmp;
139+
}
140+
130141
/* Set the context */
131142
flb_output_set_context(ins, conf);
132143

@@ -186,6 +197,100 @@ static int ltsv_output(FILE *fp, struct flb_time *tm, msgpack_object *obj,
186197
return 0;
187198
}
188199

200+
static int template_output_write(FILE *fp, struct flb_time *tm, msgpack_object *obj,
201+
const char *key, int size)
202+
{
203+
int i;
204+
msgpack_object_kv *kv;
205+
206+
/*
207+
* Right now we treat "{time}" specially and fill the placeholder
208+
* with the metadata timestamp (formatted as float).
209+
*/
210+
if (!strncmp(key, "time", size)) {
211+
fprintf(fp, "%f", flb_time_to_double(tm));
212+
return 0;
213+
}
214+
215+
if (obj->type != MSGPACK_OBJECT_MAP) {
216+
flb_error("[out_file] invalid object type (type=%i)", obj->type);
217+
return -1;
218+
}
219+
220+
for (i = 0; i < obj->via.map.size; i++) {
221+
kv = obj->via.map.ptr + i;
222+
223+
if (size != kv->key.via.str.size) {
224+
continue;
225+
}
226+
227+
if (!memcmp(key, kv->key.via.str.ptr, size)) {
228+
if (kv->val.type == MSGPACK_OBJECT_STR) {
229+
fwrite(kv->val.via.str.ptr, 1, kv->val.via.str.size, fp);
230+
} else {
231+
msgpack_object_print(fp, kv->val);
232+
}
233+
return 0;
234+
}
235+
}
236+
return -1;
237+
}
238+
239+
/*
240+
* Python-like string templating for out_file.
241+
*
242+
* This accepts a format string like "my name is {name}" and fills
243+
* placeholders using corresponding values in a record.
244+
*
245+
* e.g. {"name":"Tom"} => "my name is Tom"
246+
*/
247+
static int template_output(FILE *fp, struct flb_time *tm, msgpack_object *obj,
248+
struct flb_file_conf *ctx)
249+
{
250+
int i;
251+
int len = strlen(ctx->template);
252+
int keysize;
253+
const char *key;
254+
const char *pos;
255+
const char *inbrace = NULL; /* points to the last open brace */
256+
257+
for (i = 0; i < len; i++) {
258+
pos = ctx->template + i;
259+
if (*pos == '{') {
260+
if (inbrace) {
261+
/*
262+
* This means that we find another open brace inside
263+
* braces (e.g. "{a{b}"). Ignore the previous one.
264+
*/
265+
fwrite(inbrace, 1, pos - inbrace, fp);
266+
}
267+
inbrace = pos;
268+
}
269+
else if (*pos == '}' && inbrace) {
270+
key = inbrace + 1;
271+
keysize = pos - inbrace - 1;
272+
273+
if (template_output_write(fp, tm, obj, key, keysize)) {
274+
fwrite(inbrace, 1, pos - inbrace + 1, fp);
275+
}
276+
inbrace = NULL;
277+
}
278+
else {
279+
if (!inbrace) {
280+
fputc(*pos, fp);
281+
}
282+
}
283+
}
284+
285+
/* Handle an unclosed brace like "{abc" */
286+
if (inbrace) {
287+
fputs(inbrace, fp);
288+
}
289+
fputs(NEWLINE, fp);
290+
return 0;
291+
}
292+
293+
189294
static int plain_output(FILE *fp, msgpack_object *obj, size_t alloc_size)
190295
{
191296
char *buf;
@@ -306,6 +411,9 @@ static void cb_file_flush(const void *data, size_t bytes,
306411
case FLB_OUT_FILE_FMT_PLAIN:
307412
plain_output(fp, obj, alloc_size);
308413
break;
414+
case FLB_OUT_FILE_FMT_TEMPLATE:
415+
template_output(fp, &tm, obj, ctx);
416+
break;
309417
}
310418
}
311419

plugins/out_file/file.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum {
2727
FLB_OUT_FILE_FMT_LTSV,
2828
FLB_OUT_FILE_FMT_PLAIN,
2929
FLB_OUT_FILE_FMT_MSGPACK,
30+
FLB_OUT_FILE_FMT_TEMPLATE,
3031
};
3132

3233
#endif

0 commit comments

Comments
 (0)